前言
进程(process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
一、什么是进程
简单来说: 进程=可执行程序+该进程对应的内核数据结构
内核观点:担当分配系统资源(CPU时间,内存)的实体。
二、进程描述(PCB)
PCB(process control block)即进程控制块,进程信息就是被进程控制块的数据结构中,可以理解为进程属性的集合。在Linux中描述进程的结构体叫做task_struct,task_struct是PCB的一种,同时它也是是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息
2.1. tack_struct 内容分类
标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。 I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。 其他信息
组织进程: 可以在内核源代码找到他,所有运行在系统里的进程都以task_struct的链表形式存在内核里。
2.2.对进程的理解
1.查看进程:
进程的信息可以通过/proc 系统文件夹查看
2.3.通过系统调用创建fork()查看进程
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t id = fork(); //子进程返回0,父进程返回 >0 。
if(id == 0)
{
while(1)
{
printf("我是子进程,我的pid是:%d,我的父进程是:%d\n", getpid(), getppid());
sleep(1);
}
}
else
{
while(1)
{
printf("我是父进程,我的pid是:%d,我的父进程是:%d\n", getpid(), getppid());
sleep(1);
}
}
return 0;
}
该示例代码中,使用了fork()函数了之后,下面通过pid的返回值来判断是父进程还是子进程在被调用,或者是系统调用失败(pid < 0 时)。其父子进程的运行顺序并不是一定的,这个系统的调度策略有关系。
使用同一个id却产生了两种不同的结果,其中的原因在我们对进程地址空间了解之后自然就会明白。系统调用fork函数之后,父子进程会共享代码,一般都会执行后续代码,但是对于数据会各自开辟空间,各自一份,互不影响。
三.进程的状态
系统进程状态查看 ps aux/ ps axj命令
Linux 系统的几种状态
R:运行状态(running):并不意味着一定在运行中,表明要不再运行中,要不再运行队列里面。
S:睡眠状态:(sleeping):意味着进程在等待事件完成(这里的睡眠可以被打断)
D:磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态
T:停止状态:(stopped):可以通过发送 SIGSTOP信号来进行中断,也可以继续发送信号来运行。
X:死亡状态:这个状态只是一个返回状态,你不会在列表中看到他。
Z:僵尸状态:这个状态是子进程先于父进程退出,子进程在退出时会告知父进程,但是父进程未回收子进程的资源,导致子进程的PCB没有释放导致内存泄漏。
3.1.僵尸状态
僵尸状态是一种比较特殊的状态,当进程退出并且父进程没有读取到子进程退出的返回值就会成为僵尸进程。
僵死进程会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态码,所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入z状态
下面展示一段僵尸进程的代码
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
void text()
{
pid_t id=fork();//创建子进程
if(id==0)
{
//子进程
int cnt=5;
while (cnt--)
{
printf("我是子进程, 还剩下%d",cnt);
sleep(1);
}
printf("我是子进程, 我成为僵尸进程");
exit(0);
}
else
{
while (1)
{
printf("我是父进程\n");
sleep(1);
}
}
}
int main()
{
text();
}
僵尸进程的危害:
进程的退出状态必须维持下去,因为他要告诉父进程,你教给我的认为我办的怎摸样了,可如果父进程一直不读取,那么子进程就一直处于z状态。
维护退出状态本身就是要数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,z状态一直不退出PCB要一直维护
当一个父进程创建了很多的子进程,就是不回收,就会造成内存资源浪费,因为数据结构本身就要占用内存。
3.2.孤儿进程
在操作系统领域中,孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程,这写孤儿进程将被init(一号进程)操作系统收养。
void test_orphan_process()
{
pid_t id = fork();
if(id == 0)
{
while(1)
{
printf("我是子进程\n");
sleep(1);
}
}
else
{
int cnt = :wq5;
while(cnt--)
{
printf("我是父进程,我还剩%d秒\n", cnt);
sleep(1);
}
sleep(3);
exit(0);
}
}