kernel笔记——内核编译与进程管理
内核与操作系统
由于一些商业操作系统设计上的缺陷以及日益庞杂,“操作系统”的概念对很多人而言变得含糊不清。在进一步讨论Linux内核的话题前,我们先区分“内核”与“操作系统”这两个概念。
- 操作系统:指在整个系统中完成最基本功能和系统管理的部分,包括内核、设备驱动、文件管理工具、系统管理工具、shell命令行或其他用户界面(gnome/KDE等)
- 内核:是操作系统的核心,完成进程管理、cpu调度、内存管理、中断处理等功能
一般我们编写的应用程序,跑在操作系统上,完成文字编辑、音乐播放、网页游览等特定功能。
内核编译
内核源码一般放在/usr/src目录下,我们也可以从这里获取所需内核版本的源码包。编译内核的第一步是配置内核功能,例如配置是否支持对称多处理器(SMP),可通过设置CONFIG_SMP的值。
通常我们使用"make menuconfig"命令进行配置,其提供了友好的配置界面:
保存配置后,源码目录下将生成.config配置文件,打开该文件,可以看到其内容为各种选项设置:
- CONFIG_X86_64=y
- CONFIG_64BIT=y
- CONFIG_X86=y
- CONFIG_SEMAPHORE_SLEEPERS=y
- CONFIG_MMU=y
- ……
我们也可以使用当前的内核配置,使用以下命令快速地生成.config文件:
- zcat /proc/config.gz > .config
之后根据.config配置,对源码进行编译:
- make -j4
以上使用-j选项,指定并行编译工作任务数目,在多核环境下,减少了编译时间。
编译完成后生成内核压缩镜像:
- make bzImage
生成的内核压缩镜像文件位于 arch/x86/boot目录下:
- linux-2.6.32.59 # ll arch/x86/boot/bzImage
- -rw-r--r-- 1 root root 2814112 07-02 22:27 arch/x86/boot/bzImage
接着安装内核模块:
- make modules_install
新的模块会被放置在/lib/modules目录下:
- /lib/modules # ll
- 总计 8
- drwxr-xr-x 4 root root 4096 03-08 23:53 2.6.32.12-0.7-default
- drwxr-xr-x 3 root root 4096 07-02 23:31 2.6.32.59-0.7-default
最后执行make install安装内核,在/boot目录下将生成System.map、vmlinuz和initrd文件:
- linux-2.6.32.59 # make install
- sh /home/lx/kernel/linux-2.6.32.59/arch/x86/boot/install.sh 2.6.32.59-0.7-default arch/x86/boot/bzImage \
- System.map "/boot"
- Kernel image: /boot/vmlinuz-2.6.32.59-0.7-default
- Initrd image: /boot/initrd-2.6.32.59-0.7-default
- ……
完成安装后,在/boot/grub/menu.lst文件中增加了新内核相应的启动项,我们可以修改该文件,指定系统启动后使用新编译的内核。
进程与线程
Linux下,进程与线程的最大不同是进程拥有独立的内存地址空间,而线程与其他线程共享内存地址空间。除此之外,进程与线程的实现基本相同,都有task_struct结构,都被分配PID。
内核线程没有独立的地址空间,它们完成特定工作并接受内核的调度,不同于一般用户进程,它们不接收kill命令发送的信号:
- F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
- 1 S root 2 1 0 -40 - - 0 migrat Jul01 ? 00:00:00 [migration/0]
- 1 S root 3 1 0 94 19 - 0 ksofti Jul01 ? 00:00:00 [ksoftirqd/0]
- 5 S root 18 1 0 70 -5 - 0 worker Jul01 ? 00:00:00 [events/0]
- ……
task_struct
task_struct结构包含进程使用的虚拟内存、打开的文件、进程状态、进程pid等信息,占用的内存由slab分配,在文件中定义。thread_info结构的第一个字段为task_struct类型的指针,当进程创建时,thread_info存放在进程内核栈的顶部:
current全局变量指向当前运行进程的task_struct结构,由于thread_info存放的位置固定,这样我们通过以下汇编指令就能很容易地计算出current的值:
- movl $-8192, %eax
- andl $esp, %eax
进程状态
进程可处于以下几种状态:
- RUNNING
- INTERRUPTABLE
- UNINTERRUPTABLE
- STOP
- ZOMBIE
这些进程状态作为宏,在sched.h文件中被定义。
- RUNNING状态表示进程是可执行的,或正在执行或在运行队列中,这些进程占用或等待cpu资源。
- 进程调用退出函数exit后,进程中止,进入ZOMBIE状态。在ZOMBIE状态下进程不会占用cpu,但因其task_struct结构尚未释放,仍占用一点内存,直到父进程调用wait函数接受子进程遗愿,假如父进程先于子进程退出,则由init进程接受子进程遗愿。如果一个进程长期处于ZOMBIE状态,则是父进程中未调用wait,为程序编码问题。
- UNINTERRUPTABLE状态表示进程不可中断,处于此状态的进程处于内核态,并且不接收任何信号。
设置进程状态的函数为set_task_state函数,在文件中定义。
进程间关系
进程间关系与目录结构一样,为树状结构,目录结构以/为根,而进程关系以init为根。我们可以使用pstree查看进程间关系:
- linux-14:~ # echo $$
- linux-14:~ # pstree -G -p 10939
- bash(10939)─┬─pstree(12806)
- └─sh(12796)───sleep(12801)
内核代码中提供了一条双向闭环链表,自init进程始,链表连接了所有进程的task_struct结构,可以通过for_each_process宏遍历系统的所有进程:
- #define for_each_process(p) \
- for (p = &init_task ; (p = next_task(p)) != &init_task ; )
进程创建
Linux kernel将进程创建的步骤分成两步:fork和exec。fork生成子进程的pid,将父进程执行上下文、打开的文件描述符等内容复制一份给子进程;exec将子进程自己的执行上下文加载进内存地址空间。有以下fork例子,问执行该程序将输出多少个1?
- #include <stdio.h>
- #include <unistd.h>
- int main()
- {
- int i;
- for(i=0 ; i < 10; i++)
- {
- fork();
- }
- printf("%d\n", 1);
- return 0;
- }
fork拷贝父进程的内容到子进程,开销较大,假若调用fork之后马上调用exec,子进程加载自己的执行文件,则拷贝的动作就是多余的。写时拷贝(copy-on-write,COW)解决了拷贝带来无谓开销的问题,在子进程写父进程地址空间时,才触发拷贝的动作。不做多余事情、非到不得已的时候才完成工作,这也是Linux kernel高效的原因之一。
内核中do_fork函数完成fork调用的工作,do_fork调用copy_process。copy_process函数中主要完成以下工作:
- 调用dup_task_struct函数申请新进程的task_struct、thread_info结构
- 根据clone_flags标志,调用copy_files、copy_fs、copy_mm等函数完成文件、文件系统、内存等信息的拷贝
- 调用alloc_pid申请新进程pid
fork返回两次,在do_fork函数中实现。
进程中止
最终进程会调用exit函数中止,exit系统调用最终会调用内核中do_exit函数,do_exit在中定义,其完成以下工作:
- 调用exit_signals设置进程flags标志为PF_EXITING
- 调用exit_mm、exit_files、exit_fs等函数释放进程内存、文件、文件系统等结构
- 设置进程的exit_code
- 调用exit_notify,向当前进程的父进程、子进程发送信号,告知当前进程将要中止,并设置当前进程退出状态exit_state为EXIT_DEAD或EXIT_ZOMBIE
- 调用schedule,切换到另一个进程,从do_exit函数不会返回到调用它的函数
Reference: Chapter 1 to chapter 3, Linux kernel development.3rd.Edition
kernel笔记——内核编译与进程管理的更多相关文章
- 《Linux内核设计与实现》读书笔记 第三章 进程管理
第三章进程管理 进程是Unix操作系统抽象概念中最基本的一种.我们拥有操作系统就是为了运行用户程序,因此,进程管理就是所有操作系统的心脏所在. 3.1进程 概念: 进程:处于执行期的程序.但不仅局限于 ...
- Linux内核学习笔记(1)-- 进程管理概述
一.进程与线程 进程是处于执行期的程序,但是并不仅仅局限于一段可执行程序代码.通常,进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个 ...
- Linux学习笔记(5)-进程管理
进程简介 进程是正在执行的一个程序或命令,每一个进程都有自己的地址空间,并占有一定的系统资源.感性的认识,进程就是一个正在运行的程序 进程管理的作用 判断服务器的运行状态 查看系统中有哪些进程 杀死进 ...
- kernel笔记——内核同步与锁
内核同步 内核同步解决并发带来的问题,多个线程对同一数据进行修改,数据会出现不一致的情况,同步用于保护共享数据等资源. 有两种形式的并发: 同时进行式并发,在不同cpu上执行的进程同时访问共享数据 二 ...
- UNIX环境编程学习笔记(21)——进程管理之获取进程终止状态的 wait 和 waitpid 函数
lienhua342014-10-12 当一个进程正常或者异常终止时,内核就向其父进程发送 SIGCHLD信号.父进程可以选择忽略该信号,或者提供一个该信号发生时即被调用的函数(信号处理程序).对于这 ...
- UNIX环境编程学习笔记(19)——进程管理之fork 函数的深入学习
lienhua342014-10-07 在“进程控制三部曲”中,我们学习到了 fork 是三部曲的第一部,用于创建一个新进程.但是关于 fork 的更深入的一些的东西我们还没有涉及到,例如,fork ...
- UNIX环境编程学习笔记(18)——进程管理之进程控制三部曲
lienhua342014-10-05 1 进程控制三部曲概述 UNIX 系统提供了 fork.exec.exit 和 wait 等基本的进程控制原语.通过这些进程控制原语,我们即可完成对进程创建.执 ...
- UNIX环境编程学习笔记(17)——进程管理之进程的几个基本概念
lienhua342014-10-05 1 main 函数是如何被调用的? 在编译 C 程序时,C 编译器调用链接器在生成的目标可执行程序文件中,设置一个特殊的启动例程为程序的起始地址.当内核执行 C ...
- UNIX环境编程学习笔记(15)——进程管理之进程终止
lienhua342014-10-02 1 进程的终止方式 进程的终止方式有 8 种,其中 5 种为正常终止,它们是 1. 从 main 返回. 2. 调用 exit. 3. 调用_exit 或_Ex ...
随机推荐
- pymysql.err.InternalError: (1205, 'Lock wait timeout exceeded; try restarting transaction')错误处理
问题描述: 在使用pymysql库时,利用游标执行插入操作,产生错误,会出现pymysql.err.InternalError: (1205, 'Lock wait timeout exceeded; ...
- innodb是如何巧妙实现事务隔离级别
之前的文章mysql锁机制详解中我们详细讲解了innodb的锁机制,锁机制是用来保证在并发情况下数据的准确性,而要保证数据准确通常需要事务的支持,而mysql存储引擎innodb是通过锁机制来巧妙地实 ...
- 【java多线程】多线程的创建三种方式--笔记
申明:线程的概念以及进程的相关概念,可以参考网络上其他资料,这里只讨论多线程是怎么实现. 一.多线程的简单理解 明白什么是多线程,小生通俗一点的理解为:在一个程序里,我想同时让这个程序完成多个任务. ...
- pwnable.tw unexploitable 分析
这题是和pwnable.kr差不多的一道题,主要区别在于没有给syscall.所以需要自己去找. 只有read和sleep两个函数. 思路一是首先劫持堆栈到bss段,然后调用read函数将sleep的 ...
- Jenkins结合.net平台综合之权限修正和文件排除
笔者在发布项目的时候遇到这样一个问题,第一次发布的时候成功发布,然后再次发布失败.但是这个问题很快就排除了,这里提出来是为了帮助遇到这个问题的小伙伴,以顺利避开坑.之所以会这样是因为我们在设置权限的时 ...
- KVO原理解析
KVO在我们项目开发中,经常被用到,但很少会被人关注,但如果面试一些大公司,针对KVO的面试题可能如下: 知道KVO嘛,底层是怎么实现的? 如何动态的生成一个类? 今天我们围绕上面几个问题,我们先看K ...
- MVC_分页方法调用
/// <summary> /// 分页页脚 /// </summary> /// <param name="currentPageIndex"> ...
- es简单打造站内搜索
最近挺忙的,在外出差,又同时干两个项目.白天一个晚上一个,特别是白天做的项目,马上就要上线了,在客户这里 三天两头开会,问题很多真的很想好好静下来怼代码,半夜做梦都能fix bugs~ 和客户交流真的 ...
- 第六讲 smart qq C#开发总结
smart qqC#开发总结: 整个开发下来其实一点都不是很难,从一开始二维码 获取到最终的收发消息,基本上都是模拟浏览器的操作.都是基于http通讯.一下就是 本次新手学习http协议的最关键的一个 ...
- 搞懂 JavaScript 继承原理
在理解继承之前,需要知道 js 的三个东西: 什么是 JS 原型链 this 的值到底是什么 JS 的 new 到底是干什么的 1. 什么是 JS 原型链? 我们知道 JS 有对象,比如 var ob ...