Linux 内核设计与实现(第二版)1-3章(读书)

第1章. Linux内核简介

1.1 Dennis Ritchie和Ken Thompson

UNIX特点:简洁;所有东西都被当做文件看待;内核和相关工具软件由C语言编写(移植能力);提供了一套非常简单但又很稳定的进程间通信元语。 Linux系统的基础:内核;C库;编译器;工具集;系统的基本工具。

1.2 操作系统和内核简介操作系统:一组控制和管理计算机硬件和软件资源,合理地对各类作业进行调度,以及方便用户使用的程序的集合。内核:通常由负责响应中断的中断服务程序,负责管理多个进程从而分享处理器时间的调度程序,负责管理进程地址空间的内存管理程序和网络、进程间通信等系统服务程序共同组成。应用程序通过系统调用陷入内核是应用程序完成其工作的基本行为方式。

1.3 单内核与微内核单内核:把内核整体上作为一个单独的大过程来实现,并同时运行在一个单独的地址空间。微内核:内核功能被划分为独立的过程,每个过程叫做一个服务器。 Linux模块化设计;抢占式内核;支持内核线程;动态装载内核。

 

第2章. 从内核出发(略)

第3章. 进程管理

进程定义:是进程实体的运行过程,是系统进程资源分配和调度的一个独立单位。

现代OS的两种虚拟机制:虚拟处理器和虚拟内存。

3.1 进程描述符及任务结构内核把进程存放在叫做任务队列的双向循环链表中。其中,项为task_struct,称为进程描述符的结构包含一个进程的所有信息,定义在中。

分配:通过slab分配器分配task_struct结构,能达到对象利用和缓存着色。只需在栈底(对向下增长)和栈顶(上)创建一个新的结构struct thread_info,每个任务的struct thread_info结构在它的内核栈的尾端分配,结构中task域存放的是指向该任务实际task_struct的指针(由于寄存器并不富余,否则可由硬件存放当前进程的task_struct结构指针)。

存放:PID来标示进程,表示为pid_t隐含类型(int)。系统管理员可通过修改/proc/sys/kernel/pid/pid_max来提高上限。

进程状态:TASK_RUNNING(运行)正在执行,或在运行队列中等待执行;TASK_INTERRUPTIBLE(可中断)正在睡眠,等待条件发生进而执行; TASK_UNINTERRUPTIBLE(不可中断)除不会因为接收到信号而被唤醒外与上同; TASK_ZOMBIE(僵死)该进程已结束,但是其父进程还没调用wait4()。其进程描述符仍被保留,以便其父进程能获得它的消息;TASK_STOPPED(停止)进程停止运行,没有也不能投入运行。

设置当前进程状态:set_task_state(task,state);

进程上下文:当一个程序执行了系统调用或触发异常时,陷入内核空间,称内核代表进程执行并处于进程上下文中。

进程家族树:init进程的进程描述符是是作为 init_task静态分配的。每个task_struct都包含一个指向其父进程的parent指针,还包含一个称为children的子进程链表。

父进程Struct task_struct *myparent=current->parent; 子进程 Struct task_struct *task;

     struct list_head *list;

     list_for_each(list, &corrent->children){

                  task=list_entry(list, struct task_struct, sibling);//task指向当前的某个子进程

     }

3.2进程创建

OS产生(spawn)进程的机制:

(1) 许多其它的OS:首先在新的地址空间创建进程,读入可执行文件,最后开始执行。

(2) UNIX:分成两个单独的过程fork()和exec()。首先,fork()通过拷贝当前进程创建一个子进程,唯一区别在于PID、PPID和某些资源和统计量。Exec()函数负责读取可执行文件并将其载入地址空间开始运行。

写时拷贝: fork()时,推迟甚至免除拷贝数据的技术。内核此时并不复制父进程整个地址空间,而是让它们以只读方式共享同一个拷贝,资源的复制只有在需要的时候才进行。这使得地址空间上页的拷贝推迟到实际发生写入的时候。实际开销就是复制父进程的页表以及给予子进程创建惟一的进程描述符。一般进程创建后都会马上运行一个可执行的文件,这种优化可以避免拷贝大量根本就不会使用的数据。

