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;
}

浙公网安备 33010602011771号