套接字之recvmsg系统调用

recvmsg系统调用允许用户指定msghdr结构来接收数据,可以将数据接收到多个缓冲区中,并且可以接收控制信息;接收信息过程与其他接收系统调用核心一致,都是调用传输层的接收函数进行数据接收;

 1 SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg,
 2         unsigned int, flags)
 3 {
 4     /* 不支持64位采用32位兼容标记 */
 5     if (flags & MSG_CMSG_COMPAT)
 6         return -EINVAL;
 7 
 8     /* 接收消息 */
 9     return __sys_recvmsg(fd, msg, flags);
10 }

 

 1 /*
 2  *    BSD recvmsg interface
 3  */
 4 
 5 long __sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned flags)
 6 {
 7     int fput_needed, err;
 8     struct msghdr msg_sys;
 9     struct socket *sock;
10 
11     /* 获取socket */
12     sock = sockfd_lookup_light(fd, &err, &fput_needed);
13     if (!sock)
14         goto out;
15 
16     /* 接收信息 */
17     err = ___sys_recvmsg(sock, msg, &msg_sys, flags, 0);
18 
19     fput_light(sock->file, fput_needed);
20 out:
21     return err;
22 }

 

 1 static int ___sys_recvmsg(struct socket *sock, struct user_msghdr __user *msg,
 2              struct msghdr *msg_sys, unsigned int flags, int nosec)
 3 {
 4     struct compat_msghdr __user *msg_compat =
 5         (struct compat_msghdr __user *)msg;
 6     struct iovec iovstack[UIO_FASTIOV];
 7     struct iovec *iov = iovstack;
 8     unsigned long cmsg_ptr;
 9     int len;
10     ssize_t err;
11 
12     /* kernel mode address */
13     struct sockaddr_storage addr;
14 
15     /* user mode address pointers */
16     struct sockaddr __user *uaddr;
17     int __user *uaddr_len = COMPAT_NAMELEN(msg);
18 
19     msg_sys->msg_name = &addr;
20     
21     /* 需要做64位采用32位兼容 */
22     if (MSG_CMSG_COMPAT & flags)
23         /* 从64位消息头拷贝到32位消息头 */
24         err = get_compat_msghdr(msg_sys, msg_compat, &uaddr, &iov);
25     else
26         /* 拷贝消息头 */
27         err = copy_msghdr_from_user(msg_sys, msg, &uaddr, &iov);
28     if (err < 0)
29         return err;
30 
31     /* 控制信息 */
32     cmsg_ptr = (unsigned long)msg_sys->msg_control;
33     msg_sys->msg_flags = flags & (MSG_CMSG_CLOEXEC|MSG_CMSG_COMPAT);
34 
35     /* We assume all kernel code knows the size of sockaddr_storage */
36     msg_sys->msg_namelen = 0;
37 
38     /* 设置非阻塞标记 */
39     if (sock->file->f_flags & O_NONBLOCK)
40         flags |= MSG_DONTWAIT;
41 
42     /* 调用读取函数 */
43     err = (nosec ? sock_recvmsg_nosec : sock_recvmsg)(sock, msg_sys, flags);
44     if (err < 0)
45         goto out_freeiov;
46     len = err;
47 
48     /* 拷贝地址到用户空间 */
49     if (uaddr != NULL) {
50         err = move_addr_to_user(&addr,
51                     msg_sys->msg_namelen, uaddr,
52                     uaddr_len);
53         if (err < 0)
54             goto out_freeiov;
55     }
56 
57     /* 拷贝标志到用户空间 */
58     err = __put_user((msg_sys->msg_flags & ~MSG_CMSG_COMPAT),
59              COMPAT_FLAGS(msg));
60     if (err)
61         goto out_freeiov;
62 
63     /* 拷贝控制信息长度到用户空间 */
64     if (MSG_CMSG_COMPAT & flags)
65         err = __put_user((unsigned long)msg_sys->msg_control - cmsg_ptr,
66                  &msg_compat->msg_controllen);
67     else
68         err = __put_user((unsigned long)msg_sys->msg_control - cmsg_ptr,
69                  &msg->msg_controllen);
70     if (err)
71         goto out_freeiov;
72     err = len;
73 
74 out_freeiov:
75     kfree(iov);
76     return err;
77 }

 

TCP层的recvmsg系统调用的实现函数为tcp_recvmsg,具体分析请移步<TCP层recvmsg系统调用的实现分析>;

上一篇:关于ios 推送功能的终极解决


下一篇:opencv图像的载入与保存