功能:由exec函数族中的函数,则可以根据指定的文件名或路径,找到可执行文件。
对比fork函数:
fork:子进程复制父进程的堆栈段和数据段,子进程一旦开始运行,它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再影响
exec:一个进程调用exec类函数,它本身就"死亡"了,系统把代码段替换成新的程序代码,废弃原有数据段和堆栈段,并为新程序分配新数据段与堆栈段
也就是说:
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
exec函数族中的六个函数:
#include
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char * const argv[]);
int execvp(const char *file, char * const argv[]);
int execve(const char *path, char * const argv[], char * const envp[]);
参数:
(1)当参数是path,传入的为路径名;当参数是file,如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。;
(2)可以将exec函数族分为execl和execv两类:
execl类:带l的一类exac函数(l表示list),包括execl、execlp、execle,要求将新程序的每个命令行参数都说明为 一个单独的参数,这种参数表以空指针结尾。
execv类:函数将以参数向量表传递参数,char * argv[]的形式传递文件执行时使用的参数,数组中最后一个参数为NULL;
(3)如果没有参数char * const envp[],则采用默认环境变量;如果有,则用传入的参数替换默认环境变量;
实例:
#include
#include
#include
int main(){
pid_t tempPid;
tempPid=fork();
if(tempPid == -1){
perror("fork error");
exit(1);
} else if(tempPid > 0) {
printf("parent process:pid=%d\n", getpid());
} else {
printf("child process:pid=%d\n", getpid());
//execl("/bin/ls","-a","-l","/home/bonjour/Experiment/fork1/main.c",NULL); //①
//execlp("ls","-a","-l","/home/bonjour/Experiment/fork1/main.c",NULL); //②
char *arg[]={"-a","-l","/home/bonjour/Experiment/fork1/main.c", NULL}; //③
execvp("ls", arg);
perror("error exec\n");
printf("child process:pid=%d\n", getpid());
} //of if
return 0;
} //of main
2.exit
功能:进程退出
使用:
#include
void exit(int status);
参数:
(1)status:表示进程的退出状态,0表示正常退出,非0表示异常退出,一般用-1或1表示;
(2)为了可读性,标准C定义了两个宏:EXIT_SUCCESS和EXIT_FAILURE
引出:
#include
void _exit(int status);
对比:
_exit:系统会无条件停止 *** 作,终止进程并清除进程所用内存空间及进程在内核中的各种数据结构;
exit:对_exit进行了包装,在调用_exit()之前先检查文件的打开情况,将缓冲区中的内容写回文件。相对来说exit比_exit更为安全
wait函数使用:
#include
pid_t wait(int *status);
功能:
挂起进程,直至子进程变为僵尸态。
回收子进程资源。
若当前进程有多个子进程,只要捕获到有一个子进程变为僵尸态,就恢复执行态。
参数:
一个int *类型的指针,保存子进程退出时的状态信息。
如果参数设置为NULL,则表示不关心进程如何终止。
返回值:
成功:子进程id
失败:返回-1,errno被设置为ECHILD
补充:
errno 是记录系统的最后一次错误代码。 代码是一个int型的值,在errno.h中定义。 查看错误代码errno是调试程序的一个重要方法。
#include
#include
#include
int main(){
pid_t tempPid, tempW;
tempPid = fork();
if(tempPid == -1){
perror("fork error");
exit(1);
}else if(tempPid == 0){//child
sleep(3);
printf("Child process, pid = %d, ppid = %d\n", getpid(), getppid());
}else{//parent
tempW = wait(NULL);
printf("Catched a child process, pid = %d, ppid = %d\n", tempW, getpid());
}//of if
printf("......finish......\n");
return 0;
}//of main
运行结果:
当我们把wait函数注释掉:
父进程先于子进程运行并终止。
因为sleep函数导致子进程进入等待队列,所以终端竞争得到了运行权。
查看发现已经被init领养。
以上是wait函数参数为空的情况,下面进行记录子进程退出状态:
此处声明两个判断进程退出状态的宏函数:
#include
int WIFEXITED(int status);//判断子进程是否正常退出,若是,返回非0值,否则返回0
int WEXITSTATUS(int status);//和WIFEXITED配合使用,WIFEXITED返回非0值,则使用该宏提取子进程的返回值。
代码:
#include
#include
#include
int main(){
int tempStatus;
pid_t tempPid, tempW;
tempPid = fork();
if(tempPid == -1){
perror("fork error");
exit(1);
} else if(tempPid == 0){//子
sleep(3);
printf("Child process: pid=%d\n",getpid());
exit(5);
} else{//父
tempW = wait(&tempStatus);
if(WIFEXITED(tempStatus)){
printf("Child process pid=%d exit normally.\n", tempW );
printf("Return Code:%d\n",WEXITSTATUS(tempStatus));
} else {
printf("Child process pid=%d exit abnormally.\n", tempW);
}//of if
}//of if
return 0;
}//of main
4.waitpid函数
使用:
#include
pid_t waitpid(pid_t pid, int *status, int options);
功能:
waitpid是基于wait函数的,wait函数有一个缺点:当有很多个子进程的时候,wait函数无法保证所有子进程在父进程之前执行。(只是捕捉到一个僵尸态进程而已)
而waitpid则可以指定子进程,也可以在父进程不阻塞的情况下获取子进程状态。
参数:
pid:指定进程组、进程号
1.>0:等待进程号为pid的子进程退出,若退出,函数返回;若未结束,一直等待
2. =0:等待同一进程组的所有子进程退出,注意,若某进程进入其他进程组,则waitpid则不再关心他的状态
3. -1:退化为waitpid函数
4. <-1:等待指定进程组中的任何子进程,进程组的id等于pid的绝对值
options:提供控制选项,可以是一个常量,也可以是|连接的两个常量
1 WNOHANG:如果子进程没有终止,waitpid不会阻塞父进程,会立即返回;
2. WUNTRACED:如果子进程暂停执行,waitpid立即返回;
3. 0:不使用选项。
返回值:
成功:返回捕捉到的子进程id
0:options=WNOHANG,waitpid发现没有已退出的子进程可回收
-1:出错,errno被设置
test_waitpid.c
#include
#include
#include
int main(){
pid_t tempPid, tempP, tempW;
tempPid= fork(); //创建第一个子进程
if (tempPid == -1){
perror("fork1 error");
exit(1);
} else if (tempPid == 0){ //子进程沉睡
sleep(5);
printf("First child process:pid=%d\n", getpid());
} else { //父进程继续创建进程
int i;
tempP = tempPid;
for (i = 0; i < 3; i++){ //由父进程创建3个子进程
if ((tempPid = fork()) == 0){
break;
}//of if
}//of for i
if (tempPid == -1){ //出错
perror("fork error");
exit(2);
} else if (tempPid == 0){ //子进程
printf("Child process:pid=%d\n", getpid());
exit(0);
} else { //父进程
tempW = waitpid(tempP, NULL, 0); //等待第一个子进程执行
if (tempW == tempP){
printf("Catch a child Process: pid=%d\n", tempW);
}else{
printf("waitpid error\n");
}//of if
}//of if
}//of if
return 0;
}//of main
【案例 4】基于waitpid函数不断获取子进程的状态。
wait()函数:父进程可以通过wait()和waitpid()函数可以有效防止僵尸进程的产生,对于已存在的僵尸进程,则可通过杀死其父进程的方法解决,当僵尸进程的父进程被终止后,僵尸进程将作为孤儿进程被init进程接收,init进程会不断调用wait()函数获取子进程状态,对已处于僵尸态的进程进行处理。所以说,孤儿进程永远不会成为僵尸进程。
#include
#include
#include
int main(){
pid_t tempPid;
tempPid=fork();
if(tempPid == -1){
perror("fork error");
exit(1);
} else if(tempPid > 0) {
printf("parent process:pid=%d\n", getpid());
} else {
printf("child process:pid=%d\n", getpid());
//char *arg[]={"/home/bonjour/桌面/file", NULL};
//execvp("cat", arg);
execl("/bin/cat","cat","/home/bonjour/桌面/file",NULL);
perror("error exec\n");
printf("child process:pid=%d\n", getpid());
} //of if
return 0;
} //of main
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)