socket和send兩個系統調用爲例,協議棧是如何工作

首先以socket和send兩個系統調用爲例,來回顧一下協議棧是如何工作的,在這過程中可以找到如何在協議棧中增加對UDP協議的支持。socket系統調用的原型是
       int socket(int domain, int type, int protocol);
domain是協議域,對於ipv4協議來說,其值是PF_INET(ipv4因特網協議),對於我們自己實現的ipv4協議模塊,我們爲其新增MY_PF_INET。所有的協議域在include/linux/socket.h被定義,如下:
#define AF_UNSPEC 0
#define AF_UNIX  1  // Unix域的socket
#define AF_LOCAL 1  // AF_UNIX的POSIX命名
#define AF_INET  2  // 因特網IP協議
#define AF_AX25  3  // Amateur Radio AX.25
#define AF_IPX  4  // Novell IPX
#define AF_APPLETALK 5 // AppleTalk DDP
#define AF_NETROM 6   // Amateur Radio NET/ROM
#define AF_BRIDGE 7   // Multiprotocol bridge
#define AF_ATMPVC 8   // ATM PVCs
#define AF_X25  9   // Reserved for X.25 project
#define AF_INET6 10  // IP version 6
#define AF_ROSE  11  // Amateur Radio X.25 PLP
#define AF_DECnet 12  // Reserved for DECnet project
#define AF_NETBEUI 13 // Reserved for 802.2LLC project
#define AF_SECURITY 14 // Security callback pseudo AF
#define AF_KEY  15  // PF_KEY key management API
#define AF_NETLINK 16
#define AF_ROUTE AF_NETLINK // Alias to emulate 4.4BSD
#define AF_PACKET 17     // Packet family
#define AF_ASH  18     // Ash
#define AF_ECONET 19     // Acorn Econet
#define AF_ATMSVC 20     // ATM SVCs
#define AF_SNA  22     // Linux SNA Project (nutters!)
#define AF_IRDA  23     // IRDA sockets
#define AF_PPPOX 24     // PPPoX sockets
#define AF_WANPIPE 25    // Wanpipe API Sockets
#define AF_LLC  26      // Linux LLC
#define AF_BLUETOOTH 31   // Bluetooth sockets
#define AF_MAX  32     // For now..
       可以看到,當前,內核最多支持31個協議域(0爲未指定,32爲MAX)。而當前的定義中還有27,28,30爲空,所以我們定義了MY_PF_INET爲28。
       在內核中,結構體struct net_proto_family用於表示一個協議域,而全局數組變量static struct net_proto_family *net_families[NPROTO]是一個有32項的數組,用於保存當前內核中所有已註冊的協議域,函數sock_register用於把一個協議域註冊到內核中,即把一個協議域跟net_families數組
中的某一項相關聯。struct net_proto_family的完整定義如下:
struct net_proto_family {
 int  family;
 int  (*create)(struct socket *sock, int protocol);
 short  authentication;
 short  encryption;
 short  encrypt_net;
 struct module *owner;
};
       其中,family爲域編號,對於我們的模塊即爲MY_PF_INET。通過sock_register函數,使net_families[MY_PF_INET]指向需要註冊的域。create是該域的socket的創建函數,我們的MY_PF_INET域定義如下:
static struct net_proto_family myinet_family_ops = {
    .family = MY_PF_INET,
    .create = myinet_create,
    .owner  = THIS_MODULE,
};
       現在回到socket系統調用上來,內核實現socket系統調用的函數是sys_socket。該函數通過調用sock_create進行創建,sock_create調用__sock_create。__sock_create要創建一個struct socket,這是一個普通BSD socket的結構體,其定義如下:
struct socket {
 socket_state  state;
 unsigned long  flags;
 struct proto_ops *ops;
 struct fasync_struct *fasync_list;
 struct file  *file;
 struct sock  *sk;
 wait_queue_head_t wait;
 short   type;
};
       __sock_create創建的時候,爲其type賦上socket系統調用的第二個參數type,最後通過調用net_families[family]->create(sock, protocol)完成socket的創建。對於MY_PF_INET域來說,該create函數即myinet_create。MY_PF_INET域支持的網絡層協議是IP協議,在該協議上支持的套接字接口有流套接字(SOCK_STREAM),數據報套接字(SOCK_DGRAM)和原始套接字(SOCK_RAW)。在IP協議上註冊一個套接字接口,也即創建一個套接字,需要知道該類型的套接字必需的一些相關信息。結構體struct inet_protosw就是用於在IP協議上註冊套接字接口,其完整定義如下:
struct inet_protosw {
 struct list_head list;
 unsigned short  type;    //套接字類型,即socket系統調用的第二個參數。
 int   protocol;       //第4層(傳輸層)協議號
 struct proto  *prot;    //第4層協議的操作函數集
 struct proto_ops *ops;   //該類型的套接字的操作函數集
 int capability;
 char no_check;
 unsigned char  flags;
};
       myinet_create函數註冊套接字的過程本質上就是爲指定套接字類型和第4層協議號的一個socket找到對應的操作函數集,使這個socket隨後能真正被操作。全局數組inetsw_array包含了系統當前支持的所有在IP協議上能夠註冊的套接字接口,在系統初始化的時候,這些結構體以type作爲依據,被組織到
static struct list_head inetsw[SOCK_MAX]中。當在inetsw數組中找到對應的socket類型和第4層協議號後,令struct socket->ops的值爲struct inet_protosw->ops,即爲該類型的套接字指定操作函數集。而struct socket->sk是網絡層的套接字接口,其成員sk_prot的值爲struct inet_protosw->prot,即爲該類型的第4層協議指定操作函數集。套接字的創建工作大致如此。
      接下來,再來看send系統調用,它的原型如下:
      ssize_t send(int s, const void *buf, size_t len, int flags);
      s是文件描述符,在內核中跟一個struct socket結構體建立一一對應的映射關係。buf和len分別爲待發送數據的內容和長度,flag是一些標誌位。內核實現該系統調用的函數是sys_send。sys_send直接調用sys_sendto,把sys_sendto的最後兩個參數addr和addr_len置空。sys_sendto根據文件描述符s找到對應的struct socket,然後建立一個結構體struct msghdr msg用於發送數據內容,該結構體的定義如下:
struct msghdr {
 void * msg_name;      /* Socket 的名字 */
 int  msg_namelen;     /* 名字的長度  */
 struct iovec * msg_iov;  /* 數據塊   */
 __kernel_size_t msg_iovlen; /* 數據塊的數量  */
 void  * msg_control;    /* Per protocol magic (eg BSD file descriptor passing) */
 __kernel_size_t msg_controllen; /* Length of cmsg list */
 unsigned msg_flags;
};
        然後,sys_sendto調用sock_sendmsg發送數據,sock_sendmsg調用__sock_sendmsg,__sock_sendmsg調用struct socket->ops->sendmsg,即調用特定套接字類型的操作函數集中的sendmsg成員函數。比如,SOCK_RAW類型的套接字的sendmsg成員函數的實現如下(實際上SOCK_DGRAM類型的套接字的sendmsg成員函數也是這個):
int inet_sendmsg(struct kiocb *iocb, struct socket *sock, 
          struct msghdr *msg, size_t size)
{
    struct sock *sk = sock->sk;
    if (!inet_sk(sk)->num && inet_autobind(sk))
        return -EAGAIN;
     return sk->sk_prot->sendmsg(iocb, sk, msg, size);
}
       可以看到,在該函數中,調用了具體的第4層協議的操作函數集中的sendmsg成員函數,而該函數真正實現了對應協議的數據報文發送工作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章