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系统调用的实现分析>;