内核中用于数据接收的结构体struct msghdr以及iovec介绍
? ? ? 我們從一個(gè)實(shí)際的數(shù)據(jù)包發(fā)送的例子入手,來看看其發(fā)送的具體流程,以及過程中涉及到的相關(guān)數(shù)據(jù)結(jié)構(gòu)。在我們的虛擬機(jī)上發(fā)送icmp回顯請(qǐng)求包,ping另一臺(tái)主機(jī)172.16.48.1.我們使用系統(tǒng)調(diào)用sendto發(fā)送這個(gè)icmp包。
ssize_t sendto(int s, const void *buf, size_t len, int flags,const struct sockaddr *to, socklen_t tolen);? ? ?系統(tǒng)調(diào)用sendto最終調(diào)用內(nèi)核函數(shù)
asmlinkage long sys_sendto(int fd, void __user *buff, size_t len, unsigned flags, struct sockaddr __user *addr, int addr_len)? ? ?sys_sendto構(gòu)建一個(gè)結(jié)構(gòu)體struct msghdr,用于接收來自應(yīng)用層的數(shù)據(jù)包,下面是結(jié)構(gòu)體struct msghdr的定義:
struct msghdr {void *msg_name; //存數(shù)據(jù)包的目的地址,網(wǎng)絡(luò)包指向sockaddr_in//向內(nèi)核發(fā)數(shù)據(jù)時(shí),指向sockaddr_nlint msg_namelen; //地址長度struct iovec *msg_iov;__kernel_size_t msg_iovlen;void *msg_control;__kernel_size_t msg_controllen;unsigned msg_flags; };? ? ?這個(gè)結(jié)構(gòu)體的內(nèi)容可以分為四組:
? ? ?(1) 第一組是msg_name和msg_namelen,記錄這個(gè)消息的名字,其實(shí)就是數(shù)據(jù)包的目的地址。
? ? ?msg_name是指向一個(gè)結(jié)構(gòu)體struct sockaddr的指針。長度為16:
struct sockaddr {sa_family_t sa_family;char sa_addr[14]; }? ? ? ?所以,msg_namelen的長度為16.需要注意的是,結(jié)構(gòu)體struct sockaddr只在進(jìn)行參數(shù)傳遞時(shí)使用,無論是在用戶態(tài)還是在內(nèi)核態(tài),我們把其強(qiáng)制轉(zhuǎn)化為結(jié)構(gòu)體struct sockaddr_in:
struct sockaddr_in {sa_family_t sin_family;unsigned short int sin_port;struct in_addr sin_addr;unsigned char __pad[__SOCK_SIZE__ -sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)]; }; struct in_addr {__u32 s_addr; }? ? __SOCK_SIZE__的值為16,所以,struct sockaddr中真正有用的數(shù)據(jù)只有8bytes。在我們的ping例子中,傳入到內(nèi)核的msghdr結(jié)構(gòu)中:
msg.msg_name={sa_family_t=MY_AF_INET,sin_port=0,sin_addr.s_addr=172.16.48.1} msg.msg_namelen=16? 請(qǐng)求回顯icmp包沒有目的端地址的端口號(hào)。
? ? ? ?(2) 第二組是msg_iov和msg_iovlen,記錄這個(gè)消息的內(nèi)容。msg_iov是一個(gè)指向結(jié)構(gòu)體struct iovec的指針,實(shí)際上,確切地說,應(yīng)該是一個(gè)結(jié)構(gòu)體struct iovec的數(shù)組。下面是該結(jié)構(gòu)體的定義:
struct iovec {void __user *iov_base;__kernel_size_t iov_len; };? ?iov_base指向數(shù)據(jù)包緩沖區(qū),即參數(shù)buff,iov_len是buff的長度。msghdr中允許一次傳遞多個(gè)buff,以數(shù)組的形式組織在msg_iov中,msg_iovlen就記錄數(shù)組的長度(即有多少個(gè)buff).在我們的ping程序的示例中:
msg.msg_iov = { struct iovec={iov_base={icmp頭+填充字符'E'},iov_len = 40}} msg.msg_len = 1? ? ?(3) 第三組是msg_control和msg_controllen,它們可被用于發(fā)送任何的控制信息,在我們的例子中,沒有控制信息要送。暫時(shí)略過。
? ? ?(4) 第四組是msg_flags.其值即為傳入的參數(shù)flags。raw協(xié)議不支持MSG_OOB向標(biāo)志,即帶外數(shù)據(jù)。向內(nèi)核發(fā)阿松msg
時(shí)使用msghdr,netlink socket使用自己的消息頭nlmsghdr和自己的消息地址sockaddr_nl:
struct sockaddr_nl {sa_family_t nl_family;unsigned short nl_pad;__u32 nl_pid;__u32 nl_groups; }; struct nlmsghdr {__u32 nlmsg_len; /* Length of message */__u16 nlmsg_type; /* Message type */__u16 nlmsg_flags; /* Additional flags */__u32 nlmsg_seq; /* Sequence number */__u32 nlmsg_pid; /* Sending process PID */ };? ? ? 過程如下:
struct msghdr msg; memset(&msg, 0, sizeof(msg)); msg.msg_name = (void *)&(nladdr); msg.msg_namelen = sizeof(nladdr); {/*初始化一個(gè)strcut nlmsghdr結(jié)構(gòu)存,nlmsghdr為netlink socket自己的消息頭部,并使iov->iov_base指向在這個(gè)結(jié)構(gòu)*/char buffer[] = "An example message";struct nlmsghdr nlhdr;nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));strcpy(NLMSG_DATA (nlhdr),buffer);//將數(shù)據(jù)存放在消息頭指向的數(shù)據(jù)地址nlhdr->nlmsg_len = NLMSG_LENGTH(strlen(buffer));nlhdr->nlmsg_pid = getpid(); /* self pid */nlhdr->nlmsg_flags = 0;iov.iov_base = (void *)nlhdr;iov.iov_len = nlh->nlmsg_len; } msg.msg_iov = &iov; msg.msg_iovlen = 1; fd=socket(AF_NETLINK, SOCK_RAW, netlink_type); sendmsg(fd,&msg,0)I/O向量(struct iovec)
readv(2)與writev(2)函數(shù)都使用一個(gè)I/O向量的概念。這是由所包含的文件定義的:
#include?
sys/uio.h頭文件定義了struct iovc,其定義如下:
struct iovec定義了一個(gè)向量元素。通常,這個(gè)結(jié)構(gòu)用作一個(gè)多元素的數(shù)組。對(duì)于每一個(gè)傳輸?shù)脑?#xff0c;指針成員iov_base指向一個(gè)緩沖區(qū),這個(gè)緩沖區(qū)是存放
的是readv所接收的數(shù)據(jù)或是writev將要發(fā)送的數(shù)據(jù)。成員iov_len在各種情況下分別確定了接收的最大長度以及實(shí)際寫入的長度。
?
總結(jié)
以上是生活随笔為你收集整理的内核中用于数据接收的结构体struct msghdr以及iovec介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学习《apache源代码全景分析》之常用
- 下一篇: 漫画: 什么是外部排序?