Linux网络协议栈(一)——Socket入门

socket:内核初始化及创建流(文件)详细过程 socket中文名称为套接字,属于传输协议及功能接口的封装。socket首先初始化注册(socket)文件系统,然后由使用它的模块传入具体的地址族(类型)family,如ipv4模块中的(void)sock_register(&inet_family_ops); 当调用者执行socket()函数时,会触发__sys_socket函数,完成socket文件的创建、修改模式为流模式、提供文件系统操作结构的功能接口、SELINUX策略绑定等等。... 阅读详情

https://www.cnblogs.com/hustcat/archive/2009/09/17/1568765.html

3、套接字的实现
套接字最先是在UNIX的BSD版本实现的,所以也叫做BSD套接字,它隐藏了各个协议之间的差异,并向上提供统一的接口。Linux中实现套接字的基本结构:

3.1、BSD套接字
3.1.1、核心数据结构
为了实现BSD套接字,内核提供一个重要的数据结构struct socket,它的定义如下:

复制代码

 //BSD套接字(include/linux/net.h)
struct socket {
    socket_state        state;  //套接字状态
    unsigned long        flags;
    struct proto_ops    *ops; //操作函数集
    struct fasync_struct    *fasync_list;
    struct file        *file;//每个BSD套接字都有一个inode结点,通过文件对象与其关联起来  
    struct sock        *sk; //socket内部结构,与具体的协议簇(比如PF_INET)相关
    wait_queue_head_t    wait;
    short            type;    //套接字类型:如SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, and SOCK_PACKET
    unsigned char        passcred; 
};

//BSD套接字操作函数集
struct proto_ops {
    int        family;
    struct module    *owner;
    int        (*release)   (struct socket *sock);
    int        (*bind)         (struct socket *sock,
                      struct sockaddr *myaddr,
                      int sockaddr_len);
    int        (*connect)   (struct socket *sock,
                      struct sockaddr *vaddr,
                      int sockaddr_len, int flags);
    int        (*socketpair)(struct socket *sock1,
                      struct socket *sock2);
    int        (*accept)    (struct socket *sock,
                      struct socket *newsock, int flags);
    int        (*getname)   (struct socket *sock,
                      struct sockaddr *addr,
                      int *sockaddr_len, int peer);
    unsigned int    (*poll)         (struct file *file, struct socket *sock,
                      struct poll_table_struct *wait);
    int        (*ioctl)     (struct socket *sock, unsigned int cmd,
                      unsigned long arg);
    int        (*listen)    (struct socket *sock, int len);
    int        (*shutdown)  (struct socket *sock, int flags);
    int        (*setsockopt)(struct socket *sock, int level,
                      int optname, char __user *optval, int optlen);
    int        (*getsockopt)(struct socket *sock, int level,
                      int optname, char __user *optval, int __user *optlen);
    int        (*sendmsg)   (struct kiocb *iocb, struct socket *sock,
                      struct msghdr *m, size_t total_len);
    int        (*recvmsg)   (struct kiocb *iocb, struct socket *sock,
                      struct msghdr *m, size_t total_len,
                      int flags);
    int        (*mmap)         (struct file *file, struct socket *sock,
                      struct vm_area_struct * vma);
    ssize_t        (*sendpage)  (struct socket *sock, struct page *page,
                      int offset, size_t size, int flags);
};
//BSD套接字状态
typedef enum {
    SS_FREE = 0,            /* not allocated        */
    SS_UNCONNECTED,            /* unconnected to any socket    */
    SS_CONNECTING,            /* in process of connecting    */
    SS_CONNECTED,            /* connected to socket        */
    SS_DISCONNECTING        /* in process of disconnecting    */
} socket_state;

复制代码

3.1.2、BSD套接字初始化

复制代码

//net/socket.c
//BSD套接字的初始化
void __init sock_init(void)
{
    int i;

    /*
     *    Initialize all address (protocol) families. 
     */
     
    for (i = 0; i < NPROTO; i++) 
        net_families[i] = NULL; //协议簇数组初始化

    /*
     *    Initialize sock SLAB cache.
     */
     //分配sock缓存
    sk_init();

#ifdef SLAB_SKB
    /*
     *    Initialize skbuff SLAB cache 
     */
    skb_init();
#endif

    /*
     *    Initialize the protocols module. 
     */

    init_inodecache();

    //注册sockfs文件系统
    register_filesystem(&sock_fs_type);
    //安装sockfs
    sock_mnt = kern_mount(&sock_fs_type);
    /* The real protocol initialization is performed when
     *  do_initcalls is run.  
     */

#ifdef CONFIG_NETFILTER
    netfilter_init();
#endif
}


