系统级程序设计第三课内容——Linux进程管理2
一、exec函数族
fork:子进程复制父进程的堆栈段和数据段,子进程一旦开始运行,它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再影响
exec:一个进程调用exec类函数,它本身就"死亡"了,系统把代码段替换成新的程序代码,废弃原有数据段和堆栈段,并为新程序分配新数据段与堆栈段
主要分成两类:execl和execv
execl类:函数将以列举的形式传入参数,由于参数列表的长度不定,所以要用哨兵NULL表示列举结束;
execv类:函数将以参数向量表传递参数,char * argv[]的形式传递文件执行时使用的参数,数组中最后一个参数为NULL;
#include <unistd.h>
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[]);
注意:
当参数是path,传入的为路径名;当参数是file,传入的可执行文件名;
如果没有参数char * const envp[],则采用默认环境变量;如果有,则用传入的参数替换默认环境变量;
代码举例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
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[] = {"-a","-l","testMain.c",NULL};
execvp("ls",arg);
perror("error exec \n");
printf("child process : pid = %d\n",getpid());
}
return 0;
}
二、进程退出
void exit(int status);
参数:
(1)status:表示进程的退出状态,0表示正常退出,非0表示异常退出,一般用-1或1表示;
(2)为了可读性,标准C定义了两个宏:EXIT_SUCCESS和EXIT_FAILURE
在Linux系统中有一个_exit()函数,相比于exit()较于底层,exit()是对这个函数进行封装,增加了一些安全性的判断。
三、僵尸进程
孤儿进程:
父进程负责回收子进程,如果父进程在子进程退出之前退出,子进程就会变成孤儿进程,此时init进程将代替父进程完成子进程的回收工作;
僵尸进程:
调用exit函数后,该进程不会马上消失,而是留下一个称为僵尸进程的数据结构。它几乎放弃进程退出前占用的所有内存,既没有可执行代码也不能被调度,只是在进程列表中保留一个位置,记载进程的退出状态等信息供父进程回收。若父进程没有回收子进程的代码,子进程将会一直处于僵尸态。
四、wait函数
pid_t wait(int *status);
挂起进程,进程进入阻塞状态,直到子进程变为僵尸态,如果捕获到子进程的退出信息就会转为运行态,然后回收子进程资源并返回;若没有变为僵尸态的子进程,wait函数就会让进程一直阻塞。若当前进程有多个子进程,只要捕获到一个变为僵尸态的子进程,wait函数就会恢复执行态。
成功:返回子进程的进程id;
失败:返回-1,errno被设置为ECHILD。
代码举例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(){
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, ppid = %d\n",getpid(),getppid());
}else{
tempW = wait(NULL);
printf("Catched a child process: pid = %d, ppid = %d\n",tempW,getppid());
}//of if
printf("-------finish-------\n");
return 0;
}//of mian
五、waitpid函数
pid_t waitpid(pid_t pid, int *status, int options);
功能:
wait函数的缺点:当前进程有很多个子进程,wait函数无法保证所有子进程在父进程之前执行。
waitpid函数:可以应对 wait函数面临的缺点。可以等待指定的子进程,也可以在父进程不阻塞的情况下获取子进程的状态。
代码举例:父进程等待进程组中指定子进程,该进程不退出,则父进程一直阻塞。
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(){
pid_t tempPid,tempP,tempW;
tempPid = fork();
if(tempPid == -1){
perror("fork error");
exit(1);
}else if(tempPid == 0){//child
sleep(5);
printf("First child process : pid = %d\n",getpid());
}else{
int i;
tempP=tempPid;
for(i=0; i < 3; i++){
if((tempPid=fork())==0){
break;
}//of if
}//of for
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
代码举例:基于waitpid函数不断获取子进程的状态。
test_waitpid2.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main() {
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(0);
} else {
do{
tempW = waitpid(tempPid, NULL, WNOHANG);
if (tempW == 0){
printf("No child exited\n");
sleep(1);
}//of if
} while (tempW == 0);
if (tempW == tempPid){
printf("Catch a Child process:pid=%d\n", w);
}else{
printf("waitpid error\n");
}//of if
}//of if
return 0;
}//of main
僵尸进程不能再次被运行,会占用一定的内存空间,并占据进程编号,当僵尸进程较多时,将会消耗系统内存,新进程可能因内存不足或无法获取pid而无法创建;这也是一种网络常见攻击手段,只占进程号不占内存。
六、管道
管道分为:
匿名管道:只能用于有亲缘关系的进程间通信,进程退出后管道会被销毁。
命名管道:命名管道与进程的联系较弱,相当于一个读写内存的接口,进程退出后,命名管道依然存在
代码举例:使用pipe()实现父子进程间通信,父进程作为读端,子进程作为写端。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
int tempFd[2];
int tempRet = pipe(tempFd);
if(tempRet == -1){
perror("pipe");
exit(1);
}
pid_t tempPid = fork();
if(tempPid > 0){
close(tempFd[1]);
char tempBuf[64] = {0};
tempRet = read(tempFd[0], tempBuf, sizeof(tempBuf));
close(tempFd[0]);
write(STDOUT_FILENO, tempBuf, tempRet);
wait(NULL);
} else if(tempPid == 0){
close(tempFd[0]);
char *tempStr = "hello,pipe\n";
write(tempFd[1], tempStr, strlen(tempStr)+1);
}//of if
return 0;
}//of main
参考文章:https://blog.csdn.net/search_129_hr/article/details/124568099?spm=1001.2014.3001.5502
https://blog.csdn.net/search_129_hr/article/details/124483201?spm=1001.2014.3001.5502