内核中的UDP socket流程(4)——sock_create

本文详细解析了内核中UDPsocket的创建流程,包括对PF_INET和SOCK_PACKET的兼容性处理,socket节点的分配过程,以及通过RCU机制获取协议家族对应的指针等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内核中的UDP socket流程(4)——sock_create
作者:gfree.wind@gmail.com

又懒了2天,继续sock_create

    /* 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;

首先是对PF_INET,SOCK_PACKET的兼容性处理。 security_socket_create函数是一个空函数,省略。
下面是去申请一个socket node,我们需要进入sock_alloc中看如何分配的节点。

    /*
     *    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.
     */

    sock = sock_alloc();
    if (!sock) {
        if (net_ratelimit())
            printk(KERN_WARNING "socket: no more sockets\n");
        return -ENFILE;    /* Not exactly a match, but its the
                 closest posix thing */

    }



下面是sock_alloc的代码

static struct socket *sock_alloc(void)
{
    struct inode *inode;
    struct socket *sock;

    inode = new_inode(sock_mnt->mnt_sb);
    if (!inode)
        return NULL;

    sock = SOCKET_I(inode);

    kmemcheck_annotate_bitfield(sock, type);
    inode->i_mode = S_IFSOCK | S_IRWXUGO;
    inode->i_uid = current_fsuid();
    inode->i_gid = current_fsgid();

    percpu_add(sockets_in_use, 1);
    return sock;
}

其中sock_mnt是一个全局变量,它在sock_init中被初始化的。并挂载到VFS层上。在sock_alloc函数中,首先是从sock_mnt->mnt_sb即socket的super block中申请一个节点。然后通过SOCKET_I宏,取得inode对应的socket的地址。
这里之所以这样做的,原因是因为,inode的分配是统一的,但是每个inode的用途是不一样的。
在socket中,申请的inode的节点,实际上对应的结构应该为下面这个结构。

struct socket_alloc {
    struct socket socket;
    struct inode vfs_inode;
};

new_inode中实际上是申请了一个struct socket_alloc的地址,但是返回的确是socket_all->vs_inode的地址。所以在这里需要使用SOCKET_I宏,来从inode地址中,得到socket的地址。

后面的宏kmemcheck_annotate_bitfield是一个内存检查,略过。然后,设置inode的mode,uid,gid。
percpu_add(sockets_in_use, 1)是增加sockets_in_use的统计计数——这个计数变量是每个cpu的私有变量。

然后,我们就回到__sock_create。

    rcu_read_lock();
    pf = rcu_dereference(net_families[family]);
    err = -EAFNOSUPPORT;
    if (!pf)
        goto out_release;

    /*
     * We will call the ->create function, that possibly is in a loadable
     * module, so we have to bump that loadable module refcnt first.
     */

    if (!try_module_get(pf->owner))
        goto out_release;

    /* Now protected by module ref count */
    rcu_read_unlock();

通过RCU机制,获得pf(family)对应的net_families中的指针。

下面通过调用函数指针调用用户指定family的函数去创建socket。

    err = pf->create(net, sock, protocol, kern);
    if (err 0)
        goto out_module_put;

对于TCP/IP来说,family是PF_INET。

现在又需要跳到文件linux/net/ipv4/af_inet.c,下面是PF_INET对应的协议域定义

static const struct net_proto_family inet_family_ops = {
    .family = PF_INET,
    .create = inet_create,
    .owner    = THIS_MODULE,
};

所以对于对于TCP/IP的socket来说,sock_create中这里的create函数实际上指向的是inet_create——这个函数明天再说,继续sock_create的代码。

    /*
     * 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))
        goto out_module_busy;

    /*
     * Now that we're done with the ->create function, the [loadable]
     * module can have its refcnt decremented
     */

    module_put(pf->owner);
    err = security_socket_post_create(sock, family, type, protocol, kern);
    if (err)
        goto out_sock_release;
    *res = sock;

    return 0;

这几行代码的注释已经很清楚,到此sock_create已经完结。

// 网络开发: UDP客户端 #include <stdio.h> #include <stdlib.h> #include <unistd.h> //延时函数 #include <ohos_init.h> //系统初始化函数 #include <cmsis_os2.h> //内核开发函数 #include <string.h> //字符串处理函数 #include "wifi_device.h" #include "lwip/netifapi.h" #include "lwip/api_shell.h" #include "lwip/sockets.h" #include <netdb.h> #include "lwip/sockets.h" #include "wifi_connect.h" // 定义端口 #define _PROT_ 8888 // 在sock_fd 进行监听,在 new_fd 接收新的链接 int sock_fd; int addr_length; static const char *send_data = "Hello! I'm BearPi-HM_Nano UDP Client!\n"; void UDP_Client_Thread(void *argument) { (void)argument; // 服务器地址信息 struct sockaddr_in send_addr; socklen_t addr_length = sizeof(send_addr); char recvBuf[512]; // 连接wifi WifiConnect("HUAWEI P60 Pro", "12345678"); // 创建socket if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { perror("create socket failed!\r\n"); exit(1); } // 初始化预连接的服务端地址 send_addr.sin_family = AF_INET; send_addr.sin_port = htons(_PROT_); send_addr.sin_addr.s_addr = inet_addr("192.168.43.38"); addr_length = sizeof(send_addr); // 总计发送 count 次数据 while (1) { bzero(recvBuf, sizeof(recvBuf)); // 发送数据到服务远端 sendto(sock_fd, send_data, strlen(send_data), 0, (struct sockaddr *)&send_addr, addr_length); // 线程休眠一段时间 sleep(10); // 接收服务端返回的字符串 recvfrom(sock_fd, recvBuf, sizeof(recvBuf), 0, (struct sockaddr *)&send_addr, &addr_length); printf("%s:%d=>%s\n", inet_ntoa(send_addr.sin_addr), ntohs(send_addr.sin_port), recvBuf); } // 关闭这个 socket closesocket(sock_fd); } void UDP_Client_Example(void) { osThreadAttr_t attr; attr.attr_bits = 0U; attr.cb_mem = NULL; attr.cb_size = 0U; attr.stack_mem = NULL; attr.stack_size = 1024 * 5; attr.priority = 25; attr.name = "UDP_Client_Thread"; if (osThreadNew(UDP_Client_Thread, NULL, &attr) == NULL) { printf("Failed to create UDP_Client_Thread!\n"); } } APP_FEATURE_INIT(UDP_Client_Example); 将其改成TCP客户端
最新发布
08-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值