//net/socket.c
//sockfs文件系统的安装点
static struct vfsmount *sock_mnt;
//sockfs文件系统类型
static struct file_system_type sock_fs_type = {
    .name =        "sockfs",
    .get_sb =    sockfs_get_sb,
    .kill_sb =    kill_anon_super,
}; 
//地址簇及协议信息
static struct net_proto_family *net_families[NPROTO];

复制代码

sock_init在系统初始化的被调用:

3.1.3、BSD套接字的系统调用
实际上,Linux内核只提供了一个与套接字相关的系统调用,即sys_socketcall,应用程序的所有套接字调用都会映射到这个系统调用上。

复制代码

//BSD套接字调用入口(net/socket.c)
asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
    unsigned long a[6];
    unsigned long a0,a1;
    int err;

    if(call<1||call>SYS_RECVMSG)
        return -EINVAL;

    /* copy_from_user should be SMP safe. */
    if (copy_from_user(a, args, nargs[call]))//从用户区拷贝参数
        return -EFAULT;
        
    a0=a[0];
    a1=a[1];
    
    switch(call)  //调用相应的函数
    {
        case SYS_SOCKET:
            err = sys_socket(a0,a1,a[2]);
            break;
        case SYS_BIND:
            err = sys_bind(a0,(struct sockaddr __user *)a1, a[2]);
            break;
        case SYS_CONNECT:
            err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
            break;
        case SYS_LISTEN:
            err = sys_listen(a0,a1);
            break;
        case SYS_ACCEPT:
            err = sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
            break;
        case SYS_GETSOCKNAME:
            err = sys_getsockname(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
            break;
        case SYS_GETPEERNAME:
            err = sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]);
            break;
        case SYS_SOCKETPAIR:
            err = sys_socketpair(a0,a1, a[2], (int __user *)a[3]);
            break;
        case SYS_SEND:
            err = sys_send(a0, (void __user *)a1, a[2], a[3]);
            break;
        case SYS_SENDTO:
            err = sys_sendto(a0,(void __user *)a1, a[2], a[3],
                     (struct sockaddr __user *)a[4], a[5]);
            break;
        case SYS_RECV:
            err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
            break;
        case SYS_RECVFROM:
            err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                       (struct sockaddr __user *)a[4], (int __user *)a[5]);
            break;
        case SYS_SHUTDOWN:
            err = sys_shutdown(a0,a1);
            break;
        case SYS_SETSOCKOPT:
            err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
            break;
        case SYS_GETSOCKOPT:
            err = sys_getsockopt(a0, a1, a[2], (char __user *)a[3], (int __user *)a[4]);
            break;
        case SYS_SENDMSG:
            err = sys_sendmsg(a0, (struct msghdr __user *) a1, a[2]);
            break;
        case SYS_RECVMSG:
            err = sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]);
            break;
        default:
            err = -EINVAL;
            break;
    }
    return err;
}

//include/asm/unistd.h
#define __NR_socketcall        102  //系统调用号

复制代码

下面来看一下sys_socket的实现:


//net/socket.c
/*创建socket
**首先建立一个socket数据结构,然后将其“映射”到一个已打开的文件.
*/
asmlinkage long sys_socket(int family, int type, int protocol)
{
    int retval;
    struct socket *sock;
    //创建socket
    retval = sock_create(family, type, protocol, &sock);
    if (retval < 0)
        goto out;
    //将socket映射到文件描述符
    retval = sock_map_fd(sock);
    if (retval < 0)
        goto out_release;

out:
    /* It may be already another descriptor 8) Not kernel problem. */
    return retval;

out_release:
    sock_release(sock);
    return retval;
}

int sock_create(int family, int type, int protocol, struct socket **res)
{
    return __sock_create(family, type, protocol, res, 0);
}

