新读核日记
最近搜索Linux内核的资料时,无意间读到sunmoon前辈在2001年的笔记,发布在http://os.silversand.net上,可是不知道是何原因,网站现在已经无法访问了,能搜索到的多为2001到2005年间转载的博客,其间错版错字有不少,恰好这学期正在学习Linux内核的代码,老师也对 linux代码了解颇多,于是把自己听课学习之余的心得写下来,同时也借鉴了于渊前辈的著作《Orange‘s,一个操作系统的实现》,他轻快的文笔让我有持续的探索。
Linux第一行的内核代码最早由Linus torvalds编写,是在学生时代学习的Minux的基础上开发而来,过多的发展历史不再赘述,说Linus是世上最伟大的程序员一点不为过,Linux的开源模式也决定了Linux将一直流行下去。我的老师在上课时常常说起Linus,说起内核中的代码如何精巧。这段历史不在此详细展开,感兴趣的朋友可以去了解一下Linus对开源事业的不断坚持,git技术同样也是Linus所提出。
第一部分:Linux 的内核初始化、启动
当计算机启动时,主板上电,CPU(linux最初构建在i386处理器上,往后所提的CPU除特指均是指i386处理器)在实模式下自检,至于什么是实模式,于渊前辈在书中写的相当有意思,自检完成后,把物理地址0x0FFFFH下的ROM-BIOS移到处理器中,引导BIOS系统自检,初始化中断向量表到物理地址0x0 然后硬件上的引导程序移动到物理地址0x7c00H处,开始执行此处的代码。此时已经准备好加载linux 的内核了,lilo和loadlin 作用就是加载已经准备就绪的内核,具体的有关lilo的原理我不曾了解过,应该可用在lilo的readme里找到。我们来看看 /usr/src/kernels/5.10.0-60-8.0.50.oe2203.x86_64/kernel 下的Makefile文件,我对makefile文件并不算精通,但是可以从只言片语之间窥见kernel是如何启动的。
obj-y = fork.o exec_domain.o panic.o\
cpu.o exit.o softirq.o resource.o\
…
sysctl.o sys.o workqueue.o pid.o task_work.o
…
kthread.o
从这几个可执行文件可用推测,在引导完内核后首先就运行了fork,在fork创建的进程中无限等待用户输入来创建子进程,从而实现交互式操作系统的功能。
==============================================================================
成都今日降温
今天我们来看fork.c
int last_pid;
struct task_struct *pidhash[PIDHASH_SZ];
33行,35行定义了上一个进程的PID(process id),还有一个结构体task_struct这个我们以后又说,其后是添加进程到队列中的两个函数add_wait_queue和add_wait_queue_exclusive,两个函数唯一的区别就在wait->flags=0; wait->flags=WQ_FLAG_EXCLUSIVE. 其中让我好奇的是这个宏定义:WQ_FLAG_EXCLUSIVE,颇费了一番功夫之后,了解到这个宏定义是为了解决惊群现象,何为惊群现象呢,不妨举个例子,我站在鸽子群中想要喂一只特定的鸽子,我拿着谷子走到鸽群中间,所有鸽子都被惊动,而我的目的只有那一只特定的鸽子。在监听同一个socket时,所有进程都会挂在同一个等待队列,当请求到来时,所有的进程都会被唤醒,这将耗费大量的系统资源。在linux2.6版本之后,加入了一个标志位WQ_FLAG_EXCLUSIVE来解决,这个宏定义实现了互斥等待从而避免所有进程均被唤醒。