内核源码:linux-2.6.38.8.tar.bz2
网络子系统设备处理层的初始化大致会完成以下各种任务:
(1)、在/proc/net目录下创建四个proc条目(分别为dev、softnet_stat、ptype和wireless)
/* linux-2.6.38.8/net/core/dev.c */ static int __init dev_proc_init(void) { return register_pernet_subsys(&dev_proc_ops); } static struct pernet_operations __net_initdata dev_proc_ops = { .init = dev_proc_net_init, .exit = dev_proc_net_exit, }; static int __net_init dev_proc_net_init(struct net *net) { int rc = -ENOMEM; if (!proc_net_fops_create(net, "dev", S_IRUGO, &dev_seq_fops)) goto out; if (!proc_net_fops_create(net, "softnet_stat", S_IRUGO, &softnet_seq_fops)) goto out_dev; if (!proc_net_fops_create(net, "ptype", S_IRUGO, &ptype_seq_fops)) goto out_softnet; if (wext_proc_init(net)) goto out_ptype; rc = 0; out: return rc; out_ptype: proc_net_remove(net, "ptype"); out_softnet: proc_net_remove(net, "softnet_stat"); out_dev: proc_net_remove(net, "dev"); goto out; }
其中两个主要函数的定义如下:
/* linux-2.6.38.8/fs/proc/proc_net.c */ struct proc_dir_entry *proc_net_fops_create(struct net *net, const char *name, mode_t mode, const struct file_operations *fops) { return proc_create(name, mode, net->proc_net, fops); } void proc_net_remove(struct net *net, const char *name) { remove_proc_entry(name, net->proc_net); }
net->proc_net的值在proc文件系统初始化函数proc_root_init所调用的proc_net_init函数中被初始化为字符串”/proc/net”,也就是说这两个函数主要用于在/proc/net目录下创建和删除proc条目。
(2)、创建sysfs文件系统的net类
/* linux-2.6.38.8/net/core/net-sysfs.c */ int netdev_kobject_init(void) { kobj_ns_type_register(&net_ns_type_operations); register_pernet_subsys(&kobj_net_ops); return class_register(&net_class); }
即会创建/sys/class/net目录,在此目录下,每个已注册的网络设备都会有一个子目录。
(3)、接收数据包类型的链表的初始化
/* linux-2.6.38.8/net/core/dev.c */ INIT_LIST_HEAD(&ptype_all); for (i = 0; i < PTYPE_HASH_SIZE; i++) INIT_LIST_HEAD(&ptype_base[i]);
其中变量ptype_all和ptype_base以及宏PTYPE_HASH_SIZE的定义如下:
#define PTYPE_HASH_SIZE (16) static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; static struct list_head ptype_all __read_mostly;
以下图片来自《Understanding Linux Network Internals》:
(4)、网络设备全局列表以及两个哈希表的初始化
/* linux-2.6.38.8/net/core/dev.c */ if (register_pernet_subsys(&netdev_net_ops)) goto out; static struct pernet_operations __net_initdata netdev_net_ops = { .init = netdev_init, .exit = netdev_exit, }; static int __net_init netdev_init(struct net *net) { INIT_LIST_HEAD(&net->dev_base_head); net->dev_name_head = netdev_create_hash(); if (net->dev_name_head == NULL) goto err_name; net->dev_index_head = netdev_create_hash(); if (net->dev_index_head == NULL) goto err_idx; return 0; err_idx: kfree(net->dev_name_head); err_name: return -ENOMEM; }
根据设备名称和设备索引号搜寻网络设备的哈希表如下图( 图片来自《Understanding Linux NetworkInternals》):
(5)、初始化数据包接收队列
/* linux-2.6.38.8/net/core/dev.c */ for_each_possible_cpu(i) { struct softnet_data *sd = &per_cpu(softnet_data, i); memset(sd, 0, sizeof(*sd)); skb_queue_head_init(&sd->input_pkt_queue); skb_queue_head_init(&sd->process_queue); sd->completion_queue = NULL; INIT_LIST_HEAD(&sd->poll_list); sd->output_queue = NULL; sd->output_queue_tailp = &sd->output_queue; #ifdef CONFIG_RPS sd->csd.func = rps_trigger_softirq; sd->csd.info = sd; sd->csd.flags = 0; sd->cpu = i; #endif sd->backlog.poll = process_backlog; sd->backlog.weight = weight_p; sd->backlog.gro_list = NULL; sd->backlog.gro_count = 0; }
对于多CPU的系统来说,每个CPU都有一个各自的接收队列,并且使用各自的softnet_data结构体变量*sd来管理网络数据包的收发流量(通过per_cpu函数)。
初始化两个sk_buff_head结构体变量process_queue和input_pkt_queue。
(6)、注册网络命令空间设备,确保loopback设备在所有网络设备中最先出现和最后消失
/* linux-2.6.38.8/net/core/dev.c */ if (register_pernet_device(&loopback_net_ops)) goto out; if (register_pernet_device(&default_device_ops)) goto out;
(7)、分别注册网络设备数据包接收和发送的软中断处理程序
/* linux-2.6.38.8/net/core/dev.c */ open_softirq(NET_TX_SOFTIRQ, net_tx_action); open_softirq(NET_RX_SOFTIRQ, net_rx_action);
(8)、注册回调处理函数dev_cpu_callback
/* linux-2.6.38.8/net/core/dev.c */ hotcpu_notifier(dev_cpu_callback, 0);
(9)、协议无关目的缓存(Protocol independent destination cache)相关函数的初始化
/* linux-2.6.38.8/net/core/dst.c */ void __init dst_init(void) { register_netdevice_notifier(&dst_dev_notifier); }
(10)、在/proc/net目录下创建proc条目dev_mcast
/* linux-2.6.38.8/net/core/dev_addr_lists.c */ static int __net_init dev_mc_net_init(struct net *net) { if (!proc_net_fops_create(net, "dev_mcast", 0, &dev_mc_seq_fops)) return -ENOMEM; return 0; } static void __net_exit dev_mc_net_exit(struct net *net) { proc_net_remove(net, "dev_mcast"); } static struct pernet_operations __net_initdata dev_mc_net_ops = { .init = dev_mc_net_init, .exit = dev_mc_net_exit, }; void __init dev_mcast_init(void) { register_pernet_subsys(&dev_mc_net_ops); }