static int __sock_create(int family, int type, int protocol, struct socket **res, int kern)
{
    int i;
    int err;
    struct socket *sock;

    /*
     *    Check protocol is in range
     */
     //检查协议是否可用
    if (family < 0 || family >= NPROTO)
        return -EAFNOSUPPORT;
    if (type < 0 || type >= SOCK_MAX)
        return -EINVAL;

    /* Compatibility.

       This uglymoron is moved from INET layer to here to avoid
       deadlock in module load.
     */
    if (family == PF_INET && type == SOCK_PACKET) {
        static int warned; 
        if (!warned) {
            warned = 1;
            printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n", current->comm);
        }
        family = PF_PACKET;
    }

    err = security_socket_create(family, type, protocol, kern);
    if (err)
        return err;
        
#if defined(CONFIG_KMOD)
    /* Attempt to load a protocol module if the find failed. 
     * 
     * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user 
     * requested real, full-featured networking support upon configuration.
     * Otherwise module support will break!
     */
    if (net_families[family]==NULL)
    {
        request_module("net-pf-%d",family);
    }
#endif

    net_family_read_lock();
    if (net_families[family] == NULL) {
        i = -EAFNOSUPPORT;
        goto out;
    }

/*
 *    Allocate the socket and allow the family to set things up. if
 *    the protocol is 0, the family is instructed to select an appropriate
 *    default.
 */
    //从sockfs分配一个inode,并为之分配一个套接字结构
    if (!(sock = sock_alloc())) 
    {
        printk(KERN_WARNING "socket: no more sockets\n");
        i = -ENFILE;        /* Not exactly a match, but its the
                       closest posix thing */
        goto out;
    }
    //设置类型
    sock->type  = type;

    /*
     * We will call the ->create function, that possibly is in a loadable
     * module, so we have to bump that loadable module refcnt first.
     */
    i = -EAFNOSUPPORT;
    if (!try_module_get(net_families[family]->owner))
        goto out_release;
    
    //调用具体协议的create函数
    if ((i = net_families[family]->create(sock, protocol)) < 0)
        goto out_module_put;
    /*
     * Now to bump the refcnt of the [loadable] module that owns this
     * socket at sock_release time we decrement its refcnt.
     */
    if (!try_module_get(sock->ops->owner)) {
        sock->ops = NULL;
        goto out_module_put;
    }
    /*
     * Now that we're done with the ->create function, the [loadable]
     * module can have its refcnt decremented
     */
    module_put(net_families[family]->owner);
    *res = sock;
    security_socket_post_create(sock, family, type, protocol, kern);

out:
    net_family_read_unlock();
    return i;
out_module_put:
    module_put(net_families[family]->owner);
out_release:
    sock_release(sock);
    goto out;
}
///////////////////////////////////////////////////////////

int sock_map_fd(struct socket *sock)
{
    int fd;
    struct qstr this;
    char name[32];

    /*
     *    Find a file descriptor suitable for return to the user. 
     */
    //分配一个没有使用的描述符
    fd = get_unused_fd();
    if (fd >= 0) {
        struct file *file = get_empty_filp();

        if (!file) {
            put_unused_fd(fd);
            fd = -ENFILE;
            goto out;
        }

        sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino);
        this.name = name;
        this.len = strlen(name);
        this.hash = SOCK_INODE(sock)->i_ino;
        
        //从sockfs文件系统中分配一个目录项对象
        file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this);
        if (!file->f_dentry) {
            put_filp(file);
            put_unused_fd(fd);
            fd = -ENOMEM;
            goto out;
        }
        file->f_dentry->d_op = &sockfs_dentry_operations;
        
        //将目录项对象与sock的索引节点关联起来
        d_add(file->f_dentry, SOCK_INODE(sock));
        file->f_vfsmnt = mntget(sock_mnt);
        file->f_mapping = file->f_dentry->d_inode->i_mapping;
        
        //设置sock对应的文件对象
        sock->file = file;
        
        //设置文件对象的操作函数
        file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops;
        file->f_mode = FMODE_READ | FMODE_WRITE;
        file->f_flags = O_RDWR;
        file->f_pos = 0;
        fd_install(fd, file);
    }

out:
    return fd;
}

3.2、INET套接字
INET套接字就是支持 Internet 地址族的套接字,它位于TCP协议之上, BSD套接字之下,如下:

3.2.1、数据结构


