内核中用于数据接收的结构体struct msghdr
我們從一個(gè)實(shí)際的數(shù)據(jù)包發(fā)送的例子入手,來(lái)看看其發(fā)送的具體流程,以及過(guò)程中涉及到的相關(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);
??? s就是socket的fd。buf是待發(fā)送的icmp數(shù)據(jù)包,len是buf的長(zhǎng)度。flags可以是下列標(biāo)志位的位或:
??? MSG_DONTROUTE
??? 在發(fā)送分組時(shí),不使用網(wǎng)關(guān),只有直接連接在本網(wǎng)絡(luò)中的主機(jī)才能接收到數(shù)據(jù)。這個(gè)標(biāo)志通常僅用于診斷和路由程序。可路由的協(xié)議族才能使用這個(gè)標(biāo)志;packet sockets不可以使用。
??? MSG_DONTWAIT
??? 使用非阻塞操作。
??? MSG_NOSIGNAL
??? 當(dāng)流式套接字的另一端中斷連接時(shí)不發(fā)送SIGPIPE信號(hào),但仍然返回EPIPE錯(cuò)誤。
??? MSG_CONFIRM (僅用于Linux 2.3以上版本)
??? 通知鏈路層發(fā)生了轉(zhuǎn)發(fā)過(guò)程:得到了另一端的成功應(yīng)答。如果鏈路層沒(méi)有收到通知,它將按照常規(guī)探測(cè)網(wǎng)絡(luò)上的相鄰主機(jī)(比如通過(guò)免費(fèi)arp)。只能用于 SOCK_DGRAM和SOCK_RAW類型的套接字,且僅對(duì)IPv4和IPv6有效。
??? 除此之外,還有MSG_MORE,MSG_OOB,MSG_EOR。
??? to是這個(gè)數(shù)據(jù)包發(fā)送的目地端地址,tolen是to的長(zhǎng)度。
??? 系統(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,用于接收來(lái)自應(yīng)用層的數(shù)據(jù)包,下面是結(jié)構(gòu)體struct msghdr的定義:
??????? struct msghdr {
??????????? void??????????? *msg_name;
??????????? int???????????? 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)容可以分為四組。
??? 第一組是msg_name和msg_namelen,記錄這個(gè)消息的名字,其實(shí)就是數(shù)據(jù)包的目的地址。msg_name是指向一個(gè)結(jié)構(gòu)體struct sockaddr的指針。長(zhǎng)度為16:
??????? struct sockaddr{
??????????? sa_family_t sa_family;
??????????? char??????? sa_addr[14];
??????? }
所以,msg_namelen的長(zhǎng)度為16。需要注意的是,結(jié)構(gòu)體struct sockaddr只在進(jìn)行參數(shù)傳遞時(shí)使用,無(wú)論是在用戶態(tài)還是在內(nèi)核態(tài),我們都把其強(qiáng)制轉(zhuǎn)化為結(jié)構(gòu)體struct sockaddr_in:
??????? strcut 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包沒(méi)有目的端地址的端口號(hào)。
??? 第二組是msg_iov和msg_iovlen,記錄這個(gè)消息的內(nèi)容。msg_iov是一個(gè)指向結(jié)構(gòu)體struct iovec的指針,實(shí)際上,確切地說(shuō),應(yīng)該是一個(gè)結(jié)構(gòu)體strcut 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的長(zhǎng)度。msghdr中允許一次傳遞多個(gè)buff,以數(shù)組的形式組織在 msg_iov中,msg_iovlen就記錄數(shù)組的長(zhǎng)度(即有多少個(gè)buff)。在我們的ping程序的實(shí)例中:
??? msg.msg_iov = { struct iovec = { iov_base = { icmp頭+填充字符'E' }, iov_len = 40 } }
??? msg.msg_len = 1
??? 第三組是msg_control和msg_controllen,它們可被用于發(fā)送任何的控制信息,在我們的例子中,沒(méi)有控制信息要發(fā)送。暫時(shí)略過(guò)。
??? 第四組是msg_flags。其值即為傳入的參數(shù)flags。raw協(xié)議不支持MSG_OOB標(biāo)志,即帶外數(shù)據(jù)。
總結(jié)
以上是生活随笔為你收集整理的内核中用于数据接收的结构体struct msghdr的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 互联网日报 | 理想汽车交付量突破300
- 下一篇: 互联网日报 | 3月12日 星期五 |