struct socket结构体详解

在内核中为什么要有struct socket结构体呢?

   struct socket结构体的作用是什么?

   下面这个图,我觉得可以回答以上两个问题。 

    由这个图可知,内核中的进程可以通过使用struct socket结构体来访问linux内核中的网络系统中的传输层、网络层、数据链路层。也可以说struct socket是内核中的进程与内核中的网路系统的桥梁。

 

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层中的数据)。

   linux内核中的网络系统中的网络层的协议,在linux中被称为address family(地址簇,通常以AF_XXX表示)或protocol family(协议簇,通常以PF_XXX表示)。

  

        


 1.创建一个struct socket结构体:

   int sock_create(int family, int type, int protocol,

                    struct socket **res);

   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结构体的地址;

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;

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 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 : 为将要发送的数据的长度;

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;     
     
}

struct kvec
{
    void *iov_base; //用于存放来自应用层的数据;
    size_t iov_len; //来自应用层的数据的长度;
}

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

           struct kvec *vec, size_t num, size_t size)函数的实现为:


   有kernel_sendmsg()的实现代码可知,struct kvec中的数据部分最终还是要放到struct msghdr之中去的。

   也可以使用下面这个函数来实现相同的功能:

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

                    size_t size);

  EXPORT_SYMBOL(sock_sendmsg);

 













时间: 2024-10-06 05:27:34

struct socket结构体详解的相关文章

struct sk_buff结构体详解

struct sk_buff是linux网络系统中的核心结构体,linux网络中的所有数据包的封装以及解封装都是在这个结构体的基础上进行. struct sk_buff_head  {     struct sk_buff *next;     struct sk_buff *prev;          __u32 qlen;     spinlock_t lock; } struct sk_buff {     struct sk_buff *next;     struct sk_buff

struct ethhdr结构体详解

    在linux系统中,使用struct ethhdr结构体来表示以太网帧的头部.这个struct ethhdr结构体位于#include<linux/if_ether.h>之中. #define ETH_ALEN 6  //定义了以太网接口的MAC地址的长度为6个字节 #define ETH_HLAN 14  //定义了以太网帧的头长度为14个字节 #define ETH_ZLEN 60  //定义了以太网帧的最小长度为 ETH_ZLEN + ETH_FCS_LEN = 64个字节 #d

linux内核中的struct rlimit结构体详解

   在linux内核中,对一个进程获取系统资源的数量进行了限制.那么linux内核是如何实现这种对一个进程的各种资源的限制呢?    linux使用struct rlimit结构体来实现的,rlimit是 resource limit的缩写.    struct rlimit           {               unsigned int rlim_cur;  /* soft limit */               unsigned int rlim_max;  /* ha

NetBios 的结构体详解(网络控制块NCB)

对之前网络基础编程用到控制块NCB进行介绍(补充): 在Win32环境下,使用VC++6.0进行NetBIOS程序开发时, 需要用到nb30.h文件和netapi32.lib静态链接库.前者定义了NetBIOS的所有函数声明和常数定义,后者定义了NetBIOS应用. Ncb的结构在nb30.h文件中定义.Ncb结构的定义:Ncb有64个字符,分为14个域(或称为字段)和一个10字节的保留域,表2.1显示了Ncb和它的域. 域名及字节偏移量说明. 各个命令详细说明: 1. 命令 NetBIOS命令

NetBios 的结构体详解

[NetBios 的结构体详解] 1.结构体. 2.命令 NetBIOS命令的使用方式有两种,即等待和非等待(或称为同步与异步)方式. 如果命令码的高阶位是0时是等待方式,命令将阻止提交命令的应用程序继续执行,直到该NetBIOS命令执行完毕后才允许这个应用程序继续执行.NetBIOS每次只能处理一个等待方式命令. 如果命令码的高阶位是二进制1时则是非等待方式,命令由NetBIOS在内部排队,并不阻止提交命令的应用程序继续执行.应用程序可通过对命令结束标志字段值的轮询来了解命令执行的状态(详见命

struct net_device网络设备结构体详解

在linux中使用struct net_device结构体来描述每一个网络设备.同时这个用来刻画网络设备的struct net_device结构体包含的字段非常的多,以至于内核的开发者都觉得在现在的linux内核中,这个struct net_device是一个大的错误.    在本篇文章中,只介绍struct net_device中的一些字段,其他的字段在以后使用的时候再说.    #define IFNAMSIZ 32    struct net_device    {        //用于

结构体详解

1 概述 C语言允许用户自己指定这样一种数据结构,它由不同类型的数据组合成一个整体,以便引用,这些组合在一个整体中的数据是互相联系的,这样的数据结构称为结构体,它相当于其它高级语言中记录. 声明一个结构休类型的一般形式如下: struct 结构体名 {成员列表}; 结构体名,用作结构体类型的标志,它又称 结构体标记,大括号内是该结构体中的各个成员,由它们组成一个结构体,对各成员都应进行类型声明如: 类型名 成员名; 也可以把 成员列表称为 域表,第一个成员也称为结构体中的一个域.成员名定名规则写

struct结构体详解

为什么要有结构体 结构体和其他类型基础数据类型一样,例如int类型,char类型 只不过结构体可以做成你想要的数据类型.以方便日后的使用. 在实际项目中,结构体是大量存在的.研发人员常使用结构体来封装一些属性来组成新的类型.由于C语言内部程序比较简单,研发人员通常使用结构体创造新的"属性",其目的是简化运算. 结构体在函数中的作用不是简便,其最主要的作用就是封装.封装的好处就是可以再次利用.让使用者不必关心这个是什么,只要根据定义使用就可以了. 在C语言中,可以定义结构体类型,将多个相

Linux下DIR,dirent,stat等结构体详解

摘要: 最近在看Linux下文件操作相关章节,遇到了这么几个结构体,被搞的晕乎乎的,今日有空,仔细研究了一下,受益匪浅. DIR结构体类似于FILE,是一个内部结构,以下几个函数用这个内部结构保存当前正在被读取的目录的有关信息(摘自<UNIX环境高级编程(第二版)>).函数 DIR *opendir(const char *pathname),即打开文件目录,返回的就是指向DIR结构体的指针,而该指针由以下几个函数使用: 最近在看Linux下文件操作相关章节,遇到了这么几个结构体,被搞的晕乎乎