posix标准
定义操作系统应该为应用程序提供的接口标准。
进程管理中比较重要的几个系统调用如下:
pid = fork():创建与父进程相同的子进程。
pid = waitpid(pid,&statloc,options):等待一个子进程终止。
s = execve(name,argv,environp):让进程执行新的代码。
exit(status):终止进程执行并返回状态。
下面给出现代操作系统中给出的实例:
UNIX中的shell
#define TRUE 1
while(TRUE){
//在屏幕上显示提示符
type_prompt();
//从终端读取输入
read_command(command,parameters);
if(fork()!=0){
//等待子进程退出
waitpid(-1,&status,0);
}else{
//执行键入的命令
execve(command,parameters,0);
}
}
该shell在键入一个命令后会调用fork()创建一个子进程,由这个子进程执行键入的命令。
fork()系统调用会创建一个原有进程的精确副本,换句话说,子进程将执行父进程剩余的代码,即fork()之后的代码,并且子进程默认父进程之前的代码已经执行完毕,这些代码是只读的,所以两个进程是共享的,但子进程与父进程有不同的堆栈段以及数据段,只是内容与父进程一致,在一些资料上,会说fork()系统调用返回两个值,其实不够准确,我的理解是,对于父进程,fork()返回子进程的进程标识符,对于子进程,返回0,这两个进程的执行将会是并发的,谁先谁后由操作系统决定,对于子进程,将会执行execve函数。
execve系统调用有三个参数:将要执行的文件名称,指向变量数组的指针(存放键入指令的参数),指向环境数组的指针(将诸如终端类型以及根目录等信息传送给程序),该系统调用实质上是exec系统调用,该调用没有创建新的进程,只是替换了原来进程的上下文,原进程的代码段、数据段、堆栈段被新的进程替换。意味着子进程执行完该函数后就会终止。
对于父进程,将会执行waitpid系统调用,第一个参数为子进程的进程标识符,若为-1,表示任何子进程结束就运行父进程,否则,必须在具体子进程结束后,在运行父进程,第二个参数指向存放子进程退出状态的容器地址,第三个参数为选项,可以选择执行一些功能。
子进程完成后会使用exit系统调用,该系统调用返回一个参数,为退出状态(0到255),该参数通过waitpid中的第二个参数返回给父进程。
为什么UNIX不像windows那样,将fork和exec合并为一个函数?
在网上看到了许多说法,我个人比较认同的是——这是UNIX的设计理念,由于与exec类似的函数有很多,如果将fork与exec合并,会造成大量的代码重复,与UNIX设计理念不合。