管道通信——FIFO的代码实现
一、用到的函数
umask
linux中的 umask 函数主要用于:在创建新文件或目录时 屏蔽掉新文件或目录不应有的访问允许权限。
文件的访问允许权限共有9种,分别是:r w x r w x r w x(它们分别代表:用户读 用户写 用户执行 组读 组写 组执行 其它读 其它写 其它执行)
其实这个函数的作用,就是设置允许当前进程创建文件或者目录最大可操作的权限,比如这里设置为0,它的意思就是0取反再创建文件时权限相与,也就是:(~0) & mode 等于八进制的值0777 & mode了,这样就是给后面的代码调用函数mkdir给出最大的权限,避免了创建目录或文件的权限不确定性
第一位代表了一项特别的安全特性,叫作粘着位(sticky bit),后面的3位表示文件或目录对应的umask八进制值。要理解umask是怎么工作的,得先理解八进制模式的安全性设置。
八进制模式的安全性设置先获取这3个rwx权限的值,然后将其转换成3位二进制值,用一个八进制值来表示。在这个二进制表示中,每个位置代表一个二进制位。因此,如果读权限是唯一置位的权限,权限值就是r--,转换成二进制值就是100,代表的八进制值是4。下表列出了可
能会遇到的组合。
S_IFIFO|0666
S_IFIFO|0666指明创建有名管道且存取权限为0666,即创建者/其同组用户/其他用户均可读可写
mknod(FIFO_FILE,S_IFIFO|0666,0);
函数mknod参数中path为创建的命名管道的全路径名:mod为创建的命名管道的模式,指明其存取权限;dev为设备值,该值取决于⽂件创建的种类,它只在创建设备⽂件时才会⽤到。这个函数调⽤成功都返回0,失败返回-1
perror
C 库函数 void perror(const char *str) 把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 str,后跟一个冒号,然后是一个空格
str -- 这是 C 字符串,包含了一个自定义消息,将显示在原本的错误消息之前
该函数不返回任何值
exit是一个函数,进程退出时会有一个值,exit函数的参数就是指明进程退出的返回值,操作系统根据这个值来判断是否是正常退出。比如说:
exit(1)是异常退出,比如你的代码在出现不应该出现的分枝,要求终止程序的时候就用exit(1)
exit(0)是正常退出,就是你认为代码一切正常的时候的退出
open
open函数属于Linux中系统IO,用于“打开”文件,代码打开一个文件意味着获得了这个文件的访问句柄。
int fd = open(参数1,参数2,参数3);
int fd = open(const char *pathname,int flags,mode_t mode);
1.句柄(file descriptor 简称fd)
首先每个文件都属于自己的句柄,例如标准输入是0,标准输出是1,标准出错是2。
每打开一个文件就会返回句柄来操作这个文件,一般是从3开始,然后4,5,6一直下去。
close(fd)之后句柄就返回给系统,例如打开一个文件后fd是3,close之后再打开另外一个文件也还是3,但代表的文件不一样了。
2.使用open前需要先包含头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
3.参数1(pathname)
即将要打开的文件路径,例如:“a.txt”当前目录下的a.txt文件
4.参数2(flags)
flags分为两类:主类,副类
主类:O_RDONLY 以只读方式打开 / O_WRONLY 以只写方式打开 /O_RDWR 以可读可写方式打开
三这是互斥的
副类:
O_CREAT 如果文件不存在则创建该文件
O_EXCL 如果使用O_CREAT选项且文件存在,则返回错误消息
O_NOCTTY 如果文件为终端,那么终端不可以调用open系统调用的那个进程的控制终端
O_TRUNC 如果文件已经存在泽删除文件中原有数据
O_APPEND 以追加的方式打开
主副可以配合使用,例如:O_RDWR|O_CREAT|O_TRUNC
5.参数3(mode)
mode:如果文件被新建,指定其权限未mode
mode是八进制权限码,0777表示文件所有者 该文件用户组 其他用户都有可读可写可执行权限
ssize_t
ssize_t是有符号整型,在32位机器上等同与int,在64位机器上等同与long int
read()
函数定义:ssize_t read(int fd, void * buf, size_t count);
函数说明:read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中。
返回值:返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据。若参数count 为0, 则read()不会有作用并返回0。
注意:read时fd中的数据如果小于要读取的数据,就会引起阻塞。
fflush
在使用多个输出函数连续进行多次输出时,有可能发现输出错误。因为下一个数据在上一个数据还没输出完毕,还在输出缓冲区中时,下一个printf就把另一个数据加入输出缓冲区,结果冲掉了原来的数据,出现输出错误。 在 printf();后加上fflush(stdout); 强制马上输出,避免错误
close (fd)
每打开一个文件就会返回句柄来操作这个文件,一般是从3开始,然后4,5,6一直下去。
close(fd)之后句柄就返回给系统,例如打开一个文件后fd是3,close之后再打开另外一个文件也还是3,但代表的文件不一样了
write()
函数定义:ssize_t write (int fd, const void * buf, size_t count);
函数说明:write()会把参数buf所指的内存写入count个字节到参数放到所指的文件内。
返回值:如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中
二、代码实现
read端代码
1 #include<stdlib.h> 2 #include<stdio.h> 3 #include<sys/types.h> 4 #include<sys/stat.h> 5 #include<fcntl.h> 6 #include<errno.h> 7 #define PATH "./fifo" 8 #define SIZE 128 9 int main() 10 { 11 umask(0); 12 if (mkfifo (PATH,0666|S_IFIFO) == -1) 13 { 14 perror ("mkefifo error"); 15 exit(0); 16 } 17 int fd = open (PATH,O_RDONLY); 18 if (fd<0) 19 { 20 printf("open fd is error\n"); 21 return 0; 22 } 23 24 char Buf[SIZE]; 25 while(1){ 26 ssize_t s = read(fd,Buf,sizeof(Buf)); 27 if (s<0) 28 { 29 perror("read error"); 30 exit(1); 31 } 32 else if (s == 0) 33 { 34 printf("client quit! i shoud quit!\n"); 35 break; 36 } 37 else 38 { 39 Buf[s] = '\0'; 40 printf("client# %s ",Buf); 41 fflush(stdout); 42 } 43 } 44 close (fd); 45 return 3; 46 }
write端代码
1 #include<stdlib.h> 2 #include<stdio.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<string.h> 7 #include<errno.h> 8 #include<fcntl.h> 9 10 #define PATH "./fifo" 11 #define SIZE 128 12 int main() 13 { 14 int fd = open(PATH,O_WRONLY); 15 if (fd < 0) 16 { 17 perror("open error"); 18 exit(0); 19 } 20 21 char Buf[SIZE]; 22 while(1) 23 { 24 printf("please Enter#:"); 25 fflush(stdout); 26 ssize_t s = read(0,Buf,sizeof(Buf)); 27 if (s<0) 28 { 29 perror("read is failed"); 30 exit(1); 31 } 32 else if(s==0) 33 { 34 printf("read is closed!"); 35 return 1; 36 } 37 else{ 38 Buf[s]= '\0'; 39 write(fd,Buf,strlen(Buf)); 40 } 41 } 42 return 0; 43 }
三、实例展示
write端
read端
四、存在的问题
数据溢出了怎么办?
当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。
这个过程用到了fflush函数。
在 printf();后加上fflush(stdout); 强制马上输出,避免错误