进程概念
进程:程序的一个执行实例,正在执行的程序等。
内核观点:担当分配系统资源(CPU时间,内存)的实体。
程序员观点:进程 = 对应的代码和数据 + 进程对应的PCB结构体
PCB(Process control block)
进程信息被放在一个叫做进程控制块(PCB)的数据结构中,即进程属性的集合。
在OS中,通过对PCB进行操作,从而达到控制程序运行的目的。
linux下的PCB结构体具体内容为:PCB具体内容。
进程组织
进程被组织进程管理,是以双链表的形式将PCB存在内核里。
查看进程
在linux里,查看正在运行的进程并不难。进程的运行状态信息,在根目录下的proc系统文件里。
如下:数字代表每个进程的ID号(PID)
proc里面存着所有进程的运行信息。
下面我们使用一个例子查看自己正在运行的进程:
持续运行该代码。
运行ps命令,可以查看当前运行的进程,PID号。
根据该PID即可查找到该文件,相关信息就存在里面。
其中cwd目录中,存储的就是当前程序运行的路径。
ps 命令
查看当前运行的进程,并显示运行进程的PPID,PID号等等。
用法: ps [选项]
选项:
a:显示所有用户的所有进程(包括其他用户)
j:用任务格式来显示进程
x:显示无控制终端的进程(后台运行)。
u:按用户名和启动时间顺序来显示进程。
显示结果的各个含义:
getpid()/getppid()
包含头文件<unistd.h>和<sys/types.h>
getpid()
获取当前进程的pid
getppid()
获取当前进程的父进程的ppid
kill -9 pid/ppid
使用该命令可以杀死进程。
fork()
创建一个子进程。
使用man fork时出现这个,提示:
缺少相关手册,输入以下命令即可解决。
yum install -y man-pages
为什么fork会有两个返回值?
根据man手册的介绍,创建成功后,会返回两个值。将子进程的PID号返回给父进程;将0返回给子进程。
#include<stdio.h> #include<unistd.h> int main () { pid_t id = fork(); if(id == 0) { //child while(1) { sleep(1); printf("I am a child,my pid:%d, ppid:%d\n",getpid(),getppid()); } } else{ //parent while(1) { sleep(1); printf("I am a parent,my pid:%d, ppid:%d\n",getpid(),getppid()); } } //printf("hello world!\n"); return 0; }
运行结果:
ps命令查询父子进程ID号:
从该程序来看,该函数确实创建了一个子进程,并且if的两种情况都执行了。父进程执行else,子进程执行if。
原因:
原因:
因为子进程被创建后,fork后面的代码是父子进程都看的见的,而在fork函数return id前,子进程就已经被创建好了,父子进程都会执行return id语句因此看起来像一个变量两个值,本质上,是两个不同的进程,执行了同一段代码。
为什么父进程返回子进程的PID,而子进程返回0?
因为父进程可以创建多个子进程,返回子进程的PID方便父进程知道,我创建的孩子是谁,不至于变成野孩子;而子进程它自己知道自己是谁,当然也知道它的父亲是谁,因此子进程我们只需知道它创建成功即可,返回0就行。
关于fork的写时拷贝技术
fork之后,os按道理来说,需要给自己成也拷贝一份数据,供子进程使用。但是如果子进程只读数据的话,那么其实读父进程的也可以,这样就造成数据冗余了!因此写时拷贝技术产生了!
- 代码段父子进程是一模一样的,不需要拷贝一份,父子共享即可。(根据页表映射到同一物理地址)。
- 数据段(起初也通过页表映射到同一物理地址),只有子进程想要改变数据时,此时拷贝一份新的给子进程。
优点:
写时拷贝是一种延时申请技术,可以提高整机内存的使用率。
fork调用失败的原因
fork调用失败会返回-1。
- 系统中有太多进程。
- 实际用户的进程数超过了限制。
linux下的进程状态
R(running): 运行状态,表明PCB要么在运行中,要么在运行队列。
S(sleeping):睡眠状态(阻塞状态),等待非CPU资源就绪,可中断睡眠。
D(Disk sleep):比如IO磁盘的时候的状态,不可中断睡眠。
T(stopped):暂停或调试状态
t(tracing stop):暂停或调试状态
X(dead):终止态,瞬时性非常强(OS释放资源很快)。
Z(zombie):僵尸状态,终止或异常状态,需要被CPU或父进程检查,检查完即可变到X。
可以在linux使用以下脚本命令,监控进程的状态:
while :; do ps axj | head -1 && ps axj | grep ./my_test | grep -v grep; sleep 1; echo "-----------------------------------"; done
Z进程
僵尸进程是一个比较特殊的状态,当进程退出,并且父进程没有读取到子进程的返回代码时,就会产生僵死进程。僵死进程会一直以终止状态保持在进程表中,并且会一直等待父进程读取退出状态码。
僵尸进程危害:
- 进程的退出状态会一直被维持下去,因为它要等待父进程读取。
- 维护退出状态就是维护(PCB),也就是僵尸进程的PCB会一直在内存中。
- 父进程创建很多子进程,就是会不回收,就会造成内存资源浪费。
孤儿进程
和Z进程产生情况相反,如果父进程先退出,子进程就会变成孤儿进程。因为父进程都没有了,没人释放它了,就变成孤儿了。
为了解决这种问题,孤儿进程会被领养,被1号进程领养(init),就是被系统本身领养。以后1号进程会自动回收子进程。
进程优先级
CPU分配资源是有先后顺序的,就是指进程的优先权。优先权高的进程先分配资源,优先权低的后分配资源。
进程能否上CPU执行,是受进程调度器调度的,而优先级是进程调度器的主要参考依据。
查看优先级命令
ps -al
优先级参考值
PRI(priority):代表这个进程被执行的优先级,值越小越早被执行。
NI(nice):代表这个进程的nice值(可以被修改)。
优先级(PRI) = 默认优先级的值(PRI) + nice值(NI)
注意:
nice值不是进程的优先级,但是nice值的改变会影响进程的优先级。
默认的PRI为80。
nice的取值范围是:-20~19,一共40个级别。
修改优先级
使用top命令可以修改进程的优先级。
top -> r -> 输入PID -> 输入nice值。
以上就是进程相关概念,后续还会继续补充。