套接字之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 structure */
 5     /* 地址结构长度 */
 6     int        msg_namelen;        /* size of socket address structure */
 7     /* 数据 */
 8     struct iovec    __user *msg_iov;    /* scatter/gather array */
 9     /* 数据区个数 */
10     __kernel_size_t    msg_iovlen;        /* # elements in msg_iov */
11     /* 控制信息 */
12     void        __user *msg_control;    /* ancillary data */
13     /* 控制信息缓冲区长度 */
14     __kernel_size_t    msg_controllen;        /* ancillary data buffer length */
15 
16     /* 接收信息的标志 */
17     unsigned int    msg_flags;        /* flags on received message */
18 };

 

在套接字发送接收系统调用流程中,send/recv,sendto/recvfrom,sendmsg/recvmsg最终都会使用内核中的msghdr来组织数据,如下,其中msg_iter为指向数据区域的向量汇总信息,其中数据区指针可能包含一个或者多个数据区,对于send/sendto其只包含了一个数据区;

 1 /*
 2  *    As we do 4.4BSD message passing we use a 4.4BSD message passing
 3  *    system, not 4.3. Thus msg_accrights(len) are now missing. They
 4  *    belong in an obscure libc emulation or the bin.
 5  */
 6  
 7 struct msghdr {
 8     /* 指向socket地址结构 */
 9     void        *msg_name;    /* ptr to socket address structure */
10     /* 地址结构长度 */
11     int        msg_namelen;    /* size of socket address structure */
12     /* 数据 */
13     struct iov_iter    msg_iter;    /* data */
14     /* 控制信息 */
15     void        *msg_control;    /* ancillary data */
16     /* 控制信息缓冲区长度 */
17     __kernel_size_t    msg_controllen;    /* ancillary data buffer length */
18 
19     /* 接收信息的标志 */
20     unsigned int    msg_flags;    /* flags on received message */
21 
22     /* 异步请求控制块 */
23     struct kiocb    *msg_iocb;    /* ptr to iocb for async requests */
24 };

 

向量指向的数据通过iov_iter进行汇总信息和调整指向,其中iov为多个数据区的首地址,nr_segs为数据区个数;

 1 struct iov_iter {
 2     int type; /* 类型,读写方向,以及数据指针类型ITER_XXX */
 3     size_t iov_offset; /* 偏移 */
 4     size_t count; /* 数据总字节数 */
 5     union {
 6         /* 数据向量指针 */
 7         const struct iovec *iov;
 8         const struct kvec *kvec;
 9         const struct bio_vec *bvec;
10         struct pipe_inode_info *pipe;
11     };
12     union {
13         /* 向量中的数据块数量 */
14         unsigned long nr_segs;
15         struct {
16             int idx;
17             int start_idx;
18         };
19     };
20 };

 

对于每个数据区,iovec记录了数据区的首地址以及数据长度;

1 /* 一个数据区的信息 */
2 struct iovec
3 {
4     /* 数据区地址 */
5     void __user *iov_base;    /* BSD uses caddr_t (1003.1g requires void *) */
6     /* 数据区长度 */
7     __kernel_size_t iov_len; /* Must be size_t (1003.1g) */
8 };

 

总的数据组织结构如下:

 1 struct msghdr{
 2     iov_iter {
 3         type
 4         iov_offset
 5         count   | total_buff_len = buff0_len + buff1_len + buff2_len ?
 6         ---------
 7         iov_base|------>[buff0]
 8         iov_len | buff0_len
 9         ---------
10         iov_base|------>[buff1]
11         iov_len | buff1_len
12         ---------
13         iov_base|------>[buff2]
14         iov_len | buff2_len
15         ---------
16         nr_segs | iov_count = 3
17        
18     }
19  }

 

posted @ 2019-10-27 21:50  AlexAlex  阅读(5711)  评论(0编辑  收藏  举报