//include/net/sock.h
//与特定协议相关的socket
struct sock {
    /*
     * Now struct tcp_tw_bucket also uses sock_common, so please just
     * don't add nothing before this first member (__sk_common) --acme
     */
    struct sock_common    __sk_common;
#define sk_family        __sk_common.skc_family
#define sk_state        __sk_common.skc_state
#define sk_reuse        __sk_common.skc_reuse
#define sk_bound_dev_if        __sk_common.skc_bound_dev_if
#define sk_node            __sk_common.skc_node
#define sk_bind_node        __sk_common.skc_bind_node
#define sk_refcnt        __sk_common.skc_refcnt
    volatile unsigned char    sk_zapped;
    unsigned char        sk_shutdown;
    unsigned char        sk_use_write_queue;
    unsigned char        sk_userlocks;
    socket_lock_t        sk_lock;
    int            sk_rcvbuf;
    wait_queue_head_t    *sk_sleep;
    struct dst_entry    *sk_dst_cache;
    rwlock_t        sk_dst_lock;
    struct xfrm_policy    *sk_policy[2];
    atomic_t        sk_rmem_alloc;
    struct sk_buff_head    sk_receive_queue;
    atomic_t        sk_wmem_alloc;
    struct sk_buff_head    sk_write_queue;
    atomic_t        sk_omem_alloc;
    int            sk_wmem_queued;
    int            sk_forward_alloc;
    unsigned int        sk_allocation;
    int            sk_sndbuf;
    unsigned long         sk_flags;
    char             sk_no_check;
    unsigned char        sk_debug;
    unsigned char        sk_rcvtstamp;
    unsigned char        sk_no_largesend;
    int            sk_route_caps;
    unsigned long            sk_lingertime;
    int            sk_hashent;
    /*
     * The backlog queue is special, it is always used with
     * the per-socket spinlock held and requires low latency
     * access. Therefore we special case it's implementation.
     */
    struct {
        struct sk_buff *head;
        struct sk_buff *tail;
    } sk_backlog;
    rwlock_t        sk_callback_lock;
    struct sk_buff_head    sk_error_queue;
    
    struct proto        *sk_prot;
    
    int            sk_err,
                sk_err_soft;
    unsigned short        sk_ack_backlog;
    unsigned short        sk_max_ack_backlog;
    __u32            sk_priority;
    unsigned short        sk_type;
    unsigned char        sk_localroute;
    unsigned char        sk_protocol;
    struct ucred        sk_peercred;
    int            sk_rcvlowat;
    long            sk_rcvtimeo;
    long            sk_sndtimeo;
    struct sk_filter          *sk_filter;
    void            *sk_protinfo;
    kmem_cache_t        *sk_slab;
    struct timer_list    sk_timer;
    struct timeval        sk_stamp;
    struct socket        *sk_socket;
    void            *sk_user_data;
    struct module        *sk_owner;
    struct page        *sk_sndmsg_page;
    __u32            sk_sndmsg_off;
    struct sk_buff        *sk_send_head;
    int            sk_write_pending;
    void            *sk_security;
    __u8            sk_queue_shrunk;
    /* three bytes hole, try to pack */
    void            (*sk_state_change)(struct sock *sk);
    void            (*sk_data_ready)(struct sock *sk, int bytes);
    void            (*sk_write_space)(struct sock *sk);
    void            (*sk_error_report)(struct sock *sk);
      int            (*sk_backlog_rcv)(struct sock *sk,
                          struct sk_buff *skb);  
    void                    (*sk_destruct)(struct sock *sk);
};

//底层协议的操作函数
struct proto {
    void            (*close)(struct sock *sk, 
                    long timeout);
    int            (*connect)(struct sock *sk,
                        struct sockaddr *uaddr, 
                    int addr_len);
    int            (*disconnect)(struct sock *sk, int flags);

    struct sock *        (*accept) (struct sock *sk, int flags, int *err);

    int            (*ioctl)(struct sock *sk, int cmd,
                     unsigned long arg);
    int            (*init)(struct sock *sk);
    int            (*destroy)(struct sock *sk);
    void            (*shutdown)(struct sock *sk, int how);
    int            (*setsockopt)(struct sock *sk, int level, 
                    int optname, char __user *optval,
                    int optlen);
    int            (*getsockopt)(struct sock *sk, int level, 
                    int optname, char __user *optval, 
                    int __user *option);       
    int            (*sendmsg)(struct kiocb *iocb, struct sock *sk,
                       struct msghdr *msg, size_t len);
    int            (*recvmsg)(struct kiocb *iocb, struct sock *sk,
                       struct msghdr *msg,
                    size_t len, int noblock, int flags, 
                    int *addr_len);
    int            (*sendpage)(struct sock *sk, struct page *page,
                    int offset, size_t size, int flags);
    int            (*bind)(struct sock *sk, 
                    struct sockaddr *uaddr, int addr_len);

