首页 > 编程知识 正文

UNIX网络编程 卷2 笔记 FIFO,unix网络编程卷2读书笔记

时间:2023-05-05 09:02:18 阅读:276655 作者:423

FIFO本意是指先进先出(first in,first out),在本系列文章中,FIFO指的是有名管道。与管道不同的是,FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO。

#include <sys/types.h>#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);FIFO由mkfifo函数创建,pathname表示路径名,mode类似open函数的mode参数,指定文件权限位。在本系列文章中,mode参数的值如下:

/*默认的文件权限,文件创建者用户可读可写,用户组只读,其他用户只读*/#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)如果该FIFO已经存在,则函数返回失败。在这种情况下,可以使用open函数打开FIFO。

本节我们使用FIFO代替管道来实现上节的客户-服务器程序,如下图所示:


client和server函数的实现与上节相同,main函数代码如下:

#include "unpipc.h"/*两个FIFO的路径名*/#define FIFO1 "/tmp/fifo.1"#define FIFO2 "/tmp/fifo.2"void client(int, int), server(int, int);int main(int argc, char **argv){int readfd, writefd;pid_t childpid;/*创建两个FIFO*/if ((mkfifo(FIFO1, FILE_MODE)) < 0 && (errno != EEXIST))err_sys("can't create %s", FIFO1);if ((mkfifo(FIFO2, FILE_MODE)) < 0 && (errno != EEXIST)) {unlink(FIFO1);err_sys("can't create %s", FIFO2);}if ((childpid = Fork()) == 0) {/*服务器以读的方式打开FIFO1,以写的方式打开FIFO2*/readfd = Open(FIFO1, O_RDONLY, 0);writefd = Open(FIFO2, O_WRONLY, 0);server(readfd, writefd);exit(0);}/*客户以写的方式打开FIFO1,以读的方式打开FIFO2*/writefd = Open(FIFO1, O_WRONLY, 0);readfd = Open(FIFO2, O_RDONLY, 0);client(readfd, writefd);Waitpid(childpid, NULL, 0);Close(readfd);Close(writefd);/*删除两个FIFO*/Unlink(FIFO1);Unlink(FIFO2);exit(0);}

在这里,我们需要就管道和FIFO的打开、读出和写入更为详细地描述它们的某些属性。

首先,一个描述符能以两种方式被设置成非阻塞。

    1. 调用open时指定O_NONBLOCK标志。

    2. 如果一个描述符已经打开,可以调用fcntl函数指定O_NONBLOCK标志。

阻塞(默认设置)和O_NONBLOCK标志设置时对管道和FIFO的打开,读出和写入的影响如下表所示:


管道或FIFO的读出与写入还有如下的规则:

1. 如果请求读出的数据量多于管道或FIFO中当前的数据量,那么只返回这些数据量。

2. 如果请求写入的数据量小于或等于PIPE_BUF(内核中定义为4096),那么write操作保证是原子的。

3. O_NONBLOCK标志对write操作的原子性没有影响,它影响write函数的返回值。

    如果设置了O_NONBLOCK标志

        如果写入的字节数小于或等于PIPE_BUF

            如果该管道或FIFO有足够的空间容纳数据

                所有的数据立即写入成功,write返回成功写入的字节数

            否则

                write返回EAGAIN错误

        否则(如果写入的字节数大于PIPE_BUF)

           如果该管道或FIFO至少有1字节空间

                写入管道或FIFO能容纳的字节数,该数目作为write的返回值。

            否则

                write返回EAGAIN错误。

4. 如果向一个没有为读打开着的管道或FIFO写入,内核将产生一个SIGPIPE信号。

下面给出一个使用FIFO的单个服务器进程和多个客户进程通信的例子。


服务器创建一个名为/tmp/fifo.serv的FIFO。客户创建一个名为/tmp/fifo.pid的FIFO(pid为进程ID号),然后打开服务器FIFO,将进程pid和一个从标准输入读取的文件路径写入服务器FIFO。服务器从自己的FIFO读取到数据后,解析出pid(根据pid知道客户FIFO的名字)和文件路径,打开客户FIFO,读取文件内容写入客户FIFO。最后客户从自己的FIFO读取文件内容(或出错信息)输出到标准输出。

