管道与命名管道(FIFO文件)
管道是内核中的一个单向的数据通道,同时也是一个数据队列。具有一个读取端与一个写入端,
每一端对应着一个文件描述符。通过使用pipe系统调用创建管道。当父进程调用fork时,所有的
连接、文件描述符都会从父进程传递给子进程。只有有共同父进程的进程之间才可以用管道连接。
管道类似于文件,但又与文件不同。
相同: 都是不带有任何结构的字节序列
不同:
从管道总读取数据
管道读取阻塞:
当进程试图从管道中读取数据时,进程被挂起直到数据被写进管道。
管道的读取结束标志:
当所有的写者关闭了管道的写数据端时,试图从管道读取数据的调用
返回0,者意味着文件的结束。
多个读者可能会引起麻烦:
管道是一个队列。当进程从管道总读取数据之后,数据已经不存在了。
当两个进程同时对一个管道进行读取时,要注意数据的完整性。
向管道中写数据
写入的数据阻塞直到管道有空间去容纳新的数据。
若无读者在读取数据,则写操作失败。
命名管道 即FIFO文件,通过命名管道可以在不相关的进程之间交换数据。FIFO有路径名与之相关联,以一种特殊设备文件形式存在于文件系统中
FIFO有两种用途:
(1)FIFO由shell使用以便数据从一条管道线传输到另一条,为此无需创建临时文件。
(2)FIFO用于客户进程-服务器进程程序中,已在客户进程与服务器进程之间传送数据。
FIFO的读写规则:
从FIFO中读取数据:
1.如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。
对于没有设置阻塞标志的读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试。
2.对于设置了阻塞标志的读操作来说,造成阻塞的原因有两种:一种是当前FIFO内有数据,但有其它进程
再读这些数据;另一种是FIFO内没有数据,阻塞原因是FIFO中有新的数据写入,而不论新写入数据量的
大小,也不论读操作请求多少数据量。
3.读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操
作被唤醒并完成读操作后,其他将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也
一样(此时,读操作返回0)
4.如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞
5.如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,
读操作会返回FIFO中现有的数据量。
向FIFO中写入数据:
对于设置了阻塞标志的写操作:
1.当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要
写入的字节数,则进入睡眠,知道当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作
2.当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就
会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
对于没有设置阻塞标志的写操作:
1.当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。再写满所有FIFO空闲缓冲区后,写操作返回。
2.当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的
字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以
后再写
FIFO的一些注意问题:
(1)管道数据的FIFO处理方式
首先放入管道的数据,在端口首先被读出
(2)管道数据的不可再现性
已读取的数据在管道里消失,不能再读
(3)管道长度的有限性
(4)SIGPIPE信号 如果一个进程试图写入到一个没有读取到进程的管道中哦你,系统内核产生SIGPIPE信号
实例1 :
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<string.h>
#define FIFO "/tmp/my_fifo"
main(int argc,char ** argv){
char buf_r[100];
int fd;
int nread;
if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)) //O_EXCL:可执行
printf("cannot create FIFOserver\n");
printf("Preparing for reading bytes...\n");
memset(buf_r,0,sizeof(buf_r));
fd=open(FIFO,O_RDONLY|O_NONBLOCK,0); //O_RDONLY:只读,O_NONBLOCK:非阻塞
if(fd==-1){
perror("open");
exit(1);
}
while(1){
memset(buf_r,0,sizeof(buf_r));
if((nread=read(fd,buf_r,100))==-1){
if(errno==EAGAIN)
printf("no data yet\n");
}
printf("read %s from FIFO\n",buf_r);
sleep(1);
}
pause();
unlink(FIFO);
}
实例2 :
分别写两个程序,一个是服务器程序,不断从管道读取客户发送的信息;
另一个是客户程序,在命令行输入信息并从管道发送:
客户程序(写管道)
- /*fifo_write.c*/
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- /*FIFO管道路径*/
- #define FIFO_SERVER "/tmp/myfifo"
- main(int argc,char** argv)
- {
- int fd = 0;
- char w_buf[100];
- int nwrite;
- /*打开FIFO管道*/
- fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
- if(fd==-1)
- if(errno==ENXIO)
- printf("open error; no reading process\n");
- /*判断有没有参数输入*/
- if(argc==1)
- printf("Please send something\n");
- /*复制参数输入*/
- strcpy(w_buf,argv[1]);
- /*写到FIFO去*/
- if((nwrite=write(fd,w_buf,100))==-1)
- {
- if(errno==EAGAIN)
- printf("The FIFO has not been read yet.Please try later\n");
- }
- else
- /*输出写入的内容*/
- printf("write %s to the FIFO\n",w_buf);
- }
服务程序(读管道)
/*fifo_read.c*/
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- /*定义FIFO路径*/
- #define FIFO "/tmp/myfifo"
- main(int argc,char** argv)
- {
- char buf_r[100];
- int fd;
- int nread;
- /*创建FIFO管道*/
- if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
- printf("cannot create fifoserver\n");
- printf("Preparing for reading bytes...\n");
- memset(buf_r,0,sizeof(buf_r));
- /*打开FIFO管道,不阻塞方式*/
- fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);
- if(fd==-1)
- {
- perror("open");
- exit(1);
- }
- while(1)
- {
- memset(buf_r,0,sizeof(buf_r));
- /*读管道,因为定义了非阻塞方式,故在此不会阻塞进程*/
- if((nread=read(fd,buf_r,100))==-1){
- if(errno==EAGAIN)
- printf("no data yet\n");
- }
- printf("read %s from FIFO\n",buf_r);
- sleep(1);
- }
- pause();
- unlink(FIFO);
- }
接下来进行编译,编译好后,在Linux中运行两个终端,分别运行以上两个程序,可以看到,运行fifo_read时,程序一直在每隔一秒读,然后我们在另一个终端输入:
$ ./fifo_write helloworld
可以看出fifo_read显示出“helloworld”,说明接受成功
下接下来进行编译,编译好后,在Linux中运行两个终端,分别运行以上两个程序,可以看到,运行fifo_read时,程序一直在每隔一秒读,然后我们在另一个终端输入:
$ ./fifo_write helloworld
可以看出fifo_read显示出“helloworld”,说明接受成功