网络启动初始化函数入口为net/socket.c:2200:socket_init()
static int __init sock_init(void) { /* * Initialize sock SLAB cache. */ sk_init(); /* * Initialize skbuff SLAB cache */ skb_init(); /* * Initialize the protocols module. */ init_inodecache(); register_filesystem(&sock_fs_type); sock_mnt = kern_mount(&sock_fs_type); /* The real protocol initialization is performed in later initcalls. */ #ifdef CONFIG_NETFILTER netfilter_init(); #endif return 0; }
sock_init()函数结构很简单,实现的功能也非常简单,下面逐个进行解析:
1.sk_init()
void __init sk_init(void) { if (num_physpages <= 4096) { sysctl_wmem_max = 32767; sysctl_rmem_max = 32767; sysctl_wmem_default = 32767; sysctl_rmem_default = 32767; } else if (num_physpages >= 131072) { sysctl_wmem_max = 131071; sysctl_rmem_max = 131071; } }
该函数初始化了两个极值:sysctl_wmem_max和sysctl_rmem_max。这两个值就是来控制我们执行setsockopt系统调用时对SNDBUF和RCVBUF设置的最大值。和该函数被调用时所说的“Initialize sock SLAB cache.”的描述有些不符。
2.skb_init()。该函数只是申请了两个struct sk_buff的slab,sk_buff是linux网络实现中最重要的结构体之一,在网络协议的各层都被使用到,用来存储数据。
3.init_inodecache()。为将来要创建的所有的socket申请一个slab。
4.register_filesystem(),kern_mount()。
注册了一个socket的pseudo文件系统,并进行挂载,该文件系统不能在用户空间mount。那该文件系统的作用何在呢?我们知道在linux系统上一切皆文件,那socket当然也不能例外。既然是文件,就要有inode;既然有inode,就要有super_block;既然有super_block,就要有file_system_type,就要注册文件系统。
在这里注册的是一个伪文件系统[net/socket.c:304:sock_fs_type],这个file_system_type主要提供了一个sockfs_get_sb()函数,获取sock_fs的super_block。
sockfs的super_block主要提供了一个sock_alloc_inode(),这个函数是用来获取sockfs的inode。因为一个socket和一个inode相对应,为了简化实现和提高访问速度,内核里的实现是将socket和inode分配在同一片连续的内存区,通过宏SOCKET_I()[include/net/sock.h:783]和SOCK_INODE()[include/net/sock.h:788]来在这两个结构体间进行切换,通过sock_alloc()[net/socket.c:481]来申请一个socket
至此,socket系统初始化阶段完成。
下面说一些题外话,关于socket初始化时所用到的这两个文件系统的接口。
register_filesystem(),该函数主要维护了系统中的一个单链表:file_systems[fs/filesystems.c:32]。新来的文件系统不停的加到前一个文件系统的->next上。每一个文件系统类型在系统中只注册一次,这是一个文件系统的总入口,该接口主要提供了一个接口:get_sb(),用来在文件系统被mount时获取super_block和vfsmount。
另外,这个这个函数还有一个特点就是没有被显示的调用,那它是在什么时候运行的呢?经观察发现,返回值类型声明后跟有一个__init,另外在函数的下方还有这样一个声明:
2231 core_initcall(sock_init); /* early initcall */
这另我们想起了module_init(),实际上他们的实现类似,他们的作用都是将初始化函数的地址放到系统的某一块内存中,在启动的时候逐个调用,那么到底是谁调用的呢?这就要使用强大的调试工具gdb来给我们解释了,在此处设置断点,执行bt可以发现
#0 sock_init () at /usr/src/linux-2.6.29.i586/net/socket.c:2206 #1 0xc10013bb in do_one_initcall (fn=0xc1b3c442 <sock_init>) at /usr/src/linux-2.6.29.i586/init/main.c:722 #2 0xc1b01b0c in do_initcalls () at /usr/src/linux-2.6.29.i586/init/main.c:762 #3 0xc1b01b3c in do_basic_setup () at /usr/src/linux-2.6.29.i586/init/main.c:782 #4 0xc1b01bd9 in kernel_init (unused=0x0) at /usr/src/linux-2.6.29.i586/init/main.c:874 #5 0xc1005e4b in kernel_thread_helper () at /usr/src/linux-2.6.29.i586/arch/x86/kernel/entry_32.S:846 #6 0x00000000 in ?? ()
将来研究系统启动过程启动的时候可以跟踪一下。