当一个进程连接数据到另一个进程时,这种连接方式被称为管道。
管道实际上在内核创建了一个i node,和一个指向它的固定大小内核缓冲区(可以理解为类似cache,但读写不分开),传入的两个文件描述符就都指向这段缓冲区,这样读管道的0文件描述符和写它的1文件描述符,就好像读写这个文件一样,实现了进程间的通信。
管道借助于文件系统,根据是否要创建管道文件分为有名管道和无名管道。有名管道的用法是创建一个管道文件,然后利用文件的读写操作利用其进行通信。无名管道是在父子进程或者兄弟进程之间利用文件描述符进行通信,这里并不需要创建文件。
无名管道是一种非永久性的管道通信机构.当它访问的进程全部终止时,它也将随之被撤消。无名管道只能用在具有家族联系的进程之间。
有名管道可以长期存在于系统之中.而且提供给任意关系的进程使用,但是使用不当容易导致出错。
管道是所有进程间通讯最简单的方式,这样也导致了管道只能传递固定格式的数据。一旦遭遇一对多,多对一,多对多的情况管道就不再适用了。
无名管道利用pipe函数进行创建管道,其原型为:
int pipe(int file_descriptor[2]);
其中,file_descriptor[1]用来写入,file_descriptor[0]用来读取。
父子进程间代码实现如下
#include
#include
#include
#include
#include
int main()
{
int data_processed;
int file_pipes[2];
const char some_data[] = "123";
char buffer[128];
pid_t fork_result;
memset(buffer, '\0', sizeof(buffer));
if(pipe(file_pipes) == 0)
{
fork_result = fork();
assert(fork_result != -1);
if(fork_result == 0)
{
// 子进程
data_processed = read(file_pipes[0], buffer, 127);
printf("READ %d bytes: %s \n",data_processed, buffer);
exit(0);
}
else
{
//父进程
data_processed = write(file_pipes[1], some_data,
strlen(some_data));
printf("Wrote %d bytes \n",data_processed);
}
}
exit(0);
}
无名管道也可以应用于兄弟进程之间,这样就需要利用exec函数进行进程替代了。
函数实现如下
//pipe3.c
#include
#include
#include
#include
#include
int main()
{
int data_processed;
int file_pipes[2];
const char some_data[] = "123";
char buffer[128];
pid_t fork_result;
memset(buffer, '\0', sizeof(buffer));
if(pipe(file_pipes) == 0)
{
fork_result = fork();
assert(fork_result != -1);
if(fork_result == 0)
{
// 子进程
sprintf(buffer, "%d", file_pipes[0]);
(void)execl("pipe4", "pipe4", buffer, (char*)0);
exit(0);
}
else
{
//父进程
data_processed = write(file_pipes[1], some_data,
strlen(some_data));
printf("%d - wrote %d bytes \n",getpid(),data_processed);
}
}
exit(0);
}
其中用到了pipe4.c这个文件,这个文件只是一个简单的输出文件。代码如下
//pipe4.c
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int data_processed;
char buffer[128];
int file_descriptor;
memset(buffer, '\0', sizeof(buffer));
sscanf(argv[1], "%d", &file_descriptor);
data_processed = read(file_descriptor, buffer, 127);
printf("%d - read %d bytes : %s \n",getpid(), data_processed, buffer);
exit(0);
}
有名管道需要一个共同的管道文件,他可以在命令行窗口由mkfifo filename 命令创建,也可以在程序中利用mkfifo()函数创建,其格式为:
#include
#include
int mkfifo(const char* filename,mode_t mode);
其中,filename 是文件名,mode是文件权限。
代码如下所示:
fifo3.c文件是写进程,向管道里写数据
//fifo3.c
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "./test"
#define BUFFER_SIZE 128
#define TEN_MEG (1024*1024*10)
int main()
{
int pipe_fd;
int res;
int open_mode = O_WRONLY;
int bytes_sent = 0;
char buffer[128];
if(access(FIFO_NAME, F_OK) == -1)
{
res = mkfifo(FIFO_NAME, 0777);
if(res != 0)
{
fprintf(stderr,"COULD not creat fifo %s \n",FIFO_NAME);
exit(0);
}
printf("process %d opening FIFO O_WRONLY\n",getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d\n",getpid(),pipe_fd);
if(pipe_fd != -1)
{
while(bytes_sent < TEN_MEG)
{
res = write(pipe_fd, buffer, BUFFER_SIZE);
if(res == -1)
{
fprintf(stderr, "Write errer on pipe \n");
exit(0);
}
bytes_sent += res;
}
(void)close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finished\n ",getpid());
exit(EXIT_SUCCESS);
}
exit(0);
}
fifo4.c文件是读文件。
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "./test"
#define BUFFER_SIZE 128
#define TEN_MEG (1024*1024*10)
int main()
{
int pipe_fd;
int res;
int open_mode = O_RDONLY;
char buffer[128];
int bytes_read = 0;
memset(buffer, '\0', sizeof(buffer));
printf("process %d opening FIFO O_RDONLY\n",getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d\n",getpid(), pipe_fd);
if(pipe_fd != -1)
{
do
{
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes_read += res;
}while(res > 0);
(void)close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finishe, %d bytes read\n",getpid(),bytes_read);
exit(EXIT_SUCCESS);
}
写入时,如果没有读取段,则会阻塞,以等待读进程。