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