    int            (*backlog_rcv) (struct sock *sk, 
                        struct sk_buff *skb);

    /* Keeping track of sk's, looking them up, and port selection methods. */
    void            (*hash)(struct sock *sk);
    void            (*unhash)(struct sock *sk);
    int            (*get_port)(struct sock *sk, unsigned short snum);

    /* Memory pressure */
    void            (*enter_memory_pressure)(void);
    atomic_t        *memory_allocated;    /* Current allocated memory. */
    atomic_t        *sockets_allocated;    /* Current number of sockets. */
    /*
     * Pressure flag: try to collapse.
     * Technical note: it is used by multiple contexts non atomically.
     * All the sk_stream_mem_schedule() is of this nature: accounting
     * is strict, actions are advisory and have some latency.
     */
    int            *memory_pressure;
    int            *sysctl_mem;
    int            *sysctl_wmem;
    int            *sysctl_rmem;
    int            max_header;

    kmem_cache_t        *slab;
    int            slab_obj_size;

    struct module        *owner;

    char            name[32];

    struct {
        int inuse;
        u8  __pad[SMP_CACHE_BYTES - sizeof(int)];
    } stats[NR_CPUS];
};

inet_init()函数:


//net/ipv4/af_inet.c
/*系统初始化时被调用
**调用路径:start_kernel() -->init() -->do_basic_setup() -->do_initcalls()-->inet_init()
*/
static int __init inet_init(void)
{
    struct sk_buff *dummy_skb;
    struct inet_protosw *q;
    struct list_head *r;
    int rc = -EINVAL;

    if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb)) {
        printk(KERN_CRIT "%s: panic\n", __FUNCTION__);
        goto out;
    }

    rc = sk_alloc_slab(&tcp_prot, "tcp_sock");
    if (rc) {
        sk_alloc_slab_error(&tcp_prot);
        goto out;
    }
    rc = sk_alloc_slab(&udp_prot, "udp_sock");
    if (rc) {
        sk_alloc_slab_error(&udp_prot);
        goto out_tcp_free_slab;
    }
    rc = sk_alloc_slab(&raw_prot, "raw_sock");
    if (rc) {
        sk_alloc_slab_error(&raw_prot);
        goto out_udp_free_slab;
    }

    /*
     *    Tell SOCKET that we are alive 
     */
    //注册Internet协议簇的相关信息
      (void)sock_register(&inet_family_ops);

    /*
     *    Add all the base protocols.
     */
    //添加基本的协议
    if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
        printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");
    if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
        printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
    if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
        printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
#ifdef CONFIG_IP_MULTICAST
    if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
        printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
#endif

    /* Register the socket-side information for inet_create. */
    for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
        INIT_LIST_HEAD(r);
        
  //将inetsw_array中元素加入到inetsw链表中
    for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
        inet_register_protosw(q);

    /*
     *    Set the ARP module up
     */

    arp_init(); //ARP协议初始化

      /*
       *    Set the IP module up
       */

    ip_init(); //IP协议初始化

    tcp_v4_init(&inet_family_ops);

    /* Setup TCP slab cache for open requests. */
    tcp_init();


    /*
     *    Set the ICMP layer up
     */

    icmp_init(&inet_family_ops);

    /*
     *    Initialise the multicast router
     */
#if defined(CONFIG_IP_MROUTE)
    ip_mr_init();
#endif
    /*
     *    Initialise per-cpu ipv4 mibs
     */ 

    if(init_ipv4_mibs())
        printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ;
    
    ipv4_proc_init();

    ipfrag_init();

    rc = 0;
out:
    return rc;
out_tcp_free_slab:
    sk_free_slab(&tcp_prot);
out_udp_free_slab:
    sk_free_slab(&udp_prot);
    goto out;
}

//net/ipv4/af_inet.c
//INET协议簇信息
static struct net_proto_family inet_family_ops = {
    .family = PF_INET,
    .create = inet_create,
    .owner    = THIS_MODULE,
};

static struct list_head inetsw[SOCK_MAX];
//该数组中的所有元素都会插入到inetsw的链表中
static struct inet_protosw inetsw_array[] =
{
        {
                .type =       SOCK_STREAM,
                .protocol =   IPPROTO_TCP,
                .prot =       &tcp_prot,
                .ops =        &inet_stream_ops,
                .capability = -1,
                .no_check =   0,
                .flags =      INET_PROTOSW_PERMANENT,
        },

