# 2019q1 Homework5 (daemon)
contributed by < `yiwei01` >
###### tags: `sysprog2019`
## 取得 kecho 程式碼編譯後測試:
```
$ git clone https://github.com/yiwei/kecho
$ cd kecho
$ make
$ sudo insmod fastecho.ko
$ telnet localhost 12345
```
## 閱讀程式碼
### fastecho_module.c :
```clike=
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/signal.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <net/sock.h>
#include "fastecho.h"
#define DEFAULT_PORT 12345
#define DEFAULT_BACKLOG 128
MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("National Cheng Kung University, Taiwan");
MODULE_DESCRIPTION("Fast echo server in kernel");
MODULE_VERSION("0.1");
static ushort port = DEFAULT_PORT;
static ushort backlog = DEFAULT_BACKLOG;
module_param(port, ushort, S_IRUGO);
module_param(backlog, ushort, S_IRUGO);
struct echo_server_param param;
struct socket *listen_sock;
struct task_struct *echo_server;
static int open_listen(struct socket **);
static void close_listen(struct socket *);
static int fastecho_init_module(void)
{
int error = open_listen(&listen_sock);
if (error < 0) {
printk(KERN_ERR MODULE_NAME ": listen socket open error\n");
return error;
}
param.listen_sock = listen_sock;
echo_server = kthread_run(echo_server_daemon, ¶m, MODULE_NAME);
if (IS_ERR(echo_server)) {
printk(KERN_ERR MODULE_NAME ": cannot start server daemon\n");
close_listen(listen_sock);
}
return 0;
}
static void fastecho_cleanup_module(void)
{
send_sig(SIGTERM, echo_server, 1);
kthread_stop(echo_server);
close_listen(listen_sock);
}
static int open_listen(struct socket **result)
{
struct socket *sock;
struct sockaddr_in addr;
int error;
int opt = 1;
/* using IPv4, TCP/IP */
error = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
if (error < 0) {
printk(KERN_ERR MODULE_NAME ": socket create error = %d\n", error);
return error;
}
printk(MODULE_NAME ": socket create ok....\n");
/* set tcp_nodelay */
error = kernel_setsockopt(sock, SOL_TCP, TCP_NODELAY, (char *) &opt,
sizeof(opt));
if (error < 0) {
printk(KERN_ERR MODULE_NAME
": setsockopt tcp_nodelay setting error = %d\n",
error);
sock_release(sock);
return error;
}
printk(MODULE_NAME ": setsockopt ok....\n");
/* set sockaddr_in */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(DEFAULT_PORT);
/* bind */
error = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
if (error < 0) {
printk(KERN_ERR MODULE_NAME ": socket bind error = %d\n", error);
sock_release(sock);
return error;
}
printk(MODULE_NAME ": socket bind ok....\n");
/* listen */
error = kernel_listen(sock, DEFAULT_BACKLOG);
if (error < 0) {
printk(KERN_ERR MODULE_NAME ": socket listen error = %d\n", error);
sock_release(sock);
return error;
}
printk(MODULE_NAME ": socket listen ok....\n");
*result = sock;
return 0;
}
static void close_listen(struct socket *sock)
{
kernel_sock_shutdown(sock, SHUT_RDWR);
sock_release(sock);
}
module_init(fastecho_init_module);
module_exit(fastecho_cleanup_module);
```
在 `module_init(fastecho_init_module);` 後呼叫 `open_listen` 建立一個 socket,並在第 16 行可看到 `DEFAULT_PORT 12345` (因此 `$ telnet localhost 12345` 便可執行 fastecho 這支程式)。接著展開 [kthread_run](https://www.fsl.cs.sunysb.edu/kernel-api/re67.html) 這個 macro ,執行 `echo_server_daemon` 這個 function。另外附上 `kthread_run` 在 Linux 5.1 版的 [source code](https://elixir.bootlin.com/linux/latest/source/include/linux/kthread.h#L44) :
```clike=
#define kthread_run(threadfn, data, namefmt, ...) \
({ \
struct task_struct *__k \
= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
if (!IS_ERR(__k)) \
wake_up_process(__k); \
__k; \
})
```
其實就是 `kthread_create` 成功時直接 `wake_up_process`。再來可以看到 **kthread_run(echo_server_daemon, ¶m, MODULE_NAME);** 中的 `echo_server_daemon` 便定義在 echo_server.c 裡。
### echo_server.c
```clike=
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/sched/signal.h>
#include <linux/tcp.h>
#include "fastecho.h"
#define BUF_SIZE 4096
static int get_request(struct socket *sock, unsigned char *buf, size_t size)
{
struct msghdr msg;
struct kvec vec;
int length;
/* kvec setting */
vec.iov_len = size;
vec.iov_base = buf;
/* msghdr setting */
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
printk(MODULE_NAME ": start get response\n");
/* get msg */
length = kernel_recvmsg(sock, &msg, &vec, size, size, msg.msg_flags);
printk(MODULE_NAME ": get request = %s\n", buf);
return length;
}
static int send_request(struct socket *sock, unsigned char *buf, size_t size)
{
int length;
struct kvec vec;
struct msghdr msg;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
vec.iov_base = buf;
vec.iov_len = strlen(buf);
printk(MODULE_NAME ": start send request.\n");
length = kernel_sendmsg(sock, &msg, &vec, 1, strlen(buf) - 1);
printk(MODULE_NAME ": send request = %s\n", buf);
return length;
}
static int echo_server_worker(void *arg)
{
struct socket *sock;
unsigned char *buf;
int res;
sock = (struct socket *) arg;
allow_signal(SIGKILL);
allow_signal(SIGTERM);
buf = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!buf) {
printk(KERN_ERR MODULE_NAME ": kmalloc error....\n");
return -1;
}
while (!kthread_should_stop()) {
res = get_request(sock, buf, BUF_SIZE - 1);
if (res <= 0) {
if (res) {
printk(KERN_ERR MODULE_NAME ": get request error = %d\n", res);
}
break;
}
res = send_request(sock, buf, strlen(buf));
if (res < 0) {
printk(KERN_ERR MODULE_NAME ": send request error = %d\n", res);
break;
}
}
res = get_request(sock, buf, BUF_SIZE - 1);
res = send_request(sock, buf, strlen(buf));
kernel_sock_shutdown(sock, SHUT_RDWR);
sock_release(sock);
kfree(buf);
return 0;
}
int echo_server_daemon(void *arg)
{
struct echo_server_param *param = arg;
struct socket *sock;
struct task_struct *thread;
int error;
allow_signal(SIGKILL);
allow_signal(SIGTERM);
while (!kthread_should_stop()) {
/* using blocking I/O */
error = kernel_accept(param->listen_sock, &sock, 0);
if (error < 0) {
if (signal_pending(current))
break;
printk(KERN_ERR MODULE_NAME ": socket accept error = %d\n", error);
continue;
}
/* start server worker */
thread = kthread_run(echo_server_worker, sock, MODULE_NAME);
if (IS_ERR(thread)) {
printk(KERN_ERR MODULE_NAME ": create worker thread error = %d\n",
error);
continue;
}
}
return 0;
}
```
- [kthread_should_stop](https://www.fsl.cs.sunysb.edu/kernel-api/re68.html) : 當 kthread 接收到 [kthread_stop](https://www.fsl.cs.sunysb.edu/kernel-api/re71.html) 時,`kthread_should_stop` 會 return true,否則 return false
- [signal_pending(current)](http://welkinchen.pixnet.net/blog/post/49649996--signal_pending%28current%29-%E7%94%A8%E6%B3%95) : 目前只知道當此 function 的 return value 不為 0 的時候表示有訊號產生,這邊先猜測 `if (signal_pending(current)` 這個檢查是為了處理在 `kernel_accept` 時接收到 signal 時,要 break 出 while loop,待驗證。
- `echo_server_worker` : 這個 function 主要藉由呼叫 `get_request` 和 `send_request` 來負責訊息在 socket 的傳送。
- `get_request` :
1. 先了解 `msghdr` 這個 structure : 在 [Linux 5.1](https://elixir.bootlin.com/linux/latest/source/include/linux/socket.h#L48) 定義如下 :
```clike=
struct msghdr {
void *msg_name; /* ptr to socket address structure */
int msg_namelen; /* size of socket address structure */
struct iov_iter msg_iter; /* data */
void *msg_control; /* ancillary data */
__kernel_size_t msg_controllen; /* ancillary data buffer length */
unsigned int msg_flags; /* flags on received message */
struct kiocb *msg_iocb; /* ptr to iocb for async requests */
};
```
2. 再來看到 `kvec` 這個 structure : 在 [Linux 5.1](https://elixir.bootlin.com/linux/v5.1.1/source/include/linux/uio.h#L20) 定義如下 :
```clike=
struct kvec {
void *iov_base; /* and that should *never* hold a userland pointer */
size_t iov_len;
};
```
3. 最後看到 `kernel_recvmsg` 在 [Linux 5.1](https://elixir.bootlin.com/linux/latest/source/net/socket.c#L908) 定義如下 :
```clike=
int kernel_recvmsg(struct socket *sock, struct msghdr *msg,
struct kvec *vec, size_t num, size_t size, int flags)
{
mm_segment_t oldfs = get_fs();
int result;
iov_iter_kvec(&msg->msg_iter, READ, vec, num, size);
set_fs(KERNEL_DS);
result = sock_recvmsg(sock, msg, flags);
set_fs(oldfs);
return result;
}
```
先透過 `iov_iter_kvec` 將 msg->msg_iter 做設定
```clike=
void iov_iter_kvec(struct iov_iter *i, unsigned int direction,
const struct kvec *kvec, unsigned long nr_segs,
size_t count)
{
WARN_ON(direction & ~(READ | WRITE));
i->type = ITER_KVEC | (direction & (READ | WRITE));
i->kvec = kvec;
i->nr_segs = nr_segs;
i->iov_offset = 0;
i->count = count;
}
```
最後再透過 `sock_recvmsg` 來取得資料
- `send_request` : 與 `get_request` 相似。
## kecho 缺失
執行未修改過的 kecho,會發現字串會有亂碼,因此第一直覺是字串沒有 null-charater 或是未初始化的問題。
```
yiwei@yiwei-VirtualBox:~/tmp/kecho$ telnet localhost 12345
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
banana
banana
��������������������������������������������������������������������������������������������������������������������������l�l�l������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
```
**dmesg** 下亦顯示亂碼
```
[ 3091.132582] fastecho: start get response
[ 3096.924543] fastecho: get request = banana
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xffl\xe2l\xffl\xe2l\xffl\xe2l\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf6\xf6\xf6\xff\xf6\xf6\xf6\xff\xf6\xf6\xf6\xff\xf6\xf6\xf6\xff\xf6\xf6\xf6\xff\xf6\xf6\xf6\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7\xff\xf7\xf7\xf7
[ 3096.924553] fastecho: start send request.
```
## 修改過程
1. 在 `echo_server_worker` 第 18 行加上 ` memset(buf, '\0', BUF_SIZE);` ,故在每次 `get_request` 之前都會將 buf 初始化為 null-character。
2. 在 `send_request` 時 ,將第 18 行的
`length = kernel_sendmsg(sock, &msg, &vec, 1, strlen(buf) - 1); `
改成
`length = kernel_sendmsg(sock, &msg, &vec, 1, strlen(buf));`
用意是將 null-character 一併傳送。
### 修改後的 `echo_server_worker`
```clike=
static int echo_server_worker(void *arg)
{
struct socket *sock;
unsigned char *buf;
int res;
sock = (struct socket *) arg;
allow_signal(SIGKILL);
allow_signal(SIGTERM);
buf = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!buf) {
printk(KERN_ERR MODULE_NAME ": kmalloc error....\n");
return -1;
}
while (!kthread_should_stop()) {
memset(buf, '\0', BUF_SIZE); // initialize buf with null-character
res = get_request(sock, buf, BUF_SIZE - 1);
if (res <= 0) {
if (res) {
printk(KERN_ERR MODULE_NAME ": get request error = %d\n", res);
}
break;
}
res = send_request(sock, buf, strlen(buf));
if (res < 0) {
printk(KERN_ERR MODULE_NAME ": send request error = %d\n", res);
break;
}
}
res = get_request(sock, buf, BUF_SIZE - 1);
res = send_request(sock, buf, strlen(buf));
kernel_sock_shutdown(sock, SHUT_RDWR);
sock_release(sock);
kfree(buf);
return 0;
}
```
### 修改後的 `send_request`
```clike=
static int send_request(struct socket *sock, unsigned char *buf, size_t size)
{
int length;
struct kvec vec;
struct msghdr msg;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
vec.iov_base = buf;
vec.iov_len = strlen(buf);
printk(MODULE_NAME ": start send request.\n");
length = kernel_sendmsg(sock, &msg, &vec, 1,
strlen(buf)); // send with null-character
printk(MODULE_NAME ": send request = %s\n", buf);
return length;
}
```
上述的修改能使得訊息正常傳送,但透過 dmesg 能觀察到, 每次在 telnet 按下 `ctrl + ]` 後,總是會多出 2 次 get 和 1 次 send。
```
[ 1483.493954] fastecho: start get response
[ 1484.710412] fastecho: get request = asdwed
[ 1484.710413] fastecho: start send request.
[ 1484.710431] fastecho: send request = asdwed
[ 1484.710433] fastecho: start get response
[ 1487.606301] fastecho: get request =
[ 1487.606302] fastecho: start get response
[ 1487.606305] fastecho: get request =
[ 1487.606306] fastecho: start send request.
[ 1487.606309] fastecho: send request =
```
修正 :
將 `echo_server_worker` 第 35、36 行的 `get_request` 以及 `send_request` 註解掉,如此一來當按下 `ctrl + ]` 後,便只後留下一次 `get_request`。
### 修改後的 `echo_server_worker`
```clike=
static int echo_server_worker(void *arg)
{
struct socket *sock;
unsigned char *buf;
int res;
sock = (struct socket *) arg;
allow_signal(SIGKILL);
allow_signal(SIGTERM);
buf = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!buf) {
printk(KERN_ERR MODULE_NAME ": kmalloc error....\n");
return -1;
}
while (!kthread_should_stop()) {
memset(buf, '\0', BUF_SIZE); // initialize buf with null-character
res = get_request(sock, buf, BUF_SIZE - 1);
if (res <= 0) {
if (res) {
printk(KERN_ERR MODULE_NAME ": get request error = %d\n", res);
}
break;
}
res = send_request(sock, buf, strlen(buf));
if (res < 0) {
printk(KERN_ERR MODULE_NAME ": send request error = %d\n", res);
break;
}
}
/* remove unnecessary statement */
// res = get_request(sock, buf, BUF_SIZE - 1);
// res = send_request(sock, buf, strlen(buf));
kernel_sock_shutdown(sock, SHUT_RDWR);
sock_release(sock);
kfree(buf);
return 0;
}
```
```
[ 1808.119523] fastecho: start get response
[ 1811.627405] fastecho: get request = sklc
[ 1811.627407] fastecho: start send request.
[ 1811.627424] fastecho: send request = sklc
[ 1811.627426] fastecho: start get response
[ 1814.378337] fastecho: get request =
```
而最後剩下的這個 `[ 1814.378337] fastecho: get request = ` ,是 user 按下 `ctrl + ]` 後,因為沒有送出 message , 所以在離開 `get_request` function 前印出 buf 的內容便是空的 (全是 null-character) 。