书中 很多 地方 新的内核 除了壳子 都面目全非了, 研究内核就是痛苦终身的事情 。如果你订阅了mail list 那你肯定坚持不了一个月就 unsubscribe了 。
上层 recv send 二十年如一日 的调用 kernel 里面早就翻天覆地了 从 2.6.38的内核看来吧
以前看书从来不写blog , 现在发现写 还是有好处的 , 比如一年前如果我写了cglib jvm 的阅读心得 ,我现在就不会忘记的差不多了 , 现在看别人讨论spring 源码都觉得 陌生的可怕 。。。唉
因为是 review 所以只是一些 个人的 记录 。 不是 全书的读书笔记 。
================================================
Notification chains
通知链: 当一个给定事件发生了 要执行的一系列简单的函数 ,每一个函数让另一个子系统知道有情况发生了, 所以有一个被通知的 和通知者(Observer 模式...)
函数的执行完全靠 被通知方的意愿执行 , 通知方不能干涉 它只能定义一个通知链,
任何内核子系统 都可以把在这个通知链表上 注册自己需要的回调 函数 。
通知链的好处就不多说了 ,自己去看Observer 模式 .....
每一个内核的子系统维护组织 都不需要天天盯着邮件列表看看有没有新的子系统加入 。
他们只需要 :
这个子系统有什么有趣的事情 他想知道的 。
他想知道别的子系统对这个事件感兴趣不。 所以现在事情大家都可以共享 但都可以解放不去管别人了
Notifier.h 里面定了 它 的结构
通知链分 4种 类型
* Atomic notifier chains: Chain callbacks run in interrupt/atomic
* context. Callouts are not allowed to block.
* Blocking notifier chains: Chain callbacks run in process context.
* Callouts are allowed to block.
* Raw notifier chains: There are no restrictions on callbacks,
* registration, or unregistration. All locking and protection
* must be provided by the caller.
* SRCU notifier chains: A variant of blocking notifier chains, with
* the same restrictions.
举例 :
通知产生通过 notifier_call_chain 这个函数 也是在 Notifier.c 中
static int __kprobes notifier_call_chain(struct notifier_block **nl, unsigned long val, void *v, int nr_to_call, int *nr_calls) { int ret = NOTIFY_DONE; struct notifier_block *nb, *next_nb; /*在一个安全的情况下获得rcu 保护的指针 */ nb = rcu_dereference_raw(*nl); while (nb && nr_to_call) { next_nb = rcu_dereference_raw(nb->next); ret = nb->notifier_call(nb, val, v); if (nr_calls) (*nr_calls)++; if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) break; nb = next_nb; nr_to_call--; } return ret; }
System Initializaion
首先 Main.c 中 的
注册和初始化任务 一部分是由内核完成的 ,另一部分是设备驱动里面做的 。
主要分为
硬件初始化: 设备驱动做的, 比如初始化IRQ line 和I/O 端口映射 内存映射 DMA(dam9000)之类 的(request_region release_region等一些请求资源API) 可以去看设备驱动的书
软件初始化: 根据编译内核的config 选项 还有一些别的配置文件决定 协议啊 功能啊什么的
内核与驱动之间的交互 主要是
轮询 中断 (类比SNMP 中的 Get/Trap)
算了 驱动的东西不写了, 看datasheet 和CS8900的驱动吧 写的话下去远了
不想看懂驱动源码的 记得 文件系统永远是你的天堂 驱动对外的接口
/sys/module/网卡驱动名/parameters
/sys/class/net/eth0
static int __init net_dev_init(void) { int i, rc = -ENOMEM; ./*proc 文件系统和 kobject 设备模型的初始化 */ if (dev_proc_init()) goto out; if (netdev_kobject_init()) goto out; INIT_LIST_HEAD(&ptype_all); /*初始化支持的协议数组*/ for (i = 0; i < PTYPE_HASH_SIZE; i++) INIT_LIST_HEAD(&ptype_base[i]); /*注册网络子系统 ,当网络命名空间被创建时就会去回调 传入结构的init方法 etc*/ if (register_pernet_subsys(&netdev_net_ops)) goto out; /*初始化所有cpu上面的包接收队列*/ for_each_possible_cpu(i) { struct softnet_data *sd = &per_cpu(softnet_data, i); /*2.6.38里面这个结构变的十分强大*/ 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; /*对于支持RPS的配置 ,将会被多个CPU来使用 对于.38内核RPS(XPS)的实现以后说 <sys fs 依然给了你配置的能力> /sys/class/net/<device>/queues/tx-<n>/xps_cpus /sys/class/net/<device>/queues/rx-<n>/rps_cpus*/ #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; } dev_boot_phase = 0; /*注册一个 loopback <包括proc fs> 和default 设备*/ if (register_pernet_device(&loopback_net_ops)) goto out; if (register_pernet_device(&default_device_ops)) goto out; /*使能TX RX软中断*/ open_softirq(NET_TX_SOFTIRQ, net_tx_action); open_softirq(NET_RX_SOFTIRQ, net_rx_action); hotcpu_notifier(dev_cpu_callback, 0); /*初始化DST 子系统 通知链 */ dst_init(); /*用于 IP multicast的每个网络设备值 proc/net/dev_mcast*/ dev_mcast_init(); rc = 0; out: return rc; }
static int __init net_dev_init(void) 用了gcc 的__section(.init.text) 在boot 的时候还只有一个线程什么同步都不需要
下面是精简的主要代码 (from 2.6.38)
有时候 内核也需要执行 PATH=/sbin:/usr/sbin:/bin:/usr/bin 下面的命令...
也有类似 execpl 的 函数 call_usermodehelper 当然也有wrapper 过的
request_module kobject_hotplug
(/sbin/modprobe and /sbin/hotplug)