struct socket結構體詳解

原創做品,容許轉載,轉載時請務必以超連接形式標明文章  原始出處 、做者信息和本聲明。不然將追究法律責任。 http://weiguozhihui.blog.51cto.com/3060615/1585297

    在內核中爲何要有struct socket結構體呢?node

   struct socket結構體的做用是什麼?linux

   下面這個圖,我以爲能夠回答以上兩個問題。  wKioL1R8EgfAF5ZDAAGd1S8RhIA115.jpg數組

 

    由這個圖可知,內核中的進程能夠經過使用struct socket結構體來訪問linux內核中的網絡系統中的傳輸層、網絡層、數據鏈路層。也能夠說struct socket是內核中的進程與內核中的網路系統的橋樑。緩存

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
struct  socket
{
      socket_state  state;  // socket state
      
      short    type ;  // socket type
      
      unsigned  long   flags;  // socket flags
      
      struct  fasync_struct  *fasync_list;
      
      wait_queue_head_t wait;
      
      struct  file *file;
      
      struct  sock *sock;   // socket在網絡層的表示;
      
      const  struct  proto_ops *ops;
           
}
 
 
struct  socket結構體的類型
enum  sock_type
{
    SOCK_STREAM = 1,  // 用於與TCP層中的tcp協議數據的struct socket
    
    SOCK_DGRAM  = 2,  //用於與TCP層中的udp協議數據的struct socket
    
    SOCK_RAW    = 3,  // raw struct socket
    
    SOCK_RDM    = 4,  //可靠傳輸消息的struct socket
    
    SOCK_SEQPACKET = 5, // sequential packet socket
    
    SOCK_DCCP   = 6,
    
    SOCK_PACKET = 10,  //從dev level中獲取數據包的socket
};
 
struct  socket 中的flags字段取值:
   #define SOCK_ASYNC_NOSPACE  0
   #define SOCK_ASYNC_WAITDATA 1
   #define SOCK_NOSPACE        2
   #define SOCK_PASSCRED       3
   #define SOCK_PASSSEC        4


   咱們知道在TCP層中使用兩個協議:tcp協議和udp協議。而在將TCP層中的數據往下傳輸時,要使用網絡層的協議,而網絡層的協議不少,不一樣的網絡使用不一樣的網絡層協議。咱們經常使用的因特網中,網絡層使用的是IPV4和IPV6協議。
網絡

   因此在內核中的進程在使用struct socket提取內核網絡系統中的數據時,不光要指明struct socket的類型(用於說明是提取TCP層中tcp協議負載的數據,仍是udp層負載的數據),還要指明網絡層的協議類型(網絡層的協議用於負載TCP層中的數據)。socket

   linux內核中的網絡系統中的網絡層的協議,在linux中被稱爲address family(地址簇,一般以AF_XXX表示)或protocol family(協議簇,一般以PF_XXX表示)。async

   wKiom1R8Hx3THEffAAVlvwxDKzk034.jpg

 wKioL1R8H7vSc54pAAN3Efqa5N0077.jpg        

wKiom1R8H9Lz9dl4AAMbicmeYGI140.jpg

wKioL1R8IG2DYTRwAAKqw4_sJrE248.jpg

 

 1.建立一個struct socket結構體:tcp

   int sock_create(int family, int type, int protocol, 
函數

                    struct socket **res);ui

   int sock_create_kern(int family, int type, int protocol,

                         struct socket **res);

   EXPROT_SYMBOL(sock_create);

   EXPROT_SYMBOL(sock_create_kern);

   family : 指定協議簇的類型,其值爲:PF_XXX或 AF_XXX

   type   : 指定要建立的struct socket結構體的類型;

   protocol : 通常爲0;

   res    : 中存放建立的struct socket結構體的地址;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
int  sock_create( int  family,  int  type,  int  protocol,  struct  socket **res)
{
    return  __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}
 
int  sock_create_kern( int  family,  int  type,  int  protocol,  struct  socket **res)
{
    return  __sock_create( &init_net, family, type, protocot, res, 1 );
}
若是在內核中建立 struct  socket時,推薦使用sock_create_kern()函數;
 
 
// 網絡協議簇結構體
struct  net_proto_family
{
    int  family ;  // 協議簇
    int  (*create)( struct  net *net,  struct  socket *sock,   int  protocol);
    struct  module  *owner;
};
 
