系统级程序设计第三课内容——Linux进程管理2 2022.5.11

本文详细介绍了Linux系统中的进程管理,包括exec函数族(如execl、execv等)用于程序替换,进程如何正常和异常退出,僵尸进程的概念及其影响,wait和waitpid函数在回收子进程中的作用,以及匿名和命名管道在进程间通信的应用。通过示例代码演示了各种功能的实现,是理解Linux进程管理的重要参考资料。
摘要由CSDN通过智能技术生成

系统级程序设计第三课内容——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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值