进程是什么
进程是一个正在执行中程序的实例,是担当分配系统资源(CPU时间、内存等)的实体。
如何描述一个进程?
PCB(process control block)进程控制块,可以理解这个东西就是一个结构体(struct task_struct),是进程属性的集合。
进程 = 对应的源程序 + 属性(数据结构的集合)
struct task_struct{
1、标识信息,用来标识进程的唯一性,以区别其他进程(pid、ppid)。
2、状态:任务状态、退出码、退出信号等。
3、优先级:进程与进程之间有优先级概念。
4、程序计数器:用来记录程序中即将被执行的下一条指令的地址,本质上是记录一个进程运行到哪一步,将位置记录在寄存器中。
5、上下文数据:当进程切换时,需要将寄存器内部的相关数据保存起来,目的是为了下一个时间片到来时恢复数据。
6、I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
7、其他信息
}
查看进程的指令:ps
ps aux查看正在运行的所有进程信息。
ps axj查看父子进程之间的关系。
写一个test程序(死循环),用ps aux查看一下该程序运行后的进程信息,后面的grep是用来在诸多进程中过滤出test进程。
可以看到存在很多信息,例如进程的pid、所属者、运行的时间、状态等等。
进程更详细的信息在根目录下的proc目录下
这里面有大量的进程id命名的文件
例如我们刚刚测试的test程序运行的进程的pid为39343
在proc/39343有更多详细的信息
在所有进程中有一个pid为1的进程,该进程一般来说是操作系统。
getpid与getppid函数
getpid函数是获取当前进程的id,getppid是查看父进程id
写一段测试的代码:
运行结果:
如果再次运行一次
会发现子进程id发生了变化,父进程id一直没变 。这是为什么呢?
这个父进程其实是一个bash进程,是一直在运行的,bash也叫做命令行解释器,通过创建子进程,让子进程去完成对应的任务(其实是进程程序替换)而子进程每次结束后资源回收,当重新运行起来操作系统会重新分配资源,进程id也是一种资源。
同样的用ps aux查看bash进程
所以说bash虽然也是一个进程,但是不能结束bash进程,不然无法执行其他进程。
进程的创建
fork函数
fork函数用来创建一个子进程。
返回值:
fork有两个返回值:
一个是给子进程返回0,一个是给父进程返回子进程的pid。
为什么会有两个返回值?
首先fork是一个函数
pid_t fork(void)
{
//创建子进程的逻辑
//给子进程创建task_struct
…………
//创建子进程结束
//此时已经将子进程进入到cpu的调度队列
return id;//所以这里的return id父进程会执行一次,子进程也会执行一次
}
子进程知道父进程的pid,但是父进程有可能有多个子进程,所以需要给父进程返回子进程的pid(父进程返回子进程的pid)
fork每创建一个新进程,系统需要多一组管理进程的数据结构 + 该进程对应的代码和数据
进程的创建在内核区,由操作系统管理,进程对应的代码和数据存放在其他区域(堆、栈等)。
父进程创建子进程的时候,代码是共享的,数据是各自私有的(进程具有独立性)
进程状态
static const char *task_state_array[] = {
"R (running)", /* 0 */运行状态
"S (sleeping)", /* 1 */休眠状态(浅度)
"D (disk sleep)", /* 2 */磁盘休眠状态(深度)
"T (stopped)", /* 4 */停止状态
"t (tracing stop)", /* 8 */
"Z (zombie)", /* 16 */僵尸状态
"X (dead)" /* 32 */死亡状态
};
R运行状态:运行中或者在运行队列里;
S睡眠状态:进程等待事件完成(等待某个条件形成或接受到信号);
D磁盘休眠状态:不可中断的睡眠状态,在这个状态的进程通常会等待IO的结束。
T停止状态:可以通过发送 SIGSTOP 信号给进程来停止进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
Z僵尸状态:进程退出资源还未被回收,等待父进程回收资源。
X死亡状态:返回状态,不会在任务列表里看到这个状态。
用test测试,每1s打印一次。
显示为S+状态,原因cpu运行很快,而输出IO的速度很慢,速度不匹配,cpu此时处于等待状态,等待事件的完成;+表示还在后台进程中,需要被调度。
我们可以用kill命令来修改进程的状态
kill -19停止进程
此时该进程的状态为T停止状态,将进程放在了后台。
可以用jobs查看后台进程
kill -18将停止的进程重启
也可以用fg +序号,即fg 1将后台进程唤醒。
kill -9 直接杀掉进程
创建一个进程
僵尸进程
僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态(没有对资源回收),子进程进入Z状态。
还是用上面的代码
用kill -9将子进程终止
可以发现子进程进入了僵尸状态。
因为父进程没有进行等待wait,这个放在后面讲,涉及到进程等待。
父进程如果不管不顾,则子进程会变成僵尸进程,也就是说子进程的PCB在退出后由于没有回收,系统还在维护这个PCB,进而造成内存泄漏。
孤儿进程
孤儿进程和僵尸进程相反,如果父进程先挂掉,子进程还在,此时该进程会被操作系统领养(1号进程),不然当子进程退出时,因为没有父进程进行等待,回收资源,会造成内存泄漏。
进程优先级
ps -al
这里面有几个参数
PRI:进程可被执行的优先级,值越小越早被执行
NI:进程的nice值
nice值为优先级的修正数值,即PRI(new)=PRI(old)(最原始的优先级)+nice
nice的值的范围为-20到19,40个等级。
进程的优先级的调整,就是调整nice值,所以改变优先级不是直接修改PRI的值,而是修改ncie值。
所有进程的优先级默认是80,nice值默认为0。
所以PRI的范围就是60到99。
进程的特性
竞争性:进程之间相互竞争cpu资源。
独立性:多进程运行,需要独享各种资源,运行期间互不干扰
并行:多个进程在多个cpu下分别同时进行运行
并发:多个进程在一个cpu下采用进程切换的方式,在一段时间之内,多个进程都可以推进