服务器的代码如下:

#include "unpipc.h"#define SERV_FIFO "/tmp/fifo.serv"int main(int argc, char **argv){int readinfo, writeinfo, dummyfd, fd;char *ptr, buff[MAXLINE], fifoname[MAXLINE];pid_t pid;ssize_t n;/*创建服务器FIFO*/if ((mkfifo(SERV_FIFO, FILE_MODE) < 0) && errno != EEXIST)err_sys("can't create %s", SERV_FIFO);/*以读的方式打开FIFO,阻塞直到有客户以写的方式打开该FIFO*/readinfo = Open(SERV_FIFO, O_RDONLY, 0);printf("client connectn");/*以写的方式打开FIFO*/writeinfo = Open(SERV_FIFO, O_WRONLY, 0);/*从FIFO读取内容*/while ((n = Readline(readinfo, buff, MAXLINE)) > 0) {printf("get request: %sn", buff);/*去掉换行符*/if (buff[n - 1] == 'n')n--;buff[n] = '';/*解析出pid*/if ((ptr = strchr(buff, ' ')) == NULL) {err_msg("bogus request: %s", buff);continue;}*ptr++ = 0;pid = atol(buff);snprintf(fifoname, sizeof(fifoname), "/tmp/fifo.%ld", (long)pid);/*以写的方式打开客户FIFO, 阻塞直到客户以读的方式打开该FIFO*/if ((writeinfo = open(fifoname, O_WRONLY)) < 0) {err_msg("cannot open: %s", fifoname);continue;}/*打开客户请求的文件,发生错误通知客户*/if ((fd = open(ptr, O_RDONLY)) < 0) {snprintf(buff + n, sizeof(buff) - n, ":can't open, %s", strerror(errno));n = strlen(ptr);Write(writeinfo, buff, n);Close(writeinfo);} else {/*读取文件内容发送给客户*/while ((n = Read(fd, buff, MAXLINE)) > 0)Write(writeinfo, buff, n);Close(fd);/*关闭客户FIFO*/Close(writeinfo);}}exit(0);}注意到服务器在创建FIFO后,一次以读的方式打开,又再一次以写的方式打开。为什么还要再以写的方式打开?这是因为当客户关闭服务器的FIFO时,如果此时FIFO不是打开来写,根据上图4-21,读FIFO会返回0,这会导致服务器程序退出。代码中Readline函数在这里实现。

客户程序的代码如下:

#include "unpipc.h"#define SERV_FIFO "/tmp/fifo.serv"int main(int argc, char **argv){int readinfo, writeinfo;size_t len;ssize_t n;char *ptr, fifoname[MAXLINE], buff[MAXLINE];pid_t pid;/*创建一个与进程PID关联的FIFO*/pid = getpid();snprintf(fifoname, sizeof(fifoname), "/tmp/fifo.%ld", (long)pid);if ((mkfifo(fifoname, FILE_MODE) < 0) && (errno != EEXIST))err_sys("can't create %s", fifoname);snprintf(buff, sizeof(buff), "%ld ", (long)pid);len = strlen(buff);ptr = buff + len;/*从标准输入获取文件路径*/Fgets(ptr, MAXLINE - len, stdin);len = strlen(buff);/*写入进程pid和文件路径到服务器FIFO*/writeinfo = Open(SERV_FIFO, O_WRONLY, 0);Write(writeinfo, buff, len);/*以读的方式打开FIFO,阻塞直到服务器以写的方式打开该FIFO*/readinfo = Open(fifoname, O_RDONLY, 0);/*读取文件内容*/while ((n = Read(readinfo, buff, MAXLINE)) > 0)Write(STDOUT_FILENO, buff, n);Close(readinfo);Unlink(fifoname);exit(0);}

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。