《Linux操作系统课程指导:Ch3 进程管理》由会员分享,可在线阅读,更多相关《Linux操作系统课程指导:Ch3 进程管理(45页珍藏版)》请在金锄头文库上搜索。
1、进程管理进程管理Outline进程进程描述符及任务结构进程程创建建线程在程在Linux中的中的实现进程程终止止小结2024/8/2Liang Shi:Linux Operating Systems2上节课内容回顾上节课内容回顾操作系统概念介绍Linux操作系统版本介绍Linux操作系统特有特征SourceInsight源码阅读工具介绍Linux进程描述符Linux进程状态2024/8/2Liang Shi:Linux Operating Systems8进程标识进程标识使用进程描述符地址进程和进程描述符之间有非常严格的一一对应关系,使得用32位进程描述符地址标识进程非常方便使用PID (Pro
2、cess ID,PID)每个进程的PID都存放在进程描述符的pid域中2024/8/2Liang Shi:Linux Operating Systems92024/8/2Liang Shi:Linux Operating Systems进程的进程的PID进程的pid字段Pid最大值,参见最大值,参见kernel/pid.c顺序使用顺序使用&循环使用循环使用include/linux/types.hinclude/asm-XXX/posix_typesYYY.hinclude/linux/threads.h102024/8/2Liang Shi:Linux Operating Systems用户
3、如何获得一个进程的用户如何获得一个进程的pid系统调用getpid关于进程组使用组链表所有进程共享组内第一个进程的pid数据:tgid单独一个进程可以看成只有一个进程的组getpid返回组组pid112024/8/2Liang Shi:Linux Operating Systems进程和进程的进程和进程的内核内核堆栈堆栈Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:Thread_info进程的内核堆栈进程处于内核态时使用,不同于用户态堆栈内核控制路径内核控制路径所用的堆栈很少,因此对栈和Thread_info来说,8KB足够了 Thread_info 12
4、2024/8/2Liang Shi:Linux Operating SystemsThread_unionC语言允许用如下的一个union结构来方便的表示这样的一个混合体thread_info由体系结构相关部分定义由体系结构相关部分定义阅读阅读include/asm-x86/thread_info.h 以及以及include/asm-x86/thread_info_32.hinclude/linux/sched.h132024/8/2Liang Shi:Linux Operating Systems进程描述符的分配/回收/访问Thread_info的分配/回收/访问alloc_thread_i
5、nfofree_thread_info142024/8/2Liang Shi:Linux Operating Systems152024/8/2Liang Shi:Linux Operating Systemscurrent_thread_info从刚才看到的thread_info和内核态堆栈之间的配对,内核可以很容易的从esp寄存器的值获得当前在CPU上运行的进程的描述符指针因为这个内存区是8KB=213大小,内核必须做的就是让esp有13位的有效位,以获得进程描述符的基地址8191=8192-1=0x2000-1=0x1fff取反:取反:0xffffe000(最后(最后13位为位为0)16
6、2024/8/2Liang Shi:Linux Operating SystemsCurrent宏宏172024/8/2Liang Shi:Linux Operating SystemsCurrent宏的使用宏的使用Current宏可以看成当前进程的进程描述符指针,在内核中直接使用举例:比如current-pid返回在CPU上正在执行的进程的PID182024/8/2Liang Shi:Linux Operating Systems进程的创建进程的创建许多进程可以并发的运行同一程序,这些进程共享内存中程序正文的单一副本,但每个进程有自己的单独的数据和堆栈区一个进程可以在任何时刻可以执行新的程序
7、,并且在它的生命周期中可以运行几个程序又如,只要用户输入一条命令,shell进程就创建一个新进程202024/8/2Liang Shi:Linux Operating Systems传统的UNIX操作系统采用统一的方式来创建进程子进程复制父进程所拥有的资源缺点:创建过程慢、效率低事实上,子进程复制的很多资源是不会使用到的现代UNIX内核通过引入三种不同的机制来解决这个问题212024/8/2Liang Shi:Linux Operating Systems1、写时复制技术,Copy-On-Writing,COW写时复制技术允许父子进程能读相同的物理页。只要两者有一个进程试图写一个物理页,内核就
8、把这个页的内容拷贝到一个新的物理页,并把这个新的物理页分配给正在写的进程222024/8/2Liang Shi:Linux Operating Systems2、轻量级进程允许父子进程共享许多数据结构页表打开的文件列表信号处理3、vfork使用vfork创建的新进程能够共享父进程的内存地址空间。父进程在这个过程中被阻塞,直到子进程退出或者执行一个新的程序232024/8/2Liang Shi:Linux Operating SystemsLinux的进程创建的进程创建Linux提供了几个系统调用来创建和终止进程,以及执行新程序Fork,vfork和clone系统调用创建新进程其中,clone创
9、建轻量级进程,必须指定要共享的资源exec系统调用执行一个新程序exit系统调用终止进程(进程也可以因收到信号而终止)242024/8/2Liang Shi:Linux Operating Systemsforkfork系统调用创建一个新进程调用fork的进程称为父进程新进程是子进程子进程几乎就是父进程的完全复制。它的地址空间是父进程的复制,一开始也是运行同一程序。fork系统调用为父子进程返回不同的值252024/8/2Liang Shi:Linux Operating Systemsexec很多情况下,子进程从fork返回后很多会调用exec来开始执行新的程序这种情况下,子进程根本不需要读
10、或者修改父进程拥有的所有资源。所以fork中地址空间的复制依赖于Copy On Write技术,降低fork的开销262024/8/2Liang Shi:Linux Operating Systems使用使用fork和和exec的例子的例子If (result = fork() = 0)/* 子进程代码 */if (execve(“new_program”,)0)perror(“execve failed”);exit(1);else if (result0)perror(“fork failed”)/* result=子进程的pid,父进程将会从这里继续执行*/272024/8/2Liang
11、 Shi:Linux Operating Systems分开这两个系统调用是有好处的比如服务器可以fork许多进程执行同一个程序有时程序只是简单的exec,执行一个新程序在fork和exec之间,子进程可以有选择的执行一系列操作以确保程序以所希望的状态运行重定向输入输出关闭不需要的打开文件改变UID或是进程组重置信号处理程序若单一的系统调用试图完成所有这些功能将是笨重而低效的现有的fork-exec框架灵活性更强清晰,模块化强282024/8/2Liang Shi:Linux Operating Systemsdo_fork不论是fork,vfork还是clone,在内核中最终都调用了do_f
12、orkarch/x86/kernel/process_32.c292024/8/2Liang Shi:Linux Operating Systems阅读do_fork; copy-process; 了解大致程序流程?子进程从哪里开始执行,它的返回值是什么?阅读copy_thread(arch/arm/kernel/process.c)复制父进程的堆栈?父进程的堆栈中有些什么?Fork系统调用?kernel/fork302024/8/2Liang Shi:Linux Operating Systems子进程的内核态堆栈子进程的内核态堆栈Thread_infoThread_info子进程的子进程的
13、8K unionespesp返回值返回值eaxeax被强制写被强制写0 0用户态堆栈用户态堆栈espesp的值的值用户态下用户态下eipeip的值的值子进程恢复到用户子进程恢复到用户态时需要的上下文态时需要的上下文eipeipespesp子进程的硬件上下文子进程的硬件上下文ret_from_fork低地址低地址高地址高地址312024/8/2Liang Shi:Linux Operating Systems子进程的执行子进程的执行fork后,子进程处于可运行状态,由调度器决定何时把CPU交给这个子进程进程切换后因为eip指向ret_from_fork,所以CPU立刻跳转到ret_from_fo
14、rk()去执行。接着这个函数调用ret_from_sys_call(),此函数用存放在栈中的值装载所有寄存器,并强迫CPU返回用户态回忆进程的切换322024/8/2arch/x86/kernel/entry_32.SLiang Shi:Linux Operating Systems332024/8/2Liang Shi:Linux Operating Systems内核线程内核线程系统把一些重要的任务委托给周期性执行的进程刷新磁盘高速缓存交换出不用的页框维护网络链接等待内核线程与普通进程的差别每个内核线程执行一个单独指定的内核函数只运行在内核态只使用大于PAGE_OFFSET的线性地址空间3
15、42024/8/2Liang Shi:Linux Operating Systems例如,例如,0号进程创建号进程创建1号进程号进程init352024/8/2Liang Shi:Linux Operating Systems线程和进程的比较线程和进程的比较Linux内核中没有线程的概念没有针对所谓线程的调度策略没有数据结构用来表示一个线程一般线程的概念在linux中只是表现为一组共享资源的进程(每个这样的进程都有自己的进程描述符)在其他系统中(比如windows)线程是实实在在的一种运行抽象,提供了比进程更轻更快的调度单元在linux中“线程”仅仅是表示多个进程共享资源的一种说法362024
16、/8/2Liang Shi:Linux Operating Systems创建内核线程创建内核线程Kerenl_thread()创建一个内核线程,并且只能由另一个内核线程来执行这个调用372024/8/2Liang Shi:Linux Operating Systems382024/8/2Liang Shi:Linux Operating Systems进程树进程树进程0进程1392024/8/2Liang Shi:Linux Operating Systems进程进程0所有进程的祖先叫做进程0在系统初始化阶段由start_kernel()函数从无到有手工创建的一个内核线程Init_taskI
17、nit_thread_union进程0最后的初始化工作创建init内核线程,此后运行cpu_idle,成为idle进程402024/8/2Liang Shi:Linux Operating Systems进程进程1又称为init进程由进程0在start_kernel调用rest_init创建init进程PID为1,当调度程序选择到init进程时,init进程开始执行kernel_init ()函数412024/8/2Liang Shi:Linux Operating Systemskernel_init() 为常规内核任务初始化一些必要的内核线程,如:kflushd 刷新脏缓冲区中的内容到磁盘
18、以归还内存kswapd 执行内存回收功能的线程最后kernel_init()函数在init_post中调用execve()系统调用装入可执行程序init。从此,init内核线程变成一个普通的进程。但init进程从不终止,因为它创建和监控操作系统外层的所有进程的活动422024/8/2Liang Shi:Linux Operating Systems432024/8/2Liang Shi:Linux Operating Systems撤销进程撤销进程进程终止进程终止的一般方式是exit()系统调用。这个系统调用可能由编程者明确的在代码中插入另外,在控制流到达主过程C中的main()函数的最后一条语句时,执行exit()系统调用内核可以强迫进程终止当进程接收到一个不能处理或忽视的信号时当在内核态产生一个不可恢复的CPU异常而内核此时正代表该进程在运行442024/8/2Liang Shi:Linux Operating Systems撤销进程撤销进程进程终止使用exit系统调用删除进程在父进程调用wait()类系统调用检查子进程是否合法终止以后,就可以删除这个进程45