标签:技术分享 mask color lock 利用 放弃 bubuko priority waitpid
挑选一个开源的操作系统,深入源码分析其进程模型,具体包含如下内容:
程序是一个可执行的文件,而进程是一个执行中的程序实例。Linux操作系统上利用分时技术,可同时运行多个进程。利用分时技术,在Linux操作系统上同时可以运行多个进程。分时技术的基本原理是把CPU的运行时间划分成一个个规定长度的时间片,让每个进程在一个时间片内运行。当进程的时间片用完时系统就利用调度程序切换到另一个进程去运行。
(在Linux内核中,进程通常被称作任务,而把运行在用户空间的程序称作进程)
内核程序通过进程表对进程进行管理,每个进程在进程表中占用一项,在Linux系统中,进程表项是一个task_struct任务结构指针,定义在头文件include/linux/sched.h中,其中保存着用于控制和管理进程的所有信息,主要包括进程当前运行的状态信息、信号、进程号、父进程号、运行时间累计值、正在使用的文件和本任务的局部描述符以及任务状态段信息。
struct task_struct { long state; //任务的运行状态(-1不可运行,0 可运行(就绪),>0 已停止)。 long counter ; // 任务运行时间计数(递减) (滴答数),运行时间片。 long priority; // 运行优先数。任务开始运行时counter=priority,越大运行越长。 long signal ; // 信号。是位图,每个比特位代表一种信号,信号值=位偏移值+1。 struct sigaction sigaction[32] ; // 信号执行属性结构,对应信号将要执行的操作和标志信息。 long blocked; // 进程信号屏蔽码(对应信号位图)。 int exi t_code; // 任务停止执行后的退出码,其父进程会来取。 unsigned long start._code; // 代码段地址。 unsigned long end_code ; // 代码长度(字节数)。 unsigned long end_data ; // 代码长度+ 数据长度(字节数)。 unsigned long brk ; // 总长度(字节数)。 unsigned long start_stack ; // 堆栈段地址。 long pid; // 进程标识号(进程号)。 long father; // 父进程号。 long pgrp; // 进程组号。 long session; // 会话号。 long leader ; // 会话首领。 unsigned short uid; // 用户标识号(用户id)。 unsigned short euid; // 有效用户id。 unsigned short suid; // 保存的用户id。 unsigned short gid; // 组标识号(组id)。 unsigned short egid; // 有效组id。 unsigned short sgid; // 保存的组id。 long alarm ; // 报警定时值(滴答数)。 long utime ; // 用户态运行时间(滴答数)。 long stime; // 系统态运行时间(滴答数)。 long cutime; //子进程用户态运行时间。 long cstime; // 子进程系统态运行时间。 long start_t ime ; // 进程开始运行时刻。 unsigned short used_math; // 标志: 是否使用了协处理器。 int tty; // 进程使用tty 终端的子设备号。-1表示没有使用。 unsigned short umask; // 文件创建属性屏蔽位。 struct m_inode * pwd; //当前工作目录i节点结构指针。 struct m _inode * root ; // 根目录i 节点结构指针。 struct m_inode * executable; // 执行文件i 节点结构指针。 unsigned long close_on_exec; // 执行时关闭文件句柄位图标志。 struct desc_struct ldt [3] ; // 局部描述符表。O-空,1-代码段cs,2-数据和堆栈段ds&sso struct tss_struct tss; // 进程的任务状态段信息结构。 }
进程状态即进程在生存期内,可处于一组不同的状态下,(进程状态保存在进程任务结构的state字段)
当一个进程的运行时间片用完,系统就会使用调度程序强制切换到其他进程去执行。如果进程在内核态执行时需要等待系统的某个资源,则该进程会自愿放弃CPU的使用权,让调度程序去执行其他进程,该进程则进入睡眠状态。
只有当进程从“内核运行态”转移到“睡眠状态”时,内核才会进行进程切换操作。在内核态下运行的进程不能被其他进程抢占,且一个进程不能改变另一个进程的状态。
内核中的调度程序用于选择系统中下一个要运行的进程。调度程序可以看作为在所有处于运行状态的进程之间分配CPU运行时间的管理代码。Linux进程是抢占式的,抢占发生在进程处于用户执行状态阶段,在内核执行时是不能被抢占的。
通过调度函数schedule()函数扫描任务数组,通过比较每个就绪态任务的运行时间递减滴答计数counter的值来确定当前哪个进程运行的时间最少。哪一个值大,就表示运行时间还不长,于是就选中该进程,并使用任务切换宏函数切换到该进程运行
每当选择出一个新的可运行进程时,schedule()函数就会调用定义在include/asm/system.h 中的switch_to0宏执行实际进程切换操作。该宏会把CPU 的当前进程状态(上下文) 替换成新进程的状态。在进行切换之前,switch to()首先检查要切换到的进程是否就是当前进程,如果是则什么也不做, 直接退出。否则就首先把内核全局变量current 置为新任务的指针,然后长跳转到新任务的任务状态段TSS组成的地址处,造成CPU 执行任务切换操作。此时CPU 会把其所有寄存器的状态保存到当前任务寄存器TR 中TSS 段选择符所指向的当前进程任务数据结构的tss 结构中,然后把新任务状态段选择符所指向的新任务数据结构中tss 结构中的寄存器信息恢复到CPU 中, 系统就正式开始运行新切换的任务了。
当一个进程结束了运行或在半途中终止了运行,那么内核就需要释放该进程所占用的系统资源。这包括进程运行时打开的文件、申请的内存等。
当一个用户程序调用exit()系统调用时,就会执行内核函数do exit()。该函数会首先释放进程代码段和数据段占用的内存页面,关闭进程打开着的所有文件,对进程使用的当前工作目录、根目录和运行程序的i节点进行同步操作。如果进程有子进程,则让init 进程作为其所有子进程的父进程。如果进程是- 个会话头进程并且有控制终端,则释放控制终端,并向属于该会话的所有进程发送挂断信号SIGHUP,这通常会终止该会话中的所有进程。然后把进程状态置为僵死状态TASK ZOMBIE。并向其原父进程发送SIGCHLD 信号,通知其某个子进程已经终止。最后do_ exit()调用调度函数去执行其他进程。由此可见在进程被终止时,它的任务数据结构仍然保留着。因为其父进程还需要使用其中的信息。
在子进程在执行期间,父进程通常使用wait()或waitpid()函数等待其某个子进程终止。当等待的子进程被终止并处于僵死状态时,父进程就会把子进程运行所使用的时间累加到自己进程中。最终释放已终止子进程任务数据结构所占用的内存页面,并置空子进程在任务数组中占用的指针项。
第一次接触window系统之外的系统,感觉信息量很大,要学的有很多,一开始没有头绪,但是抓住关键字‘进程’,并围绕进程状态,可以一步一步发现Linux操作系统的工作方式,因为对此系统了解不多,也没有接触别的操作系统源码,没有对比,希望在接下来的学习中,会对这门课有更多深入的了解。
参考资料
linux内核之旅 http://mp.sohu.com/profile?xpt=c29odW1wam00MXNoQHNvaHUuY29t&_f=index_pagemp_1
Linux内核完全剖析-基于0.12内核 (赵炯) pdf文档 下载:http://vdisk.weibo.com/s/dn1igR_OGfuWY
标签:技术分享 mask color lock 利用 放弃 bubuko priority waitpid
原文地址:https://www.cnblogs.com/lqqy/p/8977725.html