在Linux操作系统中,进程间通信(IPC)是一个重要的概念,它允许不同进程之间交换数据。本文将介绍一种使用管道(pipe)进行进程间通信的方法。管道是一种半双工通信方式,允许一个进程的输出成为另一个进程的输入。将通过一个简单的例子来展示如何在子进程中执行一个新的程序,并通过管道通知父进程执行结果。
首先,需要包含一些必要的头文件。在C++中,通常需要以下头文件:
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <fcntl.h>
其中,unistd.h
包含了fork和exec调用以及管道创建函数,而fcntl.h
包含了用于管道的标志。
接下来,声明一些用于进程的变量:
int pipe_status, exec_status, fork_status, pipe_descriptors[2];
这些变量将用于存储管道状态、执行状态、fork状态以及管道描述符。
使用pipe2
函数来创建管道。这个函数接受一个指向整数数组的指针(在案例中是指向数组)作为第一个参数,以及管道创建标志作为第二个参数。将标志设置为O_CLOEXEC
,这意味着在成功调用exec后,管道将自动关闭。
pipe_status = pipe2(pipe_descriptors, O_CLOEXEC);
如果创建失败,pipe2
将返回-1,并且errno
将被设置为相应的错误代码。
使用fork
函数来创建现有进程的一个副本。需要注意的是,在调用fork之后,之后的代码将执行两次(每个进程一次),因此在fork之前创建管道,否则将为父进程和子进程分别创建两个不同的管道,这将导致无法相互通信。
fork_status = fork();
如果fork失败,它将返回-1,并且errno
将被设置为相应的错误代码。如果成功,它将创建现有进程的一个副本,并且fork将返回子进程的PID给父进程,返回0给子进程。因此,通过测试fork_status
的值来确定哪个是父进程,哪个是子进程。
子进程将使用execlp
来执行目标程序,这将替换fork出的子进程的进程映像为目标程序的进程映像。之前打开的管道将自动关闭。
exec_status = execlp("gedit", "gedit", NULL);
execlp
函数接受要执行的程序名称作为第一个参数。在案例中是gedit。第二个参数是要传递给程序的参数,并且它必须是程序本身的名称。当从终端启动程序时,这是由终端隐式完成的,但是当使用exec系列调用时,这是必须自己做的事情。第三个参数是传递给目标的下一个参数,可以添加任意多的参数,所有参数都由逗号分隔,但只有execlp调用中的第一个参数是强制性的。
如果父进程从管道中读取0字节,那么意味着管道已关闭,这表明对exec的调用是成功的。如果父进程从管道中读取的字节数大于0,那么意味着子进程已向管道中写入了某些内容,这在案例中将是错误号。
if(fork_status > 0) {
char buffer[4] = {0};
close(pipe_descriptors[1]);
int bytes_read = read(pipe_descriptors[0], buffer, sizeof(int));
if(bytes_read == 0)
cout << "HOORAH!!!" << endl;
if(bytes_read > 0) {
int error = atoi(buffer);
cout << "EXECLP Failed because: " << strerror(error) << endl;
}
}