内核源码: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);
}