程序和进程
程序是指编译好的二进制文件,在磁盘上,不占用系统资源。
进程是活跃的程序,占用系统资源。
进程和cpu
当运行编译好的文件时,cpu会通过mmu(内存管理单元)把虚拟地址映射为物理地址,然后将程序运行的资源放入内存中。进程是执行(运行)程序的动态过程,而程序是进程运行的静态文本。
cpu每次只能执行一个进程,多个进程轮流循环执行,这是串行,我把它理解为排队做核酸,排队时每个人就好比一个进程,轮到你才给你做,医护人员是cpu。有三条队伍,三个医护人员,同时做,这是并行。
并发的话,只有一个医护人员(cpu),但是有三条队伍,交替做,(实际上做核算不可能并发)这种就是单核CPU,可以通过多个进程切换时间片交替运行,实现并发。一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行。任务执行的一小段时间叫做时间片,
如果是多核CPU,可以做到真实的同时运行,即并行。
因为还没有学习线程,这块的理解还是比较浅的,说实话比较混乱,后面会更新这块的知识。
进程控制块PCB
存在与内核中,控制和维护进程,linux内核的进程控制块是task_stuct结构体。如进程id,进程的状态(初始态,就绪态,运行态,挂起态,停止态,等),umask掩码,文件描述符表,用户id和组id等等。
进程状态:
三态:运行,就绪,等待。比如进程1执行了一会,处于运行态,然后Cpu该执行进程2了,这时进程1切换至就绪态,然后过了一会又轮到进程1了,那么进程1又被执行了,直到它运行结束。等待态(阻塞态)出现等待事件,比如I/O请求。
五态:跟上述一样,加了两种状态,创建态和终止态。创建态:申请PCB,然后为该线程运行分配必须的资源,并将该线程转为就绪状态插入到就绪队列中。终止态:进程结束或出现错误,或被系统终止,出现终止状态,无法再执行。
fork函数
通过调用fork创建一个与原来进程几乎完全相同的进程。原来的进程为父进程,创建的新进程是子进程。
fork()没有参数,失败返回-1,子进程返回0,父进程返回子进程PID。
getpid()得到当前进程的pid,getppid()得到当前进程的父进程的pid。可以通过这两个函数区分父进程和子进程。
孤儿进程:父进程先于子进程结束,则子进程称为孤儿进程,init进程会接管孤儿进程,成为它的父进程,可以通过kill杀死孤儿进程。
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<stdlib.h>
int main()
{
pid_t pid01;
pid01=fork();
if(pid01==-1)
{
perror("fork error\n");
}
else if(pid01==0)
{
while(1)
{
printf("I'm kid,my pid=%d,my dad pid=%d\n",getpid(),getppid());
sleep(1);
}
}
else
{
printf("I'm dad,my pid=%d,my dad pid=%d\n",getpid(),getppid());
sleep(5);
printf("son,I'm gonna die\n");
}
return 0;
}
僵尸进程:进程终止,父进程尚未回收,子进程残留资源(PCB)还存放在内核中,变成僵尸(zombie)进程。僵尸进程会存在占用进程号的问题,可能会导致不能产生新的进程。方法杀死父进程,僵尸进程就变成了孤儿进程,这些孤儿进程就会被init进程接管。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
int main()
{
pid_t pid01=fork();
if(pid01==-1)
{
perror("fork error\n");
}
else if(pid01==0)
{
printf("I'm kid锛宮y pid=%d,my dad pid=%d\n",getpid(),getppid());
sleep(5);
printf("I'm gonna die\n");
}
else
{
while(1)
{
printf("I'm dad锛宮y pid=%d,my dad pid=%d\n",getpid(),getppid());
sleep(1);
}
}
return 0;
}