网络启动初始化函数入口为net/socket.c:2200:socket_init()


  1. static int __init sock_init(void
  2.         /*    
  3.          *      Initialize sock SLAB cache. 
  4.          */ 
  5.         sk_init(); 
  6.         /*    
  7.          *      Initialize skbuff SLAB cache 
  8.          */ 
  9.         skb_init(); 
  10.         /*    
  11.          *      Initialize the protocols module. 
  12.          */ 
  13.         init_inodecache(); 
  14.         register_filesystem(&sock_fs_type); 
  15.         sock_mnt = kern_mount(&sock_fs_type); 
  16.         /* The real protocol initialization is performed in later initcalls. 
  17.          */ 
  18. #ifdef CONFIG_NETFILTER 
  19.         netfilter_init(); 
  20. #endif 
  21.         return 0; 


sock_init()函数结构很简单,实现的功能也非常简单,下面逐个进行解析:

1.sk_init()


  1. void __init sk_init(void
  2.         if (num_physpages <= 4096) { 
  3.                 sysctl_wmem_max = 32767; 
  4.                 sysctl_rmem_max = 32767; 
  5.                 sysctl_wmem_default = 32767; 
  6.                 sysctl_rmem_default = 32767; 
  7.         } else if (num_physpages >= 131072) { 
  8.                 sysctl_wmem_max = 131071; 
  9.                 sysctl_rmem_max = 131071; 
  10.         }     


该函数初始化了两个极值: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,另外在函数的下方还有这样一个声明:


  1. 2231 core_initcall(sock_init);       /* early initcall */   

这另我们想起了module_init(),实际上他们的实现类似,他们的作用都是将初始化函数的地址放到系统的某一块内存中,在启动的时候逐个调用,那么到底是谁调用的呢?这就要使用强大的调试工具gdb来给我们解释了,在此处设置断点,执行bt可以发现

 

  1. #0  sock_init () at /usr/src/linux-2.6.29.i586/net/socket.c:2206 
  2. #1  0xc10013bb in do_one_initcall (fn=0xc1b3c442 <sock_init>) at /usr/src/linux-2.6.29.i586/init/main.c:722 
  3. #2  0xc1b01b0c in do_initcalls () at /usr/src/linux-2.6.29.i586/init/main.c:762 
  4. #3  0xc1b01b3c in do_basic_setup () at /usr/src/linux-2.6.29.i586/init/main.c:782 
  5. #4  0xc1b01bd9 in kernel_init (unused=0x0) at /usr/src/linux-2.6.29.i586/init/main.c:874 
  6. #5  0xc1005e4b in kernel_thread_helper () at /usr/src/linux-2.6.29.i586/arch/x86/kernel/entry_32.S:846 
  7. #6  0x00000000 in ?? () 

将来研究系统启动过程启动的时候可以跟踪一下。