內核中的全部的網絡協議的響應的網絡協議簇結構體都存放在 net_families[]指針數組中;
static  struct  net_proto_family *net_families[NPROTO];
 
static  int  __sock_create( struct  net *net,  int  family,  int  type,  int  protocol, 
                          struct  socket **res,  int  kern )
{
     struct  socket *sock;
     struct  net_proto_family *pf;
     
     sock = sock_alloc(); //分配一個struct socket 結構體
     sock->type = type;
     
     pf = rcu_dereference(net_families[family]);  //獲取相應的網絡協議簇結構體的地址;
     pf->create(net, sock, protocol);  // 對struct socket結構體作相應的處理;
     
     *res = sock;  // res中保存建立的struct socket結構體的地址;
     return  0;
}
 
 
struct  socket_alloc
{
    struct  socket socket ;
    struct  inode vfs_node ;
}
 
static  inline  struct  socket *SOCKET_I( struct  inode *inode)
{
    return  &contain_of(inode,  struct  socket_alloc, vfs->node)->socket;
}
static  struct  socket *sock_alloc( void )
{
      struct  inode *inode;
      struct  socket *sock;
      inode = new_inode(sock_mnt->mnt_sb); //分配一個新的struct inode節點
      sock = SOCKET_I(inode); 
      inode->i_mode = S_IFSOCK | S_IRWXUGO; //設置inode節點的權限
      inode->i_uid = current_fsuid();  // 設置節點的UID
      inode->i_gid = current_fsgid();  //設置節點的GID
      
      return  sock; 
}


  有以上的代碼可知:linux內核在使用sock_create()、sock_create_kern()

進行struct socket結構體的建立時,其本質是分配了一個struct socket_alloc

結構體,而這個struct socket_alloc結構體中包含了struct socket 和struct

inode(struct inode結構體,是linux內核用來刻畫一個存放在內存中的文件的,經過將struct inode 和 struct socket綁定在一塊兒造成struct socket_alloc結構體,來表示內核中的網絡文件)。而後對分配的struct socket結構體進行初始化,來定義內核中的網絡文件的類型(family, type, protocol).

 

   在linux網絡系統中還有兩個很是重要的套接字地址結構體:

          struct sockaddr_in

          struct sockaddr;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
typedef  unsigned  short  sa_family_t;
 
// Internet Address
struct  in_addr{
     __b32 s_addr;
}
 
//struct describing an Internet socket address
//sockaddr_in 中存放端口號、網路層中的協議類型(ipv4,ipv6)等,網絡層的IP地址;
struct  sockaddr_in
{
     sa_family_t sin_family ;  // Address family AF_XXX
     
     __be16      sin_port   ;  // 端口號
     
     struct  in_addr sin_addr ;  // Internet Address 
     
     /*Pad to size of  'struct sockaddr'*/ 
     ...........  
};
 
//套接字地址結構體。
struct  sockaddr
{
     sa_family_t sa_family;  // 存放網絡層所使用的協議類型(AF_XXX 或 PF_XXX);
     char  sa_data[14];    // 裏面存放端口號、網絡層地址等信息;
}

 

   從本質上來講,struct sockaddr與struct sockaddr_in是相同的。

   但在,實際的使用過程當中,struct sockaddr_in是 Internet環境下的套接字地址形式,而struct sockaddr是經過的套接字地址個形式。在linux內核中struct sockaddr使用的更多,目的是使linux內核代碼更爲通用。

   struct sockaddr_in 能夠與 struct sockaddr 進行自由的轉換。

  

   2.將建立的套接字(struct socket)與套接字地址結構體(struct sockaddr or struct sockaddr_in)進行綁定:

     int kernel_bind(struct socket *sock, struct sockaddr *addr,

                     int addrlen)


     EXPROT_SYMBOL(kernel_bind);

     sock : 爲經過sock_create()或sock_create_kern()建立的套接字;

     addr : 爲套接字地址結構體;

     addrlen:爲套接字地址結構體的大小;

 

   3.將一個套接字(struct socket)設置爲監聽狀態:

     int kernel_listen(struct socket *sock, int backlog);

     backlog :通常狀況下設置爲0;

     EXPORT_SYMBOL(kernel_listen);

 

   4.當把一個套接字設置爲監聽狀態之後,使用這個套接字去監聽其它的套接字;

   int kernel_accept(struct socket *sock, struct socket **new_sock,

                      int flags);

   EXPORT_SYMBOL(kernel_accept);

   sock : listening socket 處於監聽狀態的套接字;

   new_sock : 被監聽的套接字;

   flags: struct socket中的flags字段的取值;

 

   5.把一個套接字鏈接到另外一個套接字地址結構體上:

   int kernel_connect(struc socket *sock, struct sockaddr *addr,

                       int addrlen, int flags);

   EXPORT_SYMBOL(kernel_connect);

   sock : struct socket;

   addr : 爲另外一個新的套接字地址結構體;

   addrlen : 套接字地址結構體的大小;

   flags :file-related flags associated with socket

 

   6.把一個應用層中的數據發送給另外一個設備中的進程:

     int kernel_sendmsg(struct socket *sock, struct msghdr *msg,

                         struct kvec *vec, size_t num, size_t size)

     EXPORT_SYMBOL(kernel_sendmsg);

     sock : 爲當前進程中的struct socket套接字;

     msg  : 用於接收來自應用層的數據包;

     kvec : 中存放將要發送出去的數據;

     num  : 見代碼;

     size : 爲將要發送的數據的長度;

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct  iovec
{
     void  __user *iov_base;
     __kernel_size_t iov_len;
}
 
