PIPE:无名管道
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <unistd.h>
int pipe(int fd[2]); //返回值:若成功,返回0,若出错,返回-1</span>
经由参数fd返回两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。fd[1]的输出是fd[0]的输入。
<span style="font-family:Microsoft YaHei;font-size:18px;"></span><pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;font-size:18px;">#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
pid_t pid;
int temp;
int fd[2];
char s[14] = "test message";
char d[14];
if(pipe(fd) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
if((pid = fork()) < -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(pid == 0) //子进程
{
close(fd[0]); //关闭读端
printf("now, write data to pipe\n");
if((write(fd[1], s, 14)) == -1)
{
perror("write");
exit(EXIT_FAILURE);
}
else
{
printf("the written data is %s\n", s);
exit(EXIT_FAILURE);
}
}
else if(pid > 0) //父进程
{
close(fd[1]); //关闭写端
sleep(2);
printf("now, read data from pipe\n");
if ((read(fd[0], d, 14)) == -1)
{
perror("read");
exit(EXIT_FAILURE);
}
printf("the data from pipe is %s\n", d);
}
return 0;
}
</span>
POPEN和PCLOSE
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <stdio.h>
FILE *open(const char *cmdstring, const char *type);//返回值:若成功,返回文件指针;若出错,返回NULL
int pclose(FILE *fd);//返回值:若成功,返回cmdstring的终止状态;若出错,返回-1</span>
函数popen先执行fork,然后调用exec执行cmdstring,并且返回一个标准I/O文件指针。
如果type是“r”,则文件指针连接到cmdstring的标准输出。
如果type是“w”,则文件指针连接到cmdstring的标准输入。
<span style="font-family:Microsoft YaHei;font-size:18px;"></span><pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;font-size:18px;">#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
int main(int argc, char *argv[])
{
FILE *finput, *foutput;
char buffer[PIPE_BUF];
int n;
finput = popen("echo test!", "r"); //将echo test命令的输出与读端相连
foutput = popen("cat", "w"); //将cat命令的输入与写端相连
read(fileno(finput), buffer, strlen("test!")); //读echo test的输出结果到buf
write(fileno(foutput), buffer, strlen("test!"));//将管道内容读出作为cat输入
pclose(finput); //关闭流
pclose(foutput);
printf("\n");
exit(EXIT_SUCCESS);
}
</span>
FIFO:有名管道
未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同的创建了它们的祖先进程。但是通过FIFO,不相关的进程也能交换数据。
<span style="font-family:Microsoft YaHei;font-size:18px;">#include <sys/stat.h>
int mkfifo(const char *path, mode_t mode);
int mkfifoat(int fd, const char *path, mode_t mode);
//两个函数的返回值: 若成功,返回0;若出错,返回-1.</span>
mkfifoat函数和mkfifo函数相似。但是mkfifoat函数可以被用来在fd文件描述符表示的目录相关的位置创建一个FIFO。
FIFO不同于无名管道之处在于它提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统中,这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据。值的注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。
注意:有名管道的名字存在于文件系统中,内容存放在内存中。
有名管道的打开规则
有名管道比无名管道多了一个打开操作:open
FIFO的打开规则:
如果当前打开操作时为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。
如果当前打开操作时为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENIO错误(当期打开操作没有设置阻塞标志)。
探究发现,如果open时没有使用O_NONBLOCK参数,我们发现不论读端还是写端先打开,先打开者都会阻塞,一直阻塞到另一端打开。
如果指定了O_NONBLOCK,则只读open立即返回。但是,如果没有进程为读而打开一个FIFO,那么只写open将返回-1,并将errno设置成ENXIO.
有名管道的读写规则
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO_NAME "/tmp/my_fifo" //要创建的有名管道路径
int main(int argc, char *argv[])
{
int pipe_fd;
int res;
char buffer[] = "hello world!";
if (access(FIFO_NAME, F_OK) == -1) //文件是否存在
{
res = mkfifo(FIFO_NAME, 0766); //创建管道
if(res != 0)
{
fprintf(stderr,"Could not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY\n", getpid()); //打印提示信息
pipe_fd = open(FIFO_NAME, O_WRONLY); //打开有名管道
printf("the file's descriptor is %d\n", pipe_fd);
if(pipe_fd != -1)
{
res = write(pipe_fd, buffer, sizeof(buffer)); //写数据
if(res == -1)
{
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
printf("write data is %s, %d bytes is write\n", buffer, res); //打印写入的数据及数据量
(void)close(pipe_fd); //关闭管道
}
else
exit(EXIT_FAILURE);
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_NAME "/tmp/my_fifo"
int main(int argc, char *argv[])
{
int pipe_fd;
int res;
char buffer[4096];
int bytes_read = 0;
bzero(buffer, sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_RDONLY); //打开管道,因创建在写进程,故执行时需要执行写进程
printf("the file's descriptor is %d\n", pipe_fd);
if (pipe_fd != -1)
{
bytes_read = read(pipe_fd, buffer, sizeof(buffer)); //读数据输出
printf("the read data is %s\n", buffer);
close(pipe_fd); //关闭
}
else
exit(EXIT_FAILURE);
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}