目录
何为进程?
task_struct 中存储了什么进程信息?
如何查看进程?
如何获取进程pid?
如何创建子进程?
为什么返回值如此呢?
为什么有两个返回值?
进程状态
进程的一般状态
运行态
终止态
阻塞态
挂起态
Linux下进程的状态(+表前台)
进程优先级
既然有优先级,那为什么需要运行队列排队呢?
基本概念
何为进程?
进程概念:正在执行的程序
对操作系统而言,OS要对进程做管理,OS用PCB(process control block)来描述进程,PCB是存储进程内部属性的内核数据结构,在Linux下,OS用struct task_struct结构体来描述进程。
在linux2.6源码中可以查到,sched.h中用task_struct描述进程
那么进程本质是什么呢?
进程本质是可执行程序和对应的内核数据结构。
task_struct 中存储了什么进程信息?
标识符(pid):进程在系统中的唯一标识符
状态:进程状态,退出码,退出信号
优先级:描述进程获取资源的先后顺序
程序计数器:程序将执行的下一条指令的地址
内存指针:包括程序代码和进程相关数据的指针,和其他进程共享的内存的指针
上下文数据:进程运行中CPU中寄存器存储的进程的相关数据
其他:进程的当前路径
如何查看进程?
1.命令:ps axj
比如使用 ps axj |head -1 && ps axj|grep ‘mytest’ |grep -v grep查看系统中的mytest进程
&& :逻辑与,前面命令执行成功了才执行后面的
2.在/proc 内存文件系统中查看当前系统实时的进程信息
当中蓝色数字是进程的pid,即每个进程在系统中的唯一标识符
进入对应进程文件可以看到
exe :进程对应的可执行程序的磁盘文件
cwd :进程当前的工作路径
如何获取进程pid?
1.使用系统调用getpid获取进程pid,使用getppid 获得父进程的pid
注意:几乎命令行上所有指令都是bash进程的子进程
2.使用ps axj命令查看,第二个数字是PID
当我们需要子进程协助父进程工作,或者希望子进程执行与父进程不同的功能的时候,需要创建子进程。
如何创建子进程?
使用fork函数(系统调用):
fork之后父子进程共享执行后续的代码,子进程继承父进程的数据,父子进程代码共享,数据各自独立。
fork调用成功时,返回子进程pid给父进程,返回0给父进程,调用失败时返回-1
为什么返回值如此呢?
父进程必须有标识子进程的方案,fork之后给父进程返回子进程pid
子进程最重要的是知道自己创建成功,因为子进程找父进程的成本很低
不同返回值可以让不同进程执行不同的代码
为什么有两个返回值?
当OS在内核态执行fork函数准备返回的时候,子进程已经被创建,被OS放入运行队列中,又创建子进程之后父子进程共享执行后续的代码,所以有两个返回值,分别返回给父子进程。
运行队列:OS系统中描述进程运行的队列 struct runqueue{struct task struct *head;......},进程运行就是指进程在运行队列中被调度器调度
进程状态
Linux在PCB中用 int status描述。
进程的一般状态
这里描述的是一般概念,各个OS描述的进程状态不尽相同,但可由此延伸。
运行态
进程在运行队列中,准备就绪,随时可以调度,但不代表正在CPU上运行
终止态
进程还在,只不过永远不运行,随时等待被释放
为什么要维护终止态?不立马释放对应的资源?
释放资源需要花时间,当操作系统忙碌的时候,操作系统可能在处理别的任务,故不会立马释放资源。
阻塞态
当进程申请CPU资源暂时无法得到满足,需要运行队列排队,申请其他慢设备同理,当进程需要访问某种非CPU资源时,该资源没有准备好,或者正在为其他进程提供服务,此时,进程要从运行队列移除,将当前进程放入对应设备描述结构体的等待队列中。
当前进程正在等待某种(非CPU)资源,进程需要在该资源的等待队列中排队,进程的代码没有运行,进程所处的状态就是阻塞态。
挂起态
当内存不足的时候,操作系统要帮我们进行辗转腾挪,腾出内存空间来处理任务
如果进程短期内不会被调度,等待的资源短期内不会就绪,代码和数据依旧在内存中,会浪费空间,操作系统把进程的代码和数据置换到磁盘上,所以内存不足时往往伴随着磁盘被高频率访问。(注意:进程的PCB还在内存中)
Linux下进程的状态(+表前台)
R running运行态:同上,表示进程在运行队列中。
S sleeping:浅度睡眠,可中断睡眠,在阻塞等待非磁盘资源。
D disk sleep:与S类似不过等待的资源是磁盘资源,深度睡眠,不可被中断睡眠,os也无权杀死进程。
T stopped:暂停状态,比如给进程发送19号信号。
t tracing stop:暂停状态,与T类似,比如用gdb调试进程遇到断点的时候,程序就是t状态。
Z zombie:僵尸状态,进程退出时一般不会进入X状态,而是进入Z状态,因为进程一般需要将执行结果告知父进程或者OS,当子进程先退出时,父进程不退出也不等待,子进程会进入Z状态,如果没有人回收,该状态会被一直维护,会造成内存泄露。
X dead:死亡状态,表示进程资源可以马上回收。
S/D/T/t状态对应前面的是阻塞态或挂起态,Z和X对应前面的终止态。
孤儿进程:父进程先退出,被bash回收,子进程被1号进程(OS)领养,也即孤儿进程退出时会将退出信息返回给OS。
进程优先级
用于表示进程获取资源的先后顺序,之所以需要进程优先级,是因为系统的资源有限,需要进程以需要的一定顺序获取资源。Linux内核是抢占式内核,会发生进程抢占,也即正在运行的低优先级进程,如果来了个优先级更高的进程,调度器会直接把进程从CPU上剥离,放上优先级更高的进程。
Linnux下的优先级用PRI表示,用ps -al 查看进程信息,可以看到:
PRI越小表示优先级更高
PRI=80+nice值,nice值的取值范围是-20到19,可以使用top命令+r,再输入进程pid和更改后的nice值,来修改nice值,从而改变进程的优先级。
既然有优先级,那为什么需要运行队列排队呢?
原因很简单,因为存在优先级一样的进程,Linux是通过进程优先级的队列来实现优先级的,使用 task_struct *queue[]指针数组实现不同优先级的队列,也即是多条链表去实现优先级,不同的数组下标就表示不同的优先级,通过位图标记不同优先级队列下是否有进程,这样就可以O(1)时间复杂度调度,不仅如此,Linux2.6内核用的是两张哈希表active和old去存储进程运行队列,新入的进程放入old表中,当CPU处理完active表后,交换两张表的底层指针,于是CPU又可以继续处理active表了。
基本概念
竞争性:因为进程数量众多,但CPU资源少量,所以进程之间具有竞争属性,即竞争性。为了高效完成任务,合理竞争相关资源,出现优先级。
独立性:多进程在运行,独享各种资源,运行期间互不干扰。
并行:多进程在多个CPU下分别同时运行。
并发:操作系统给每一个进程,在一次调度周期中赋予一个时间片的概念,每次进程执行一个时间片后进行进程切换,多个进程在一个CPU下采用进程切换的方式,在一段时间内,多个进程得以推进,进程串行运行,但宏观上我们感觉是并行运行,就称为并发。
进程的硬件上下文数据:CPU内的寄存器中保存进程运行时的临时数据,在进程剥离时保存,进程恢复时恢复。