struct  msghdr
{
      //用於存放目的進程所使用的套接字地址
      void  *msg_name;   // 用於存放目的進程的struct sockaddr_in 
      int    msg_namelen;  // 目的進程的sizeof(struct sockaddr_in)
      
      
      //用於來自應用層的數據
      struct  iovec *msg_iov ; // 指向一個struct iovec的數組,數組中的每一個成員表示一個數據塊
      __kernel_size_t  msg_iovlen ;  //數據塊數,即struct iovec數組的大小
      
      
      //用於存放一些控制信息
      void  *msg_control ;
      __kernel_size_t msg_controllen;  //控制信息的長度;
      
      
      //
      int  msg_flags;     
      
}

 

1
2
3
4
5
struct  kvec
{
     void  *iov_base;  //用於存放來自應用層的數據;
     size_t  iov_len;  //來自應用層的數據的長度;
}

 

 struct msghdr中的flags字段的取值爲:

wKiom1R9K3zSLJWrAASNJz0QxPc672.jpg

 

int kernel_sendmsg(struct socket *sock, struct msghdr *msg,

           struct kvec *vec, size_t num, size_t size)函數的實現爲:

wKioL1R8mqCDxdedAALtXFBqeDk456.jpg

 

   有kernel_sendmsg()的實現代碼可知,struct kvec中的數據部分最終仍是要放到struct msghdr之中去的。

   kernel_sendmsg()的用法:

wKiom1R9KNyQKTtUAAT07fVKu2A037.jpg

   也可使用下面這個函數來實現相同的功能:

  int sock_sendmsg(struct socket *sock, struct msghdr *msg,

                    size_t size);

  EXPORT_SYMBOL(sock_sendmsg);

  

  

  7.接受來自另外一個網絡進程中的數據:

    int kernel_recvmsg(struct socket *sock, struct msghdr *msg,

              struct kvec *vec, size_t num, size_t size, int flags)

    EXPORT_SYMBOL(kernel_recvmsg);

    sock : 爲接受進程的套接字;

    msg  : 用於存放接受到的數據;

    vec  : 用於指向本地進程中的緩存區;

    num  : 爲數據塊的塊數;

    size : 緩存區的大小;

    flags: struct msghdr中的flags字段中的取值範圍;

  int kernel_recvmsg()的實現:

wKiom1R9KnbxNq6eAAKakSYH0Js684.jpg

  kernel_recvmsg()的用法:

wKioL1R9JlixQrpdAAJD7SWJH2g182.jpg

 

 

  8.關閉一個套接字:

    void sock_release(struct socket *sock);

      用於關閉一個套接字,而且若是一個它struct socket綁定到了一個struct

inode節點上的話,相應的struct inode也會被釋放。

 

  

    以上這些函數位於linux源代碼包中的/net/socket.c之中。

本文出自 「阿輝仔」 博客,請務必保留此出處http://weiguozhihui.blog.51cto.com/3060615/1585297

相關文章
相關標籤/搜索