无名管道主要用于有亲缘关系的进程通信,其位于外存区域,但在文件系统中不可见。在实际应用中,进程通信往往发生在无关进程之间,此种情形下,若仍想使用管道,则必须使用有名管道,也称命名管道或FIFO文件。这种类型的管道在文件系统中可见,创建时需要指定具体路径和文件名,管道创建之后,可用ls命令查看。
在终端中命令行中创建有名管道的方法是使用mkfifo命令,如在/tmp目录下创建名为fifoName的有名管道文件,执行下面命令,该命令完成后,可转到相应路径下使用ls命令查看: # mkfifo /tmp/fifoName 呵呵,FIFO创建好了,我们就可以尝试使用了。首先,创建两个读写进程,其中一个放在后台执行。 # echo "Hello, FIFO!" > /tmp/fifoName & 该命令显示的结果为 [1] 6242 该结果中的6242为后台运行进程的PID号,该数值不一定,根据系统分配的PID号会有相应的值。 然后,创建读取进程,读取写入进程所写入的信息。 # cat < /tmp/fifoName 该命令显示的结果为 Hello, FIFO! [1]+ Done echo "Hello, FIFO!" >/tmp/fifoName 说明两个进程已同时完成各自的工作。
从上面的例子可以看出,FIFO完成了进程通信任务,而且读写两进程是同时完成工作的。操作系统理论指出,对共享区域的操作需要有互斥机制,这种互斥机制在Linux FIFO操作中的具体表现就是操作进程的阻塞。上面的例子中,写进程的操作被放在了后台,所以无法看到阻塞的直接效果,而是输出了一个后台运行的进程号就被阻塞在了后台,直到读进程开始操作时,FIFO中的数据被传递给读进程并显示出来,同时,写进程解除阻塞,同时结束。若想看到阻塞的直接效果,只需要在创建写进程时,不将其放在后台即可,此时的现象为命令行呈现假死机,即进程阻塞状态。 根据例子进行归纳后不难得出结论,只有FIFO两端都有进程工作时,进程才不会阻塞,否则,一端的进程一定会阻塞等待,直至另一进程前来进行操作。
在程序中使用有名管道时,需要用mkfifo系统调用创建FIFO文件,其函数原型为: int mkfifo(const char *filename, mode_t mode) 该系统调用的功能是创建名为filename的FIFO文件,该文件具有mode访问模式,该mode为一代码,与chmod命令中指定的mode代码含义相同。 FIFO文件创建完成后,要使用open系统调用打开该文件方可使用,open函数原型为: int open(const char *path, int oflags) 其中,path参数指定文件的具体路径和文件名,oflags参数指定了打开方式,传统打开方式有O_RDONLY、O_WRONLY和O_RDWR三种,而对于FIFO文件来说,O_RDWR方式不可用,因此,若想实现进程之间的双向通信,需要使用两条管道,即创建两个FIFO文件方可进行。此外,还有另外一个辅助的O_NONBLOCK标志常量,使用时与O_RDONLY或O_WRONLY进行逻辑或运算后作为oflags参数值,若使用该标志,则将以非阻塞方式打开,否则,默认为使用阻塞方式打开。 FIFO文件打开后,就可以使用read/write系统调用进行读写了,可以看出,FIFO文件将进程间通信映射为对文件的读写。read/write系统调用的函数原型如下: size_t read(int fildes, const void *buf, size_t nbytes) size_t write(int fildes, const void *buf, size_t nbytes) 这两个函数的返回值是进程之间传输的字节数,参数fildes为文件描述符,buf为通信缓冲区,nbytes为需要传输的字节数。这两个函数的功能为将buf中的前nbytes数据读出/写入fildes文件描述符所指代的文件进行通信交互。 FIFO使用完毕后,要使用close系统调用关闭FIFO文件通信,使用unlink系统调用删除FIFO文件。 下面给出一个有意义的例子,使用有名管道(FIFO文件)实现一个进程间通信的模板类。
// FifoCommunication.h #ifndef FIFO_COMMUNICATION_H_ #define FIFO_COMMUNICATION_H_
#include<iostream> #include<iomanip> #include<stdio.h> #include<fcntl.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/stat.h> #include<sys/types.h>
enum FifoResult { TRANSPORT_SUCCESS, TRANSPORT_FAILURE, CREATE_FAILURE }; const FIFO_NAME_MAX_LENGTH = 256;
template <class MESSAGE_TYPE> class FifoCommunication { public: FifoCommunication(char*, char*); ~FifoCommunication(); FifoResult Read(MESSAGE_TYPE*, int, int*); FifoResult Write(MESSAGE_TYPE*, int, int*); protected: char fifoNameRead[FIFO_NAME_MAX_LENGTH]; char fifoNameWrite[FIFO_NAME_MAX_LENGTH]; MESSAGE_TYPE msgReadTempStore; MESSAGE_TYPE msgWriteTempStore; };
#endif
// FifoCommunication.cpp #include "FifoCommunication.h"
template <class MESSAGE_TYPE> FifoCommunication<MESSAGE_TYPE>::FifoCommunication(char *fifoRead, char *fifoWrite) { strcpy(fifoNameRead, fifoRead); strcpy(fifoNameWrite, fifoWrite);
umask(0); if(mkfifo(fifoNameRead, 0777) == -1 && mkfifo(fifoNameWrite, 0777) == -1) { std::cerr<<std::endl<<"FIFO Creation Error!"<<std::endl; exit(CREATE_FAILURE); } }
template <class MESSAGE_TYPE> FifoCommunication<MESSAGE_TYPE>::~FifoCommunication() { unlink(fifoNameRead); unlink(fifoNameWrite); }
template <class MESSAGE_TYPE> FifoResult FifoCommunication<MESSAGE_TYPE>::Read(MESSAGE_TYPE *msgBuffer, int nBufferSize, int *pBytesTransport) { int FifoFd, ReadRes;
bzero((char*)&msgReadTempStore, sizeof(msgReadTempStore));
FifoFd = open(fifoNameRead, O_RDONLY); if(FifoFd == -1) { *pBytesTransport = 0; return TRANSPORT_FAILURE; } else { ReadRes = read(FifoFd, (char*)&msgReadTempStore, nBufferSize); if(ReadRes == -1) { (void)close(FifoFd); *pBytesTransport = 0; return TRANSPORT_FAILURE; } else { (void)close(FifoFd);
*pBytesTransport = ReadRes; memcpy((char*)msgBuffer,(char*)&msgReadTempStore,ReadRes);
return TRANSPORT_SUCCESS; } } }
template <class MESSAGE_TYPE> FifoResult FifoCommunication<MESSAGE_TYPE>::Write(MESSAGE_TYPE *msgData, int nMessageSize, int *pBytesTransport) { int FifoFd, WriteRes;
bzero((char*)&msgWriteTempStore, sizeof(msgWriteTempStore)); memcpy((char*)&msgWriteTempStore,(char*)msgData,nMessageSize);
FifoFd = open(fifoNameWrite, O_WRONLY); if(FifoFd == -1) { *pBytesTransport = 0; return TRANSPORT_FAILURE; } else { WriteRes = write(FifoFd, (char*)&msgWriteTempStore, nMessageSize); if(WriteRes == -1) { (void)close(FifoFd); *pBytesTransport = 0; return TRANSPORT_FAILURE; } else { (void)close(FifoFd); *pBytesTransport = WriteRes; return TRANSPORT_SUCCESS; } } } |