好了,闲话少说。上次看到了sys_socket调用sock_create的地方了。下面开始研究sock_create了。
sys_socket将自己的参数family, type, protocol传给sock_create,而sock为sock_create的输出值。

retval = sock_create(family, type, protocol, &sock);

下面看sock_create的代码

int sock_create(int family, int type, int protocol, struct socket **res)
{
    return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}

sock_create不过是一个包装函数,它通过__sock_create真正的去创建socket。

其中,current是当前task的指针——实际上current是一个宏,这个宏在linux内核中已经存在很久了。task的类型为struct task_struct。nsproxy是它的一个成员变量,是task的命名空间指针,下面是它的定义:

struct nsproxy {
    atomic_t count;
    struct uts_namespace *uts_ns;
    struct ipc_namespace *ipc_ns;
    struct mnt_namespace *mnt_ns;
    struct pid_namespace *pid_ns;
    struct net      *net_ns;
};

与咱们有关的就是最后一个指针,net_ns。命名空间namespace是linux在2.6中引入的特性,是为了虚拟化而做的。具体定义可以参考 http://oldwiki.linux-vserver.org/Namespaces

uts_ns: UTS命名空间包含了运行内核的名称、版本、底层体系机构类型等信息。UTS是UNIX Timesharing System的缩写;
ipc_ns:保存所有与进程通信(IPC)有关的信息
mnt_ns:保存已mount的文件系统信息;
pid_ns:有关进程ID的信息;
net_ns:这是与咱们有关的,包含所有网络相关的命名空间。
这是第一个参数。
最后一个参数,则表明这个创建的socket是否为kernel层创建的。对于我们来说,这个socket是为应用层创建的,所有最后一个参数是0.
下面开始分析__sock_create

static int __sock_create(struct net *net, int family, int type, int protocol,
             struct socket **res, int kern)
{
    int err;
    struct socket *sock;
    const struct net_proto_family *pf;

/*
     * Check protocol is in range
     */
    if (family < 0 || family >= NPROTO)
        return -EAFNOSUPPORT;
    if (type < 0 || type >= SOCK_MAX)
        return -EINVAL;

/*
     * Skip some codes
     */

}

首先这个函数,先对family和type进行检查,查看是否超出正常范围。

/* 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 inode *alloc_inode(struct super_block *sb)
{
struct inode *inode;
return inode;
}

struct inode *new_inode_pseudo(struct super_block *sb)
{
struct inode *inode = alloc_inode(sb);
return inode;
}

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

inode = new_inode_pseudo(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 socket流程(3)(4)——sock_create相关推荐

  1. 内核中的UDP socket流程(2)——API “sys_socket”

    内核中的UDP socket流程(2)--API "sys_socket" 作者:gfree.wind@gmail.com 原文:http://blog.chinaunix.net ...

  2. 内核中的UDP socket流程(1)

    内核中的UDP socket流程(1)  相对于TCP,UDP协议要简单的多.所以我决定由简入繁,先从UDP协议入手. 前一遍文章已经确定了struct sk_buff被用于socket的接受和发送缓 ...

  3. 内核中的UDP socket流程(7)——udp_sendmsg

    sock_sendmsg的代码很简单 int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct k ...

  4. 内核中的UDP socket流程(5)——inet_create

    进入函数inet_create static int inet_create(struct net *net, struct socket *sock, int protocol,           ...

  5. 内核中的UDP socket流程(6)——sendto

    现在开始新的API sendto,那么就重新回到了socket.c文件. SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, l ...

  6. 内核中的UDP socket流程(11)——ip_append_data

    作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net 继续ip_append_data, if (copy > length) cop ...

  7. 一文讲解Linux内核中根文件系统挂载流程

    根文件系统的概念 根文件系统是控制权从linux内核转移到用户空间的一个桥梁.linux内核就类似于一个黑匣子,只向用户提供各种功能的接口,但是功能的具体实现不可见,用户程序通过对这些功能接口的不同整 ...

  8. linux程序获取透传参数,Linux内核中TCP SACK处理流程分析

    frankzfz2014-07-27 17:32 demo121:frankzfz您好: 我想请教一个问题,就是将写好的GenericApp项目(没有配置工具),我加入zigbee协议栈的配置工具后还 ...

  9. Linux内核网络协议栈8—socket监听

    几个问题  了解以下几个问题的同学可以直接忽略下文: 1.listen 库函数主要做了什么?  2. 什么是最大并发连接请求数?  3.什么是等待连接队列? socket 监听相对还是比较简单的,先看 ...

最新文章

  1. java课堂疑问解答与思考1
  2. 取消项目git_git取消文件跟踪
  3. OpenSSL状态机中可选消息的处理
  4. Redis 6.0 新特性 ACL 介绍
  5. dfmea文件_技术干货合集「失效分析、PFMEA DFMEA关系、文件结果化」
  6. 一种定力夹具控制系统
  7. 【MOOC-生物信息学-蛋白质结构预测与分析】(占坑)
  8. vsftpd配置详解
  9. WEB版的即时聊天工具
  10. 优化方法总结(梯度下降法、牛顿法、拟牛顿法等)
  11. ISO文件与镜像文件
  12. 插头DP~(。。。了解了一下下)
  13. html 打开资源管理器,资源管理器怎么打开 教你如何快速打开资源管理器
  14. 使用stp制造广播风暴!
  15. zlib——Usage Example翻译
  16. Android百度地图SDK:隐藏比例尺,隐藏百度LOGO,隐藏缩放控件
  17. Kotlin快速学习之路(完整版)
  18. LSTM做预测遇到的错误总结
  19. Windows系统怎样配置PHP环境
  20. 安排软件保护服务在 2022-07-26T23:00:43Z 时重新启动成功。原因: RulesEngine

热门文章

  1. linux根据进程的运行路径,停止进程
  2. 数据分箱1——人工手动分箱
  3. python计算二维平面的曲线的曲率
  4. Ubuntu 打开 initramfs
  5. 九 web爬虫讲解2—urllib库爬虫—实战爬取搜狗微信公众号—抓包软件安装Fiddler4讲解...
  6. 又一家药企IPO被拒,原因竟然是……
  7. HDU 1222 Wolf and Rabbit
  8. xcode 插件安装路径
  9. BZOJ 1086: [SCOI2005]王室联邦( )
  10. 云原生和ServiceMesh主要组件--理解K8s/Istio/Envoy