套接字之sendmsg系统调用

sendmsg系统调用允许在用户空间构造消息头和控制信息,用此函数可以发送多个数据缓冲区的数据,并支持控制信息;当调用进入内核后,会将用户端的user_msghdr对应拷贝到内核的msghdr中,然后进行数据发送;

1 SYSCALL_DEFINE3(sendmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags)
2 {
3     /* 不支持64位采用32位兼容标记 */
4     if (flags & MSG_CMSG_COMPAT)
5         return -EINVAL;
6
7     /* 调用__sys_sendmsg */
8     return __sys_sendmsg(fd, msg, flags);
9 }
 1 /*
 2  *    BSD sendmsg interface
 3  */
 4
 5 long __sys_sendmsg(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_sendmsg(sock, msg, &msg_sys, flags, NULL, 0);
18
19     fput_light(sock->file, fput_needed);
20 out:
21     return err;
22 }
  1 static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg,
  2              struct msghdr *msg_sys, unsigned int flags,
  3              struct used_address *used_address,
  4              unsigned int allowed_msghdr_flags)
  5 {
  6     struct compat_msghdr __user *msg_compat =
  7         (struct compat_msghdr __user *)msg;
  8     struct sockaddr_storage address;
  9     struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
 10     unsigned char ctl[sizeof(struct cmsghdr) + 20]
 11                 __aligned(sizeof(__kernel_size_t));
 12     /* 20 is size of ipv6_pktinfo */
 13     unsigned char *ctl_buf = ctl;
 14     int ctl_len;
 15     ssize_t err;
 16
 17     msg_sys->msg_name = &address;
 18
 19     /* 需要做64位采用32位兼容 */
 20     if (MSG_CMSG_COMPAT & flags)
 21         /* 从64位消息头拷贝数据到32位消息头 */
 22         err = get_compat_msghdr(msg_sys, msg_compat, NULL, &iov);
 23     else
 24         /* 从用户空间拷贝消息数据到消息头 */
 25         err = copy_msghdr_from_user(msg_sys, msg, NULL, &iov);
 26     if (err < 0)
 27         return err;
 28
 29     err = -ENOBUFS;
 30
 31     /* 控制信息长度错误 */
 32     if (msg_sys->msg_controllen > INT_MAX)
 33         goto out_freeiov;
 34
 35     /* 拷贝控制信息 */
 36     flags |= (msg_sys->msg_flags & allowed_msghdr_flags);
 37     ctl_len = msg_sys->msg_controllen;
 38     if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
 39         err =
 40             cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl,
 41                              sizeof(ctl));
 42         if (err)
 43             goto out_freeiov;
 44         ctl_buf = msg_sys->msg_control;
 45         ctl_len = msg_sys->msg_controllen;
 46     } else if (ctl_len) {
 47         BUILD_BUG_ON(sizeof(struct cmsghdr) !=
 48                  CMSG_ALIGN(sizeof(struct cmsghdr)));
 49         if (ctl_len > sizeof(ctl)) {
 50             ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);
 51             if (ctl_buf == NULL)
 52                 goto out_freeiov;
 53         }
 54         err = -EFAULT;
 55         /*
 56          * Careful! Before this, msg_sys->msg_control contains a user pointer.
 57          * Afterwards, it will be a kernel pointer. Thus the compiler-assisted
 58          * checking falls down on this.
 59          */
 60         if (copy_from_user(ctl_buf,
 61                    (void __user __force *)msg_sys->msg_control,
 62                    ctl_len))
 63             goto out_freectl;
 64         msg_sys->msg_control = ctl_buf;
 65     }
 66
 67     /* 设置发送标记 */
 68     msg_sys->msg_flags = flags;
 69
 70     /* 设置非阻塞标记 */
 71     if (sock->file->f_flags & O_NONBLOCK)
 72         msg_sys->msg_flags |= MSG_DONTWAIT;
 73     /*
 74      * If this is sendmmsg() and current destination address is same as
 75      * previously succeeded address, omit asking LSM‘s decision.
 76      * used_address->name_len is initialized to UINT_MAX so that the first
 77      * destination address never matches.
 78      */
 79     /* 如果这次发送的地址跟上次成功发送的一致 */
 80     if (used_address && msg_sys->msg_name &&
 81         used_address->name_len == msg_sys->msg_namelen &&
 82         !memcmp(&used_address->name, msg_sys->msg_name,
 83             used_address->name_len)) {
 84         /* 无需进行检查,直接发送 */
 85         err = sock_sendmsg_nosec(sock, msg_sys);
 86         goto out_freectl;
 87     }
 88
 89     /* 进行安全模块检查后发送 */
 90     err = sock_sendmsg(sock, msg_sys);
 91     /*
 92      * If this is sendmmsg() and sending to current destination address was
 93      * successful, remember it.
 94      */
 95
 96     /* 发送成功需要更新成功地址记录 */
 97     if (used_address && err >= 0) {
 98         used_address->name_len = msg_sys->msg_namelen;
 99         if (msg_sys->msg_name)
100             memcpy(&used_address->name, msg_sys->msg_name,
101                    used_address->name_len);
102     }
103
104 out_freectl:
105     if (ctl_buf != ctl)
106         sock_kfree_s(sock->sk, ctl_buf, ctl_len);
107 out_freeiov:
108     kfree(iov);
109     return err;
110 }

