一、进程控制原语:
1、进程创建:fork
格式:pid = int fork()
fork调用返回时,系统中已有两个用户级环境完成相同的进程存在,这两个进程从fork调用中得到的返回值不同,其中子进程得到的返回值为0,父进程得到的返回值是新创建子进程的进程标识号,子进程得到是0。
2、系统调用:exec函数族
格式:
#include <unistd.h>
int execl (const char*pathname, const char* arg0,………/*(char*)0*/);
int execv (const char* pathname,char* const argv[]);
函数execl和execv的区别与参数表的传递有关(l表示list,v表示vector)
int execve (const char*pathname, char* const argv[], const char* envp[]);
execve函数加载并运行可执行目标文件pathname,且带参数列表argv和环境变量列表envp。
参数列表argv指向一个以null结尾的指针数组,其中每个指针都指向一个参数串。按照惯例,argv[0]是可执行目标文件的名字。
当进程调用一种exec函数时,该进程执行的程序完全替换为新程序。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用一个全新的程序替换了当前进程的正文、数据、堆和栈段。
3、系统调用终止进程执行:exit
僵死进程:一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的终止状态信息,释放它占用的资源)的进程被称为僵死进程(zombie)
防止僵死进程
有效方式1:父进程调用sigaction函数绑定信号SIGCHLD的信号处理函数时,把其选项字段设置为SA_NOCLDWAIT,则可防止僵死子进程。(子进程终止后,内核自动把其终止状态信息丢弃)父进程可随时结束,不必等到所有子进程终止。 详情见UNIX信号博文
有效方式2:调用fork两次以避免僵死进程。
4、系统调用等待子进程暂停或终止:wait
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。
#include <sys/wait.h>
pid_t wait(int *statloc) ; // statloc为返回的终止状态存放处
pid_t waitpid(pid_t pid, int *statloc, int options) ;
父进程调用这两个函数,只要一有子进程终止,则此函数就取得该子进程的终止状态立即返回。否则一直阻塞。(若它没有任何子进程,则立即出错返回)
这两个函数的区别:
① 在一个子进程终止前,wait使其调用者阻塞,而waitpid则有一个选项,可使调用者不阻塞。(options设置为WNOHANG)
② wait只获取在其调用之后的第一个终止子进程,而waitpid则有参数,可控制它所等待的进程。(pid设置为不同的值,有不同的含义。)
5、在程序中执行一个命令字符串:system
#include <stdlib.h>
int system(constchar * cmdstring);
(其效果相当于在控制台输入命令,这样,可以让我们在程序中用到shell命令)
system函数在实现中调用了fork、exec和waitpid。
使用system而不是直接使用fork和exec的优点是:system进行了所需的各种出错处理以及各种信号处理。(故:忘掉exec函数吧,虽然它有更强大的功能,但一般用system函数足矣。)