初始化的時候,第一個調用的是sock_init,它主要完成:
1. sysctl文件的創建
2. skb高速緩存初始化,它會在slab創建兩個節點skbuff_head_cache和skbuff_fclone_cache
3.註冊並掛socket文件系統
static int __init sock_init(void)
{
int err;
/*
* Initialize the network sysctl infrastructure.
*/
err = net_sysctl_init(); //這裏創建sys文件,用於管理和查看一下網絡參數
if (err)
goto out;
/*
* Initialize skbuff SLAB cache
*/
skb_init(); //skb高速緩存初始化,它會在slab創建兩個節點skbuff_head_cache和skbuff_fclone_cache
/*
* Initialize the protocols module.
*/
init_inodecache();
err = register_filesystem(&sock_fs_type); //註冊sock文件系統
if (err)
goto out_fs;
sock_mnt = kern_mount(&sock_fs_type); //掛載sock文件系統到super_blocks
if (IS_ERR(sock_mnt)) {
err = PTR_ERR(sock_mnt);
goto out_mount;
}
/* The real protocol initialization is performed in later initcalls.
*/
1. skb_init
945 /* Layout of fast clones : [skb1][skb2][fclone_ref] */
946 struct sk_buff_fclones {
947 struct sk_buff skb1;
948
949 struct sk_buff skb2;
950
951 atomic_t fclone_ref;
952 };
void __init skb_init(void)
{
skbuff_head_cache = kmem_cache_create("skbuff_head_cache", //skbuff_head_cache高速緩存,一般情況下,SKB都是從該高速緩存中分配的。
sizeof(struct sk_buff),
0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache", //創建每次以兩倍SKB描述符長度來分配空間的skbuff_fclone_cache高速緩存。如果在分配SKB時
就知道可能被克隆,那麼應該從這個高速緩存中分配空間,因爲在這個高速緩存中分配SKB時,會同時分配一個後備的SKB,以便將來用於克隆,
這樣在克隆時就不用再次分配SKB了,直接使用後備的SKB即可,這樣做的目的主要是提高效率。
sizeof(struct sk_buff_fclones),
0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
}
關於slab緩存的機制,可以查看這篇文章,講的比較通俗從code可以看到
struct sk_buff_fclone
其實是兩個skb的大小加上一個fclone_ref引用標識,兩個高速緩存的區別在於創建時指定的單位內存區域大小不同,skbuff_head_cache的單位內存區域長度是sizeof(struct sk_buff),而skbuff_fclone_cache的單位內存區域長度是2*sizeof(struct sk_buff)+sizeof(atomic_t),即一對SKB和一個引用計數,可以說這一對SKB是"父子"關係,指向同一個數據緩存區,引用計數值爲0,1或2,用來表示這一對SKB中有幾個已被使用。
2. init_inodecache
290 static void init_inodecache(void) 291 { 292 sock_inode_cachep = kmem_cache_create("sock_inode_cache", 293 sizeof(struct socket_alloc), 294 0, 295 (SLAB_HWCACHE_ALIGN | 296 SLAB_RECLAIM_ACCOUNT | 297 SLAB_MEM_SPREAD | SLAB_ACCOUNT), 298 init_once); 299 BUG_ON(sock_inode_cachep == NULL); 300 }在 linux 系統中,socket屬於文件系統的一部分,網絡通信可以被看作對文件的讀取。這種特殊的文件系統叫sockfs。 上一節中提到在sock_init 函數中先調用init_inodecache,爲創建socket 文件系統做好內存準備。不過要注意的是在Linux內核中存在init_inodecache多個定義,但都是靜態型,即只能由該.c文件中的函數調用,在socket.c中, 就定義了這麼一個函數
3.準備socket文件系統
爲文件系統準備 inode 緩存部分了,下面進入初始化文件系統。首先是調用 register_filesystem(&sock_fs_type)把文件系統類型註冊到file_systems鏈表上,
然後調用kern_mount(&sock_fs_type);把該文件系統註冊到super_blocks上。在系統初始化的時候要通過kern_mount安裝此文件系統。
所謂創建一個套接字就是在sockfs文件系統中創建一個特殊文件。super_block裏面有一個字段s_op是用來指向某文件系統內部的支持函數,
這個字段的類型爲super_operation。這個結構定義了12個函數指針,這些指針是要讓VFS來調用的。因此這是 VFS和文件系統之間的一個接口,經由這層接口,
超級塊可以控制文件系統下的文件或目錄。
960 struct vfsmount *
961 vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
962 {
963 struct mount *mnt;
964 struct dentry *root;
965
966 if (!type)
967 return ERR_PTR(-ENODEV);
968
969 mnt = alloc_vfsmnt(name);
970 if (!mnt)
971 return ERR_PTR(-ENOMEM);
972
973 if (flags & MS_KERNMOUNT)
974 mnt->mnt.mnt_flags = MNT_INTERNAL;
975
976 root = mount_fs(type, flags, name, data);
977 if (IS_ERR(root)) {
978 mnt_free_id(mnt);
979 free_vfsmnt(mnt);
980 return ERR_CAST(root);
981 }
982
983 mnt->mnt.mnt_root = root;
984 mnt->mnt.mnt_sb = root->d_sb;
985 mnt->mnt_mountpoint = mnt->mnt.mnt_root;
986 mnt->mnt_parent = mnt;
987 lock_mount_hash();
988 list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
989 unlock_mount_hash();
990 return &mnt->mnt;
991 }
992 EXPORT_SYMBOL_GPL(vfs_kern_mount);