用戶端在使用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 }
