系统级程序设计 -- 进程管理

3.2.1exec()函数族
使用fork()函数创建的子进程中包含的程序代码完全相同,只是能根据fork()函数的返回值执行不同的代码分支。当每个分支的内容较多时,代码自身便较为庞大;另外,若要执行的分支与程序其他内容并不相干,对子进程来说,对除与之对应的分支外,其他的内容都是无意义的。
由exec函数族中的函数,则可以根据指定的文件名或路径,找到可执行文件。
注意:1)调用exec函数族时,不创建新进程,因此进程的pid不会改变;
2)exec只是用新程序中的数据替换了进程中的代码段、数据段、堆栈中的数据;
3)exec调用成功时没有返回值


案例:

#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());
        //execl("/bin/ls","-a","-l","test_exec.c",NULL);    //①
        //execlp("ls","-a","-l","test_exec.c",NULL);    //②
        char *arg[]={"-a","-l","test_exec.c", NULL};    //③
        execvp("ls", arg);
        perror("error exec\n");
        printf("child process:pid=%d\n", getpid());
    } //of if  
    return 0;
} //of main

运行截图:

 

3.2.2进程退出
Linux系统中进程的退出通过exit()函数实现。exit()函数存在于系统函数库stdlib.h中,其声明如下:

#include <stdlib.h>
void exit(int status);


参数说明:
1)status:表示进程的退出状态,0表示正常退出,非0表示异常退出,一般用-1或1表示;

2)为了可读性,标准C定义了两个宏:EXIT_SUCCESS和EXIT_FAILURE

Linux系统中有一个与exit()函数非常相似的函数:_exit(),其声明如下:

#include <unistd.h>

void _exit(int status);

3.3进程同步
Linux系统中提供了wait()函数和waitpid()函数来获取当前进程的状态,实现进程同步。

3.3.1 wait()函数
wait()函数存在于系统库函数sys/wait.h库中,其声明如下:

#include <sys/wait.h>
pid_t wait(int *status);
 

参数说明:参数status是一个int * 类型的指针,用来保存子进程退出时的状态信息。但是通常我们只想消灭僵尸进程,不在意程序如何终止,因此一般将该参数设置为NULL。

返回值说明:
成功:返回子进程的进程id;
失败:返回-1,errno被设置为ECHILD。

测试案例:

#inlcude <unistd.h>
#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){//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, getppid());
    }//of if
    printf("......finish......");
    return 0;
}//of main

运行截图:


 

3.3.2 waitpid()函数
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);

1
2
3
功能:
wait函数的缺点:当前进程有很多个子进程,wait函数无法保证所有子进程在父进程之前执行。
waitpid函数:可以应对 wait函数面临的缺点。可以等待指定的子进程,也可以在父进程不阻塞的情况下获取子进程的状态。

参数说明:
pid:一般是进程的pid,也有可能是其他取值。进一步说明如下:
pid > 0:等待子进程(编号为pid)退出,若退出,函数返回;若未结束,则一直等待;
pid = 0:等待同一进程组的所有子进程退出,若某子进程加入了其他进程组,则waitpid不再关心它的状态;
pid = -1:waitpid函数退化为wait函数,阻塞等待并回收一个子进程;
pid < -1:等待指定进程组中的任何子进程,进程组的id等于pid的绝对值。

options: 提供控制选项,可以是一个常量,也可以是|连接的两个常量,选项如下:
WNOHANG:如果子进程没有终止,waitpid不会阻塞父进程,会立即返回;
WUNTRACED:如果子进程暂停执行,waitpid立即返回;
0:不使用选项。

返回值说明:
成功:返回捕捉到的子进程id;
0:options = WNOHANG, waitpid发现没有已退出的子进程可回收;
-1:出错,errno被设置。

测试案例:父进程等待进程组中指定子进程,该进程不退出,则父进程一直阻塞。

​
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
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.1 进程间通信
4.1.1 管道
管道是一种最基本的进程通信机制,其实质是由内核管理的一个缓冲区。可以形象的认为管道的两端连接着两个需要进行通信的进程,一个进程进行信息输出,将数据写入管道;另一个进程进行信息输入,从管道中读取信息。

4.1.2 匿名管道
匿名管道的使用流程如下:
①在进程中创建匿名管道,pipe函数;
②关闭进程中不使用的管道端口,close函数;
③在待通信的进程中分别对管道的读、写端口进行操作,read/write函数;
④关闭管道,close函数。

4.1.3 pipe()函数
该函数声明如下:

#include <unistd.h>
int pipe(int pipefd[2]);


功能:
创建匿名管道
参数说明:
pipe()的参数pipfd实质上是一个文件描述符数组,当在程序中使用pipe()创建管道时,程序可以通过传参的方式获取两个文件描述符,分别交给需要通信的两个进程。

返回值说明:
成功:返回0
不成功:返回-1

#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);//写数据
        close(tempFd[1]);
   }//of if
   return 0;
}//of main

运行截图:

 由执行结果可知:父进程在管道中写入字符串"hello,pipe",子进程从管道中将该字符串读取并打印到了终端。使用close()函数关闭父进程的写端与子进程的读端。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值