用户端在使用 sendmsg/recvmsg 发送或者接收数据时, 会使用 msghdr 来构造消息, 其对应的内核结构为 user_msghdr; 其中 msg_iov 向量指向了多个数据区, msg_iovlen 标识了数据区个数; 在通过系统调用进入内核后, 该结构中的信息会拷贝给内核的 msghdr 结构;
- /* 用户空间的消息头 */
- struct user_msghdr {
- /* 指向地址结构 */
- void __user *msg_name; /* ptr to socket address structure */
- /* 地址结构长度 */
- int msg_namelen; /* size of socket address structure */
- /* 数据 */
- struct iovec __user *msg_iov; /* scatter/gather array */
- /* 数据区个数 */
- __kernel_size_t msg_iovlen; /* # elements in msg_iov */
- /* 控制信息 */
- void __user *msg_control; /* ancillary data */
- /* 控制信息缓冲区长度 */
- __kernel_size_t msg_controllen; /* ancillary data buffer length */
- /* 接收信息的标志 */
- unsigned int msg_flags; /* flags on received message */
- };
在套接字发送接收系统调用流程中, send/recv,sendto/recvfrom,sendmsg/recvmsg 最终都会使用内核中的 msghdr 来组织数据, 如下, 其中 msg_iter 为指向数据区域的向量汇总信息, 其中数据区指针可能包含一个或者多个数据区, 对于 send/sendto 其只包含了一个数据区;
- /*
- * As we do 4.4BSD message passing we use a 4.4BSD message passing
- * system, not 4.3. Thus msg_accrights(len) are now missing. They
- * belong in an obscure libc emulation or the bin.
- */
- struct msghdr {
- /* 指向 socket 地址结构 */
- 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 */
- };
向量指向的数据通过 iov_iter 进行汇总信息和调整指向, 其中 iov 为多个数据区的首地址, nr_segs 为数据区个数;
- struct iov_iter {
- int type; /* 类型, 读写方向, 以及数据指针类型 ITER_XXX */
- size_t iov_offset; /* 偏移 */
- size_t count; /* 数据总字节数 */
- union {
- /* 数据向量指针 */
- const struct iovec *iov;
- const struct kvec *kvec;
- const struct bio_vec *bvec;
- struct pipe_inode_info *pipe;
- };
- union {
- /* 向量中的数据块数量 */
- unsigned long nr_segs;
- struct {
- int idx;
- int start_idx;
- };
- };
- };
对于每个数据区, iovec 记录了数据区的首地址以及数据长度;
- /* 一个数据区的信息 */
- struct iovec
- {
- /* 数据区地址 */
- void __user *iov_base; /* BSD uses caddr_t (1003.1g requires void *) */
- /* 数据区长度 */
- __kernel_size_t iov_len; /* Must be size_t (1003.1g) */
- };
总的数据组织结构如下:
- struct msghdr{
- iov_iter {
- type
- iov_offset
- count | total_buff_len = buff0_len + buff1_len + buff2_len ?
- ---------
- iov_base|------>[buff0]
- iov_len | buff0_len
- ---------
- iov_base|------>[buff1]
- iov_len | buff1_len
- ---------
- iov_base|------>[buff2]
- iov_len | buff2_len
- ---------
- nr_segs | iov_count = 3
- }
- }
来源: http://www.bubuko.com/infodetail-3260664.html