启用新的进程
system函数
system函数使用简单,因为它必须用一个shell来启动需要的程序,因此对shell的安装情况和环境的依赖很大,并且有一些其它的缺陷,所以system函数的使用率并不高。
如下例程system1:
#include <stdlib.h>
#include <stdio.h>
int main()
{
printf("Runing test...\r\n");
system("ps");
printf("Done.\r\n");
exit(0);
}
输出结果:
$ ./test
Runing test...
PID TTY TIME CMD
5843 pts/19 00:00:00 bash
8246 pts/19 00:00:00 test
8247 pts/19 00:00:00 sh
8248 pts/19 00:00:00 ps
Done.
因为system函数用一个shell来启动想要执行的程序,所以可以放到后台来执行,修改system1.c代码
#include <stdlib.h>
#include <stdio.h>
int main()
{
printf("Runing test...\r\n");
system("ps &");
printf("Done.\r\n");
exit(0);
}
输出结果:
$ ./test1
Runing test...
Done.
$
PID TTY TIME CMD
5843 pts/19 00:00:00 bash
9601 pts/19 00:00:00 ps
这样的打印信息会使用户比较迷茫。因为是后台程序,ps命令一启动后shell就返回了。
替换进程映像
exec系列函数可以直接替换原先进程,去执行新的指令,新进程的PID、PPID和nice值也完全一样,但是exec函数后面的代码将不会在执行,因为它不会再返回原先的进程中。
复制进程映像
要想让进程同时执行多个函数,我们可以使用线程或从原程序中创建一个完全分离的进程,后者就像init的做法一样,而不像exec调用那样用新程序替换当前执行的进程。复制一个进程可以使用fork()函数,复制进程时可以使用wait()函数,它将暂停父进程直到它的子进程结束为止。
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t pid;
printf("fork program starting\r\n");
pid = fork();
printf("fork pid is: PID=%d\r\n", pid);
switch(pid)
{
case -1:
perror("fork failed");
exit(1);
case 0:
printf("This is the child\r\n");
break;
default:
printf("This is the parent\r\n");
break;
}
if(0 != pid)//parent progress
{
int stat_val;
pid_t child_pid;
child_pid = wait(&stat_val);
printf("child has finished: PID=%d\r\n", child_pid);
if(WIFEXITED(stat_val))
printf("Child exited with code %d\r\n", WEXITSTATUS(stat_val));
else
printf("Child terminated abnormally\r\n");
}
exit(0);
}
代码为fork函数和wait函数配合,来新建一个进程,运行结果为
$ ./wait
fork program starting
fork pid is: PID=16207
This is the parent
fork pid is: PID=0
This is the child
child has finished: PID=16207
Child exited with code 0
如果fork了多个子进程,可以固定的等待某个子进程是否结束,使用waitpid函数,函数原型如下:
pid_t waitpid(pid_t pid, int *stat_loc, int options);
pid参数指定需要等待的子进程的PID。如果它的值为-1,waitpid将返回任一子进程的信息。
若stat_loc不是空指针,waitpid将会把状态信息写入它所指向的位置。
options参数用来改变waitpid的行为,其中最有用的一个选项是WNOHANG,它的作用是防止waitpid调用将调用者的执行挂起。因此,如果想让父进程周期性的检查某个子进程是否终止,可以用下面的调用方式:`waitpid(child_pid, (int *)0, WNOHANG)`
僵尸进程
使用fork函数创建进程,当子进程终止时,它与父进程之间的联系还会保持,直到父进程也正常终止或父进程调用wait后才结束。因此,子进程先结束后,进程表中代表子进程的表项不会立刻释放。虽然子进程已经不在运行,但它仍然存在于系统中,因为它的退出码还需要保存起来,以备父进程今后的wait调用使用,这时它将成为一个僵尸进程。
如果父进程异常终止,子进程会自动把PID为1的进程(即init)作为自己的父进程,僵尸进程会一直保留,直到被init进程发现并释放。