        {
                .type =       SOCK_DGRAM,
                .protocol =   IPPROTO_UDP,
                .prot =       &udp_prot,
                .ops =        &inet_dgram_ops,
                .capability = -1,
                .no_check =   UDP_CSUM_DEFAULT,
                .flags =      INET_PROTOSW_PERMANENT,
       },
        

       {
               .type =       SOCK_RAW,
               .protocol =   IPPROTO_IP,    /* wild card */
               .prot =       &raw_prot,
               .ops =        &inet_sockraw_ops,
               .capability = CAP_NET_RAW,
               .no_check =   UDP_CSUM_DEFAULT,
               .flags =      INET_PROTOSW_REUSE,
       }
};

//流套接字操作函数
struct proto_ops inet_stream_ops = {
    .family =    PF_INET,
    .owner =    THIS_MODULE,
    .release =    inet_release,
    .bind =        inet_bind,
    .connect =    inet_stream_connect,
    .socketpair =    sock_no_socketpair,
    .accept =    inet_accept,
    .getname =    inet_getname,
    .poll =        tcp_poll,
    .ioctl =    inet_ioctl,
    .listen =    inet_listen,
    .shutdown =    inet_shutdown,
    .setsockopt =    sock_common_setsockopt,
    .getsockopt =    sock_common_getsockopt,
    .sendmsg =    inet_sendmsg,
    .recvmsg =    sock_common_recvmsg,
    .mmap =        sock_no_mmap,
    .sendpage =    tcp_sendpage
};
//tcp协议
static struct net_protocol tcp_protocol = {
    .handler =    tcp_v4_rcv,
    .err_handler =    tcp_v4_err,
    .no_policy =    1,
};

static struct net_protocol udp_protocol = {
    .handler =    udp_rcv,
    .err_handler =    udp_err,
    .no_policy =    1,
};

static struct net_protocol icmp_protocol = {
    .handler =    icmp_rcv,
};


//net/ipv4/tcp_ipv4.c
//tcp协议的操作函数
struct proto tcp_prot = {
    .name            = "TCP",
    .owner            = THIS_MODULE,
    .close            = tcp_close,
    .connect        = tcp_v4_connect,
    .disconnect        = tcp_disconnect,
    .accept            = tcp_accept,
    .ioctl            = tcp_ioctl,
    .init            = tcp_v4_init_sock,
    .destroy        = tcp_v4_destroy_sock,
    .shutdown        = tcp_shutdown,
    .setsockopt        = tcp_setsockopt,
    .getsockopt        = tcp_getsockopt,
    .sendmsg        = tcp_sendmsg,
    .recvmsg        = tcp_recvmsg,
    .backlog_rcv        = tcp_v4_do_rcv,
    .hash            = tcp_v4_hash,
    .unhash            = tcp_unhash,
    .get_port        = tcp_v4_get_port,
    .enter_memory_pressure    = tcp_enter_memory_pressure,
    .sockets_allocated    = &tcp_sockets_allocated,
    .memory_allocated    = &tcp_memory_allocated,
    .memory_pressure    = &tcp_memory_pressure,
    .sysctl_mem        = sysctl_tcp_mem,
    .sysctl_wmem        = sysctl_tcp_wmem,
    .sysctl_rmem        = sysctl_tcp_rmem,
    .max_header        = MAX_TCP_HEADER,
    .slab_obj_size        = sizeof(struct tcp_sock),
};

sock_register()函数:

Code

inet_create()函数

Code

 

 

【100%通过率】华为od统一考试B卷【贪吃蛇】Python 实现 贪吃蛇是一个经典游戏,蛇的身体由若干方格连接而成,身体随蛇头移动。蛇头触碰到食物时,蛇的长度会增加一格。蛇头和身体的任一方格或者游戏版图边界碰撞时,游戏结束。 下面让我们来完成贪吃蛇游戏的模拟。给定一个N*M的数组arr,代表N*M个方格组成的版图,贪吃蛇每次移动一个方格。若arr[i][j] == ‘H’,表示该方格为贪吃蛇的起始位置;若arr[i][j] == ‘F’,表示该方格为食物,若arr[i][j] == ‘E’,表示该方格为空格。贪吃蛇初始长度为1,初始移动方向为向左。 阅读详情

相关推荐

手机拍照对焦快准狠的秘密:一文看懂PDAF、Dual Pixel和Super PD的区别

