TCP/IP协议栈初始化(三) OS大人inet socket已经准备完毕

看完了tcp_prot的内容,回到inet_init函数中。

1362 BUILD_BUG_ON()是内核的一个宏。如果条件为真强制生成一个编译错误,编译会终止。这里判断的内容是structinet_skb_parmdummy_skb->cb大小。这里有一个细节,sizeofC语言里,指针和数组的首地址有什么区别呢?sizeof可以告诉我们,如果传递给sizeof的是一个指针,那么sizeof只会输出这个指针的宽度,比如32位操作系统里就是4个字节。如果传递给它的是一个数组的首地址,那么就是得到这个数组的长度。感兴趣的可以验证下。

这里的意思是,如果条件为真就终止编译。因为这样编译出来的inet无法控制。我大概猜测下,因为内核会为inet创建一个内核socket实现与inet通信,实现对socket的整体控制。这些控制的参数是通过skb_buff结构来传递,而面向用户层的参数是放在structinet_skb_parm里面,从名称可以大概推断。所以如果一个skb_buff->cb放不下structinet_skb_parm的内容,那么就不好玩了。

另外还记得之前说过这个skb_buff可能什么也不做了吗?看到被只是作为被比较的对象。哈哈。

1364 proto_register(&tcp_prot,1)tcp_prot的内容已经在前面说过了。proto_register这个函数需要详细说下,从名称看就是为了注册TCP协议。看到这里传递进去的是tcp_prot的地址,一下想到C语言里的形参和实参的问题了。既然是传递实参,说明proto_register一定是要被修改。各种猜测可能出现,肯定是需要把tcp_prot挂到某个列表上,还有分配资源。下面去这个函数里看下。

int proto_register(struct proto *prot, int alloc_slab)
1857 {
1858 char *request_sock_slab_name = NULL;
1859 char *timewait_sock_slab_name;
1864 if (alloc_slab) {
1865 prot->slab = kmem_cache_create(prot->name,prot->obj_size, 0,
1866 SLAB_HWCACHE_ALIGN, NULL);
1868 if (prot->slab == NULL) {
1869 printk(KERN_CRIT "%s: Can't create sock SLABcache!\n",
1870 prot->name);
1871 goto out_free_inuse;
1872 }
1874 if (prot->rsk_prot != NULL) {
1875 static const char mask[] = "request_sock_%s";
1877 request_sock_slab_name = kmalloc(strlen(prot->name) +sizeof(mask) - 1, GFP_KERNEL);
1878 if (request_sock_slab_name == NULL)
1879 goto out_free_sock_slab;
1881 sprintf(request_sock_slab_name, mask, prot->name);
1882 prot->rsk_prot->slab =kmem_cache_create(request_sock_slab_name,
1883 prot->rsk_prot->obj_size, 0,
1884 SLAB_HWCACHE_ALIGN, NULL);
1886 if (prot->rsk_prot->slab == NULL) {
1887 printk(KERN_CRIT "%s: Can't create request sock SLABcache!\n",
1888 prot->name);
1889 goto out_free_request_sock_slab_name;
1890 }
1891 }
1933 }


这个函数的重点是在1864-1872行之间。为prot申请了内核缓存,用slab系统分配的,把得到的地址赋给prot->slab。这也就是前面说的,TCP协议的内存池,以后所有的TCP连接数据(tcp_sock)都会从这里分配。这里我们假定一定会分配成功,刚开机就没内存了,还玩什么啊。

从这个函数返回inet_init中。

1368-1374 与之前一样,现在还要注册UDPRAW_SOCK的协议。与TCP同属于控制层协议,现在我们不看这2个。

到目前为止,我们只给协议分配了内存,初始化才开始了一小部分。下面的资源已经有了,可以通知OS大人,我们可以干活了。

1380(void)sock_reister(&inet_family_ops)。前面的void有什么用呢?因为sock_register是有一个返回值int类型的。这里加的void,其实不是进行强制类型转化,而是告诉OS大人,这个返回值我不关心,丢掉吧。

传入的参数inet_family_ops,传入的地址,同样系统要修改这个数据。其成员如下,而这个结构体同样充当的是一个接口人的角色。inet_create是创建inetsocket的入口函数,现在我们不去看它,否则会偏离主线太远,等到遇到它的时候再细说。PF_INET表示是协议族中的INET子族。THIS_MODULE前面已经说过,不再重复。

901 static struct net_proto_family inet_family_ops = {
902 .family = PF_INET,
903 .create = inet_create,
904 .owner = THIS_MODULE,
905 };


下面看下,sock_register函数都对inet_family_ops做了什么。函数定义在net/socket.c中,函数主要执行以下内容:2109-2112,判断协议的编号是不是超出了系统的要求范围,这个很简单,我们的inet协议族不会超出。2115-2122,判断是不是已经注册过了,如果没有注册过,就把它记录到net_families的全局数组中去。net_families里是OS用来记录所有的网络协议。当我们创建特定协议的SOCKET时,系统正是在这里找到对应的协议入口,创建SOCKET.

int sock_register(const struct net_proto_family *ops)
2106 {
2107 int err;
2109 if (ops->family >= NPROTO) {
2110 printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n",ops->family,
2111 NPROTO);
2112 return -ENOBUFS;
2113 }
2115 spin_lock(&net_family_lock);
2116 if (net_families[ops->family])
2117 err = -EEXIST;
2118 else {
2119 net_families[ops->family] = ops;
2120 err = 0;
2121 }
2122 spin_unlock(&net_family_lock);
2124 printk(KERN_INFO "NET: Registered protocol family%d\n", ops->family);
2125 return err;
2126 }


至此我们的INET协议已经初步被记录到OS的名册里了。随时可以响应OS大人的呼唤了。然而,此时底层的协议还没有准备好,我们的协议还不能向下传递数据。不过现在可以总结出各个层级关系了。由于IP层不从属于哪个控制协议,还记得我在各socket之间关系里过IP是大家的。所以现在OS已经可以创建各种socket了。只是能创建归能创建,能不能正常工作,另当别论。把关系捋出来如下图,随着深入了解这个图中的内容会多起来。


代码看到现在,只是明白了inet的入口是放在inet_family_ops中,inet_family_ops是放在net_families数组中的。那么tcp_socket是怎么来的呢,这个作为一个问题先挂(put it on the back burner),以后会说到的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值