在给进程下定义之前,我们先了解一下进程:
我们在编写完代码并运行起来时,在我们的磁盘中会形成一个可执行文件,当我们双击这个可执行文件时(程序时),这个程序会加载到内存中,而这个时候我们不能把它叫做程序了,应该叫做进程。所以说,只要把程序(运行起来)加载到内存中,就称之为进程。
进程的概念:程序的一个执行实例,正在执行的程序等
PCB:进程控制块(结构体)
当一个程序加载到内存中,操作系统要为刚刚加载到内存的程序创建一个结构体(PCB),进程信息被放在这个结构体中(PCB),可以理解为PCB是进程的属性的集合。
在Linux操作系统下的PCB是:task_struct
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息
在进程执行时,任意时间内,进程对应的PCB都要以下内容:
优先级的理解:
由于CPU只要一个,进程可以有多个,所以CPU的资源有限,操作系统在调度进程到CPU时会根据进程的优先级来判断。
程序计数器的理解:
CPU跑一个进程时,要执行它的代码,而代码是自上往下执行的(if、else、循环等除外),CPU先要取指令,然后分析指令,再然后执行指令。取完一个指令后,CPU中的寄存器(EIP指令寄存器)会保存当前指令的下一条指令的地址,方便下次取下一个指令。
所谓的函数跳转、分支判断、循环等,都是修改EIP完成的。
上下文数据的理解:
CPU在跑一个进程时,没有跑完就开始切换其他进程,为了下次继续跑完这个进程,会保留这个进程的上下文数据,当这个进程回来时,会把上下文数据移动到CPU内部继续执行。
#include
#include
int main()
{
while(1)
{
printf("i am process...pid:%d,ppid:%d\n",getpid(),getppid());
sleep(1);
}
return 0;
}
通过ps aux | grep 文件名
来找
其中getpid()
是找进程的标示符,getppid()
找父进程的标示符
fork函数可以创建一个子进程
#include
#include
int main()
{
pid_t id=fork();//创建子进程
while(1)
{
if(id==0)
{
printf("i am process...child---pid:%d,ppid:%d\n",getpid(),getppid());
sleep(1);
}
else if(id>0)
{
printf("i am process..father---pid:%d,ppid:%d\n",getpid(),getppid());
sleep(1);
}
else{
;
}
}
return 0;
}
我们可以得出:这段代码有两个进程,并且它们的关系是父子关系。
什么是fork函数:在调用fork函数之前,只有一个进程(父进程),当这个进程调用fork函数之后,fork函数会复制一个进程(子进程),区别是PID不同,它们的关系是父子关系。
fork函数会返回两次值:
——给父进程返回子进程的pid。
——给子进程返回0。
——失败时,在父进程中返回-1,不创建子进程,并且errno被适当地设置。
操作系统存在着五种状态模型:
通过命令 ps aux 可以查看进程的状态
【注意】此可执行状态并非上面的运行态。
进程中的R状态不代表正在运行,代表的可被调度,此运行相当于上面的就绪态。
操作系统会把进程中R状态的进程全放在调度队列中,方便调度。
(1)创建一个可执行态
#include
#include
int main()
{
while(1);
return 0;
}
(2)创建休眠态进程
#include
#include
int main()
{
while(1)
sleep(10);
return 0;
}
S状态是浅度睡眠,随时可以被唤醒,也可以被杀掉。
可以表示为深度睡眠,该进程不会被杀掉,即使你是操作系统,除非我自动唤醒,才可以恢复。
这种状态(D)的进程杀不死。
向进程发送SIGSTOP信号,该进程会响应该信号进入暂停状态,
向该进程发送SIGCONT信号,该进程会从暂停状态恢复到可执行状态。
僵尸状态:一个处于僵尸状态的进程,会等待它的父进程或操作系统对它的信息进行读取,之后才会被释放。
通过代码来模拟僵尸状态的进程:
#include
#include
#include
int main()
{
pid_t id=fork();
int count=5;
while(1)
{
if(id==0)
{
while(count){
printf("i am process..child---.pid:%d,ppid:%d\n,count: %d",getpid(),getppi d(),--count);
sleep(1);
}
printf("child quit....\n");
exit(1);
}
else if(id>0)
{
printf("i am process..father---pid:%d,ppid:%d\n",getpid(),getppid());
sleep(1);
}
}
return 0;
}
用 while :; do ps aux |head -1&&ps aux|grep a.out; echo "#######################";sleep 1;done
来监控进程的状态。
死亡状态:进程被操作系统释放了或者自己退出了
当一个进程变为僵尸状态的时候,该进程就变成了僵尸进程。
僵尸进程的危害
在Linux中,进程的关系主要是父子关系。
一对父子进程中的父进程退出了,子进程还在运行,就会形成孤儿进程。
如果没有进程来回收该子进程的信息,那么会变成僵尸状态,会存在内存泄漏的问题。
为了解决这个问题,该子进程会立即被1号init进程领养。
通过代码来模拟孤儿状态的进程:
#include
#include
#include
int main()
{
pid_t id=fork();
int count=5;
while(1)
{
if(id==0)
{
while(1){
printf("i am process..child---.pid:%d,ppid:%d\n,count: %d",getpid(),getppid(),--count);
sleep(1);
}
printf("child quit....\n");
exit(1);
}
else if(id>0)
{
while(count)
{
printf("i am process..father---pid:%d,ppid:%d\n",getpid(),getppid());
count--;
sleep(1);
}
exit(0);
}
}
return 0;
}
用 while :; do ps axj |head -1 && ps axj | grep a.out;echo "#######################";sleep 1;done
来监控进程的状态。
观察子进程的PPID
通过查看该子进程的信息,可以得知该进程被1号init进程领养。
CPU中的资源是有限的,不可能多个进程一起在CPU上运行,利用优先级把进程有效的先后排好,改善了系统的性能。
- cpu资源分配的先后顺序,就是指进程的优先权(priority)
- 优先权高的有优先执行权。
用 ps -l
可以查看到进程的优先级
PRI:表示这个进程被执行的优先级,其值越小越早执行
NI:表示这个进程的nice值
nice值表示进程可被执行的优先级的修正值。
PIR=PIR(old)+nice。
当nice为负值时,那么该进程的优先级值会变小,优先级会变高,进程越快被执行。
当然,nice也是有范围的,-20~19,一共40个级别。
top
命令
接着按r
然后输入进程的PID
输入nice
值