标签:roo 调度 org bsp sleep ash apt 实现 file
内核与操作系统
由于一些商业操作系统设计上的缺陷以及日益庞杂,“操作系统”的概念对很多人而言变得含糊不清。在进一步讨论Linux内核的话题前,我们先区分“内核”与“操作系统”这两个概念。
一般我们编写的应用程序,跑在操作系统上,完成文字编辑、音乐播放、网页游览等特定功能。
内核编译
内核源码一般放在/usr/src目录下,我们也可以从这里获取所需内核版本的源码包。编译内核的第一步是配置内核功能,例如配置是否支持对称多处理器(SMP),可通过设置CONFIG_SMP的值。
通常我们使用"make menuconfig"命令进行配置,其提供了友好的配置界面:
保存配置后,源码目录下将生成.config配置文件,打开该文件,可以看到其内容为各种选项设置:
我们也可以使用当前的内核配置,使用以下命令快速地生成.config文件:
之后根据.config配置,对源码进行编译:
以上使用-j选项,指定并行编译工作任务数目,在多核环境下,减少了编译时间。
编译完成后生成内核压缩镜像:
生成的内核压缩镜像文件位于 arch/x86/boot目录下:
接着安装内核模块:
新的模块会被放置在/lib/modules目录下:
最后执行make install安装内核,在/boot目录下将生成System.map、vmlinuz和initrd文件:
完成安装后,在/boot/grub/menu.lst文件中增加了新内核相应的启动项,我们可以修改该文件,指定系统启动后使用新编译的内核。
进程与线程
Linux下,进程与线程的最大不同是进程拥有独立的内存地址空间,而线程与其他线程共享内存地址空间。除此之外,进程与线程的实现基本相同,都有task_struct结构,都被分配PID。
内核线程没有独立的地址空间,它们完成特定工作并接受内核的调度,不同于一般用户进程,它们不接收kill命令发送的信号:
task_struct
task_struct结构包含进程使用的虚拟内存、打开的文件、进程状态、进程pid等信息,占用的内存由slab分配,在文件中定义。thread_info结构的第一个字段为task_struct类型的指针,当进程创建时,thread_info存放在进程内核栈的顶部:
current全局变量指向当前运行进程的task_struct结构,由于thread_info存放的位置固定,这样我们通过以下汇编指令就能很容易地计算出current的值:
进程状态
进程可处于以下几种状态:
这些进程状态作为宏,在sched.h文件中被定义。
设置进程状态的函数为set_task_state函数,在文件中定义。
进程间关系
进程间关系与目录结构一样,为树状结构,目录结构以/为根,而进程关系以init为根。我们可以使用pstree查看进程间关系:
内核代码中提供了一条双向闭环链表,自init进程始,链表连接了所有进程的task_struct结构,可以通过for_each_process宏遍历系统的所有进程:
进程创建
Linux kernel将进程创建的步骤分成两步:fork和exec。fork生成子进程的pid,将父进程执行上下文、打开的文件描述符等内容复制一份给子进程;exec将子进程自己的执行上下文加载进内存地址空间。有以下fork例子,问执行该程序将输出多少个1?
fork拷贝父进程的内容到子进程,开销较大,假若调用fork之后马上调用exec,子进程加载自己的执行文件,则拷贝的动作就是多余的。写时拷贝(copy-on-write,COW)解决了拷贝带来无谓开销的问题,在子进程写父进程地址空间时,才触发拷贝的动作。不做多余事情、非到不得已的时候才完成工作,这也是Linux kernel高效的原因之一。
内核中do_fork函数完成fork调用的工作,do_fork调用copy_process。copy_process函数中主要完成以下工作:
fork返回两次,在do_fork函数中实现。
进程中止
最终进程会调用exit函数中止,exit系统调用最终会调用内核中do_exit函数,do_exit在中定义,其完成以下工作:
Reference: Chapter 1 to chapter 3, Linux kernel development.3rd.Edition
标签:roo 调度 org bsp sleep ash apt 实现 file
原文地址:https://www.cnblogs.com/felixzh/p/9047732.html