fork(): Linux通过clone()系统调用实现fork(),通过一系列的参数来指明父子进程需共享的资源。fork()、vfork()、_clone()库函数都根据需要的参数标志去调用clone(),然后由clone()去调用do_fork() do_fork()完成了创建的大部分工作,定义在kernel/fork.c中.该函数调用copy_press()函数,然后让进程开始运行。

do_fork():过程如下

(1)调用dup_task_struct(),创建内核栈,thread_info结构和task_strcut,此时父子进程描述符是完全相同的。 (2)检查新创建这个子进程后,当前用户所拥有的进程数目没有超出限制。

(3)更改子进程描述符内成员的值,主要是统计信息,以区别父进程。

(4)子进程状态被设置为TASK_UNINTERRUPTIBLE以保证它不会投入运行。

(5)copy_process()调用copy_flags()以更新task_struct的flags成员。表明进程是否拥有超级用户权限的      PF_SUPERPRIV标志被清0,表明进程还没有调用exec()函数的PF_FORKNOEXEC标志被设置。

(6)调用get_pid()为新进程获取一个有效的PID。

(7)根据传递给clone()的参数标志,copy_process()拷贝或共享打开的文件、文件系统信息、信号处理函数、进程地址空间和命名空间等。

(8)让父进程和子进程平分剩余的时间片。

(9)copy_process()作扫尾工作队并返回一个指向子进程的指针。再回到do_fork(),如果copy_process()成功返回,内核有意选择子进程首先执行,因为子进程一般都会马上调用exec()函数。 vfork()系统调用和fork()功能相同,除了不拷贝父进程的页表项。子进程作为父进程的一个单独的线程在它的地址空间里运行,父进程被阻塞,直到子进程退出或执行exec()。子进程不能向地址空间写入。

3.3 线程

线程在Linux中的实现线程机制提供了在同一程序内部共享内在地址空间运行的一组线程,支持并发程序设计技术(concurrent programming),在多处理器系统上,它也能保证真正的并行处理(parallelism)。

Linux实现线程机制非常独特:从内核角度说,它并没有线程的概念,它把所有的线程都当作进程来实现,仅仅将其视为一个与其他进程共享某些资源的进程。每个线程拥有惟一隶属于自己的task_struct,所以内核中,它看起来就像是一个普通的进程。(由于Linux本身进程就很轻了)线程的创建和普通进程的创建类似,只不过在调用clone()的时候要传递一些参数标志来指明需要共享的资源。

内核线程:没有独立的地址空间,(mm指针置为NULL),只在内核空间运行。并只有内核线程才能创建,如:int kernel_thread(int (*fn)(void), void *arg, unsigned long flags)

3.4  进程结束

进程终结一般说来,进程的析构发生在它调用 exit()之后,大部分工作要通过do_exit(),可在kernel/exit.c中找到。

删除进程描述符:在调用了do_exit()之后 ,尽管线程已经僵死不能再运行了,但是系统还是保留了它的进程描述符。因此,进程终结时所需的清理工作和进程描述符的删除分开执行。在父进程获得已终结的子进程的信息后,或者通知内核它并不关注那些信息后,子进程的task_struct结构才释放。 wait()这一族函数都是系统调用wait4()实现的。最终释放结构描述符时,release_task() 会被调用。

孤独进程:如果父进程在儿子进程之前退出,必须有机制来保证子进程能找到一个新的父亲。否则的话这些成为孤儿的进程就会在退出时永远处于僵死状态,白白的耗费内在。解决方法是给子进程在当前线程中找一个线程作为父亲,如果不行,就让init做它们的父进程。

在do_exit()中会调用notify_parent(),该函数会通过forget_parent()来执行寻你过程:

       struct task_struct *p,*reaper = father:

       struct list_head *list;

       if(father->exit_signal!=-1)  reaper = prev_thread(reaper);

       else reaper = child_reaper;

       if(reaper == father)  reaper = child_reaper;

这段代码将reaper设置为该进程所在的线程组内的其它进程,如果线程组内没有其他的进程,它就将reaper设置为child_reaper,也就是init进程。

现在,合适的父进程也找到了,只需遍历所有子进程并为它们设置新的父进程:

       list_for_each(list, &father->children){

               p=list_entry(list, struct task_struct, sibling);

               reparent_thread(p, reaper, child_reaper);

       }

        list_for_each(list, &father->ptrace_children){

               p=list_entry(list, struct task_struct, ptrace_list);

               reparent_thread(p, reaper, child_reaper);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值