本文深入解析手机拍照中的三种主流相位检测自动对焦(PDAF)技术:Shield Pixel、Super PD和Dual Pixel。通过对比它们的原理、优缺点及实际表现,帮助读者理解不同技术的对焦速度、暗光表现和画质影响,从而做出更明智的购机选择。

weixin_26985923的博客 488

linux网络数据发送流程

网络套接字如何将数据发生出去的呢?这个需要从网络协议初始化开始分析。 网络协议初始化: 所在文件net/ipv4/af_inet.c static int __init inet_init(void) 上面接口将初始化ipv4相关协议 socket模块初始化: 所在文件net/socket.c static int __init sock_init(void) { int err; /* * Initialize the network sysctl infrastr.

一个码农的足迹 3231

RTX 50系显卡(5080/5090D)CUDA 12.8环境下的vLLM部署避坑与实战指南

本文为RTX 5080/5090D显卡用户提供了在CUDA 12.8环境下部署vLLM的完整实战指南。文章详细介绍了从驱动安装、CUDA 12.8环境配置、PyTorch Nightly版本精准匹配,到vLLM源码编译及常见报错排查的全过程,帮助开发者高效利用新一代硬件进行大模型推理。

gray5的博客 670

TCP/IP源码学习(47)——socket与VFS的关联(1)

作者:gfree.wind@gmail.com 博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net 本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。 ...

weixin_34212762的博客 219

win10 离线安装.Net Framework3.5

起因,安装软件比如3dmax2014等需要.Net Framework3.5支撑,那么需要安装低版本的.Net Framework3.5 闲话少说。 1.下载 NetFx3.cab 链接: https://pan.baidu.com/s/124to7F3BOy4yAlY61OjbDg 提取码: kixu 2.将NetFx3.cab 放入 系统盘的windows目录下(示例 C:\Wi...

m0_37615458的博客 7724

项目经理面试 5 大高频题 + 实战回答模板,帮你稳拿 offer

项目经理面试5大高频问题及应答技巧 摘要:本文总结了项目经理岗位常见的5个面试问题及回答策略,帮助求职者更有针对性地准备面试。核心问题包括:1)描述负责过的典型项目;2)常用项目管理工具;3)项目遇到的挑战及解决方案;4)风险管理方法;5)项目成功标准。每个问题都从面试官考察重点、回答技巧、实战案例三个维度进行分析,强调要用STAR法则(情境-任务-行动-结果)结构化表达,突出解决问题的能力而非简单罗列经历。特别提醒要量化项目成果(如提前2周上线、节省8%预算等),并注意展示商业思维和团队协作能力。

逸尘谈PM 2106

Linux TCP/IP协议栈之Socket的实现分析 第一部份 Socket套接字的创建 (-)

内核版本:2.6.12 作者:kendo 版权所有,转载请注明出处[www.skynet.org.cn]; 说明:这仅仅是一个笔记,由于偶的水平有限,我甚至不能保证其中内容正确率超过80%。另外,我不太习惯在代码中注解来自哪个文件,第几行之类的,因为偶是直接通过source insight双击鼠标跳转之。 第一部份 Socket套接字的创建 socket并不是TCP/IP协议的一

klarclm的专栏 1275

NL2SQL进阶系列(3):Data-Copilot、Chat2DB、Vanna Text2SQL优化框架开源应用实践详解[Text2SQL]

NL2SQL进阶系列(3):Data-Copilot、Chat2DB、Vanna Text2SQL优化框架开源应用实践详解[Text2SQL]

丨汀、的博客 5421

proc文件系统下各参数解析

proc全称是process information pseudo-filesystem,翻译过来就是进程信息伪文件系统。 proc文件系统是一个伪文件系统,它提供了一个到内核数据结构的接口,一般挂载在/proc。proc文件系统中的大多数文件都是只读的,但有些文件是可写的,允许修改内核变量。在Ubuntu20.04,内核版本是5.15.0-56。我们可以看到proc文件系统下的文件和目录如下图所示: 我们首先看到的是很多的数字,这些数字就是我们系统的每一个进程的信息,数字目录下面的文件的含义是一样的。我

jianjian的博客 4438

【3天快速入门WPF】1-从最简单的xaml开始

首先我们要知道xaml语法中声明即定义,意思就是“见到元素就相当于创建实例”,MainWindow.xaml的主干是一个元素里包含着一个 元素。这段程序就是在定义一个Window类的实例,这个实例的一个组成部分是一个Grid类的实例。

Zy100Papa的博客 1623

电子元件及其规格书的查找与理解

本内容收录:在电子开发或维修过程中,查找电子元件及其规格书心得体会,以及元件规格书的理解,系个人多年实战经验的系统梳理。内容着重介绍贴片元件代码反查型号的实用技巧,并以三款典型疑难元件为例,详述其完整查找过程与最终型号确认。

让你爱上电路设计 3993

linux网络协议栈(一)-socket入门

1、TCP/IP参考模型 为了实现各种网络的互连,国际标准化组织(ISO)制定了开放式系统互连(OSI)参考模型。尽管OSI的体系结构从理论上讲是比较完整的,但实际上,完全符合OSI各层协议的商用产品却很少进入市场。而使用TCP/IP 协议的产品却大量涌入市场,几乎所有的工作站都配有TCP/IP协议,使得TCP/IP 成为计算机网络的实际的国际标准。 2、套接字(socket)  socket是操...

weichaopst的专栏 578

WordPress插件depicter存在SQL注入漏洞(CVE-2025-2011)

Depicter 是一款功能强大的 WordPress 幻灯片插件,允许用户通过直观的拖放编辑器轻松创建响应式滑块、轮播和动态内容展示。该插件提供丰富的模板库、动画效果和图层控制,支持多媒体内容嵌入(如图片、视频、HTML),并优化了移动端显示和SEO性能。其无代码操作界面适合非技术用户,同时提供高级自定义选项,适用于产品展示、营销推广和视觉叙事等多种场景,是提升网站视觉吸引力的高效工具。

m0_50125527的博客 555

linux 协议栈之socket,Linux网络协议栈(一)——Socket入门(1)

Linux网络协议栈(一)——Socket入门(1)1、TCP/IP参考模型为了实现各种网络的互连,国际标准化组织(ISO)制定了开放式系统互连(OSI)参考模型。尽管OSI的体系结构从理论上讲是比较完整的,但实际上,完全符合OSI各层协议的商用产品却很少进入市场。而使用TCP/IP 协议的产品却大量涌入市场,几乎所有的工作站都配有TCP/IP协议,使得TCP/IP 成为计算机网络的实际的国际标准...

weixin_27015375的博客 297

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Linux网络协议栈(一)——Socket入门(1)

1、TCP/IP参考模型 为了实现各种网络的互连,国际标准化组织(ISO)制定了开放式系统互连(OSI)参考模型。尽管OSI的体系结构从理论上讲是比较完整的,但实际上,完全符合OSI各层协议的商用产品却很少进入市场。而使用TCP/IP 协议的产品却大量涌入市场,几乎所有的工作站都配有TCP/IP协议,使得TCP/IP 成为计算机网络的实际的国际标准。 2、套接字(socket) 

华的专栏 576

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Linux网络协议栈(一) -- socket入门

1、TCP/IP参考模型 为了实现各种网络的互连,国际标准化组织(ISO)制定了开放式系统互连(OSI)参考模型。尽管OSI的体系结构从理论上讲是比较完整的,但实际上,完全符合OSI各层协议的商用产品却很少进入市场。而使用TCP/IP 协议的产品却大量涌入市场,几乎所有的工作站都配有TCP/IP协议,使得TCP/IP 成为计算机网络的实际的国际标准。 2、套接字(socket)

淡红星空的专栏 2184

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Linux网络协议栈(一) -- socket入门(2)

3、套接字的实现 套接字最先是在UNIX的BSD版本实现的,所以也叫做BSD套接字,它隐藏了各个协议之间的差异,并向上提供统一的接口。Linux中实现套接字的基本结构: 3.1、BSD套接字 3.1.1、核心数据结构 为了实现BSD套接字,内核提供一个重要的数据结构struct socket,它的定义如下:  //BSD套接字(include/linux/net.h)

zhaqiwen的专栏 1631

Linux网络协议栈(一)——Socket入门(2)

转自:http://www.cnblogs.com/hustcat/archive/2009/09/17/1568765.html 3、套接字的实现套接字最先是在UNIX的BSD版本实现的,所以也叫做BSD套接字,它隐藏了各个协议之间的差异,并向上提供统一的接口。Linux中实现套接字的基本结构:3.1、BSD套接字3.1.1、核心数据结构为了实现BSD套接字,内核提供一个重要的数据结构str...

weixin_30478923的博客 98
上一篇: struct sk_buff结构体详解
下一篇: 2020-08-14
TuxedoLinux
博客等级 码龄9年 268粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值