进程切换
进程状态:一个进程的生命周期可以划分为一组状态,这些状态刻画了整个进程。进程状态即体现一个进程的生命状态。
在操作系统中,常见的进程状态是三状态模型,也就是就绪,运行,阻塞(等待),它们的关系如下图:
当然,对于一个实际的系统,进程的状态及其转换更为复杂。为此我们引入了新建态和终止态构成了进程的五态模型:
关于更详细的进程状态点击此处查看。
在这里,我们聊一聊进程之间的切换。
进程切换,又叫上下文切换,它是指暂停当前运行进程,使其从运行状态变为就绪或者等待状态,同时调度另一个进程从就绪状态变为运行状态
进程切换有如下要求:
考虑有如下两个进程P0,P1,开始时候P0处于运行状态,P1出于空闲(等待,就绪),则他们两进程的切换如下图:
进程创建
进程创建,基于不同的平台有不同的实现:
Windows进程创建API:CreateProcess(filename)
Linux进程创建系统调用:fork/exec
fork会把一个进程复制成两个进程,其中原先的进程成为父进程(PID不变),产生的新进程成为子进程(分配一个新的PID)。
exec会用新程序来重写当前进程,但是PID不会改变。
关于这两者的区别:
fork函数是用于创建一个子进程,该子进程几乎是父进程的副本,而有时我们希望子进程去执行另外的程序,exec函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。
在Linux中使用exec函数族主要有以下两种情况
当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数族让自己重生。
如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。
我们写段代码来说明进程创建这个过程:
说明:
当执行完fork这一句后,就产生了两个进程,也就是说,16行代码下是由父子进程共享的,这就是为什么最后结果会分别输出两次this is shared,this is end,因此父子进程各自调用了一次。
那么如何区别父子进程,只有靠进程ID,注意fork函数,它调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
当然,fork代价会比较大,因此引入COW技术,也就是延迟拷贝(写时拷贝)
进程加载
前面,我们讲了执行fork后,此时生成的子进程是父进程的拷贝,这就意味着代码,数据是一样的,往往这没有什么意义,因此,我们可以加载其他程序到这个进程中执行,注意此时PID依旧不变,但是数据,代码都变化了,具体可看下图:
进程等待与退出
等待与退出其实父子进程的一种交互,完成子进程资源的回收
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
extern void print_exit(int status);
int main()
{
int pid = fork();
if (pid < 0)
{
printf("fork error\n");
exit(1);
}
else if (pid > 0) //father
{
int status = 0;
int ret = wait(&status);
printf("father pid = %d, father ppid = %d\n", getpid(), getppid());
exit_print(status);
}
else //child
{
printf("child pid = %d, child ppid = %d\n", getpid(), getppid());
//exit(123); //正常终止,退出码为123
//exit(0); //正常终止,退出码为0
int a = 1/0; //异常终止
abort();
}
return 0;
}
#include<stdio.h>
#include<sys/wait.h>
void exit_print(int status)
{
if (WIFEXITED(status)) //正常终止
{
printf("normally exited, status is %d\n", WEXITSTATUS(status));
}
else if (WIFSIGNALED(status)) //异常终止
{
printf("abnormally exited, signal is %d\n", WTERMSIG(status));
}
else if (WIFSTOPPED(status)) //进程暂停
{
printf("child stopped, signal is %d\n", WSTOPSIG(status));
}
}