TCP层的sendmsg实现为tcp_sendmsg,详情请移步<TCP层sendmsg系统调用的实现分析>;

原文地址:https://www.cnblogs.com/wanpengcoder/p/11749313.html

时间: 2024-10-30 00:42:17

套接字之sendmsg系统调用的相关文章

套接字之recv系统调用

recv系统调用对sys_recvfrom进行了简单的封装,只是其中不包含地址信息,其只需要从建立连接的另一端接收信息: 1 /* 2 * Receive a datagram from a socket. 3 */ 4 5 SYSCALL_DEFINE4(recv, int, fd, void __user *, ubuf, size_t, size, 6 unsigned int, flags) 7 { 8 return sys_recvfrom(fd, ubuf, size, flags,

套接字之send系统调用

send系统调用只是对sendto系统调用进行了封装,传递的参数不包含目的地址信息,数据会发送到已经建立连接的另一端的地址: 1 /* 2 * Send a datagram down a socket. 3 */ 4 5 SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len, 6 unsigned int, flags) 7 { 8 return sys_sendto(fd, buff, len, flags, NULL

套接字之close系统调用

close系统调用用于关闭文件描述符,其系统调用实现如下所示: 1 / 2 * Careful here! We test whether the file pointer is NULL before 3 * releasing the fd. This ensures that one clone task can't release 4 * an fd while another clone is opening it. 5 */ 6 SYSCALL_DEFINE1(close, unsi

套接字之sendto系统调用

sendto系统调用用于向指定的目的地址发送数据,其系统调用的流程比较容易理解,如下面所示,其主要完成 (1)将用户数据组织成msghdr,(2)而后调用socket操作的sendmsg:ipv4对应的sendmsg实现为inet_sendmsg,该函数进行端口自动绑定检查和绑定后,调用传输层的sendmsg函数发送数据,比如tcp会调用tcp_sendmsg函数,这里不详细介绍,读到tcp模块在进行补充: 1 /* 2 * Send a datagram to a given address.

套接字之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

套接字之recvfrom系统调用

recvfrom系统调用通过用户传入的接收空间构造msghdr,并且调用sock_recvmsg,该函数调用socket操作的recvmsg函数sock->ops->recvmsg,ipv4对应的是inet_recvmsg,该函数调用传输层的sk->sk_prot->recvmsg来接收数据,如tcp则调用tcp_recvmsg,接收完成之后记录地址结构长度信息: 1 /* 2 * Receive a frame from the socket and optionally rec

套接字之select系统调用

select是IO多路复用的一种方式,用来等待一个列表中的多个描述符的可读可写状态: 1 SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp, 2 fd_set __user *, exp, struct timeval __user *, tvp) 3 { 4 struct timespec64 end_time, *to = NULL; 5 struct timeval tv; 6 int r

套接字之msghdr结构

用户端在使用sendmsg/recvmsg发送或者接收数据时,会使用msghdr来构造消息,其对应的内核结构为user_msghdr:其中msg_iov向量指向了多个数据区,msg_iovlen标识了数据区个数:在通过系统调用进入内核后,该结构中的信息会拷贝给内核的msghdr结构: 1 /* 用户空间的消息头 */ 2 struct user_msghdr { 3 /* 指向地址结构 */ 4 void __user *msg_name; /* ptr to socket address st

sockets: 套接字选项相关的系统调用

########################################################### 套接字选项相关的系统调用: ########################################################### 将optval指向的单元中的值设置给optname选项: int setsockopt(int sockfd, int level, int optname, const void *optval,socklen_t optlen)