C++ 进阶 day10 多进程编程

进程退出

  • exit
    属于库函数,使用该函数退出进程时,会刷新标准IO的缓冲区
  • _exit
    属于系统调用(内核提供的函数),使用该函数退出进程时,不会刷新标准IO的缓冲区

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

  • 功能:退出当前进程,并刷新当前进程打开的标准IO的缓冲区
  • 参数:进程退出时的状态,会将改制 与 0377进行位与运算后,返回给回收资源的进程
  • 返回值:无

#include <unistd.h>
void _exit(int status);

  • 功能:退出当前进程,不刷新当前进程打开的标准IO的缓冲区
  • 参数:进程退出时的状态,会将改制 与 0377进行位与运算后,返回给回收资源的进程
  • 返回值:无

进程资源回收:wait、waitpid

wait:阻塞回收任意一个子进程的资源函数
waitpid:可以阻塞,也可以非阻塞完成对指定的进程号进程资源回收

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

  • 功能:阻塞回收子进程的资源

  • 参数:接收子进程退出时的状态,获取子进程退出时的状态与0377进行位与后的结果,如果不愿意接收,可以填NULL

  • 返回值:成功返回回收资源的那个进程的pid号,失败返回-1并置位错误码

  • pid_t waitpid(pid_t pid, int *status, int options);

  • 功能:可以阻塞也可以非阻塞回收指定进程的资源

  • 参数1:进程号

    • 大于0:表示回收指定的进程,进程号位pid(常用)
    • 等于0:表示回收当前进程所在进程组中的任意一个子进程
    • 等于-1:表示回收任意一个子进程(常用)
    • 小于-1:表示回收指定进程组中的任意一个子进程,进程组id为给定的pid的绝对值
  • 参数2:接收子进程退出时的状态,获取子进程退出时的状态与0377进行位与后的结果,如果不愿意接收,可以填NULL

  • 参数3:是否阻塞

    • 0:表示阻塞等待           例如:waitpid(-1, &status, 0)等价于 wait(&status)
    • WNOHANG:表示非阻塞
  • 返回值:

    • 大于0: 返回的是成功回收的子进程pid号
    • 等于=0:表示本次没有回收到子进程
    • 等于=-1:出错并置位错误码

父进程创建两个进程并为其收尸

点击查看代码
#include <myhead.h>
int main(){
	pid_t pid=fork();
	if(pid>0){
		//父进程
		pid_t pid_2 = fork();
		if(pid_2>0){
			//父进程
			printf("我是父进程\n");
			//回收两个子进程
			wait(NULL);
			wait(NULL);
		}
		else if(pid_2==0){
			sleep(3);
			//二儿子进程内容
			printf("我是进程2\n");
			exit(EXIT_SUCCESS);
		}
	}
	else if(pid==0){
		sleep(3);
		//大儿子进程
		printf("我是进程1\n");
		exit(EXIT_SUCCESS);
	}
	else{
		perror("fork error");
		return -1;
	}
}
大儿子和二儿子出现比较随机)

僵尸进程和孤儿进程

孤儿进程

当前进程还正在运行,其父进程已经退出了
每个进程退出后,其分配的系统资源应该由其父进程进行回收,否则会造成资源的浪费

点击查看代码
#include <myhead.h>
int main(){
	pid_t pid=-1;
	pid=fork();
	if(pid>0){
		//父进程代码
		sleep(5);
		exit(EXIT_SUCCESS);
	}
	else if(pid==0){
		//子进程代码
		while(1){
			printf("我是子进程\n");
			printf("parent pid:%d\n",getppid());
			sleep(1);
		}
	}
	else{
		perror("fork error");
		return 1;
	}
	while(1); //防止进程结束
	return 0;
}

僵尸进程

当前进程已经退出了,但是其父进程没有为其回收资源

点击查看代码
#include<myhead.h>
int main(int argc, const char *argv[])
{
    pid_t pid = -1;            //定义用于存储进程号的变量
    //创建进程
    pid = fork();
    if(pid > 0)
   {
        //父进程程序代码
        while(1)
       {
            printf("我是父进程\n");
            sleep(1);
       }
   }else if(pid == 0)
   {
        //子进程程序代码
       
        sleep(5);
        
        exit(EXIT_SUCCESS);     //子进程退出
   }else 
   {
        perror("fork error");
        return -1;
   }
    while(1);           //防止进程退出
    return 0;
}

进程间通信 IPC

  • 由于多个进程的用户空间是相互独立的,其栈区、堆区、静态区的数据都是彼此私有的,所以不可能通过用户空间中的区域完成多个进程之间数据的通信
  • 可以使用外部文件来完成多个进程之间数据的传递,一个进程向文件中写入数据,另一个进程从文件中读取数据。该方式要必须保证写进程先执行,然后再执行读进程,要保证进程执行的同步性
  • 我们可以利用内核空间来完成对数据的通信工作,本质上,在内核空间创建一个特殊的区域,一个进程向该区域中存放数据,另一个进程可以从该区域中读取数据
  • 引入原因:用户空间中的数据,不能作为多个进程之间数据交换的容器

例子:

点击查看代码
#include <myhead.h>
int num=520;
int main(){
	//int num=520 //定义变量
	pid_t pid=fork();
	if(pid>0){
		//父进程
		num=1314;
		printf("父进程:num = %d\n",num);
		wait(NULL);
	}
	else if(pid==0){
		//子进程
		sleep(3);
		printf("子进程:num = %d\n",num);
	}
	else{
		perror("fork error");
		return -1;
	}
	return 0;
}

进程间通信的基础概念

  • IPC :interprocess communication 进程间通信
  • 使用内核空间来完成多个进程间相互通信,根据使用的容器或方式不同,分为三类通信机制
  • 进程间通信分类
    • 内核提供的通信方式(无名管道,有名管道,信号)
    • system V提供的通信方式(消息队列,内存共享,信号量)
    • 套接字通信 socket 网络通信(跨主机通信)

无名管道

  • 管道的原理:管道是一种特殊的文件,该文件不用于存储数据,只用于进程间通信。管道分为有名管道和无名管道。
  • 在内核空间创建出一个管道通信,一个进程可以将数据写入管道,经由管道缓冲到另一个进程中读取
  • 进程结束就消失
    #include <unistd.h>
    int pipe(int fildes[2]);
    功能:创建一个无名管道,并返回管道的两个文件描述符
    参数:整型数组,用于返回打开的管道的两端的文件描述符,[0]读端,[1]写端
    返回值:成功返回0,失败返回-1并置位错误码
点击查看代码
#include <myhead.h>
int main(){
	int fildes[2];
	if(pipe(fildes)==-1){
		perror("pipe error");
		return -1;
	}

	printf("fildes[0] = %d,fildes[2] = %d\n",fildes[0],fildes[1]);

	pid_t pid=fork();

	if(pid>0){
		//父进程
		//不用读端
		close(fildes[0]);
		char wbuf[128]="hello world";

		write(fildes[1],wbuf,strlen(wbuf));

		close(fildes[1]);

		wait(NULL);
	}
	else if(pid==0){
		//子进程
		//关闭写端
		close(fildes[1]);
		//通过写端从管道文件中读取数据
		char rbuf[128]="";
		read(fildes[0],rbuf,sizeof(rbuf));
		printf("收到父进程的数据为:%s\n",rbuf);
		//关闭读端
		close(fildes[0]);
		//退出子进程
		exit(EXIT_SUCCESS);
	}
	else{
		perror("fork error");
		return -1;
	}
	return 0;
}
posted @ 2025-05-10 09:02  北燃  阅读(31)  评论(0)    收藏  举报