LV020-有名管道
本文主要是进程通信——管道的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
一、有名管道
有名管道主要用于非亲缘的两个进程互相通信。
1. 有名管道创建
1.1 mkfifo()
1.1.1 函数说明
在linux下可以使用man 3 mkfifo命令查看该函数的帮助手册。
/* 需包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
/* 函数声明 */
int mkfifo(const char *pathname, mode_t mode);【函数说明】该函数用于创建一个有名管道,创建的有名管道文件也可以称为FIFO文件。
【函数参数】
pathname:const char *类型,有名管道文件存放的路径(包括管道名称),需要注意的是,这里不要放在共享目录下。mode:mode_t类型,有名管道文件权限,为8进制表示法。新建的有名管道文件权限会受到umask值影响,实际权限是mode - umaks。
Tips:什么是 umask?
在类
unix系统中,umask是确定掩码设置的命令,该掩码用来设定文件或目录的初始权限。umask确定了文件创建时的初始权限:c文件或目录的初始权限 = 文件或目录的最大默认权限 - umask权限文件初始默认权限为
0666,目录为0777,若用户umask为0002,则新创建的文件或目录在没有指定的情况下默认权限分别为0664、0775)。在
Linux下,我们可以使用umask命令来查看当前用户默认的umask值,同时也可以在umask命令后面跟上需要设置的umask值来重新设置umask。
【返回值】int类型,成功返回0,失败返回-1,并设置errno。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
/* 至少应该有的语句 */
mkfifo("/home/hk/MyFifo", 0666);【注意事项】
(1)文件路径不要定在虚拟机中Linux与Windows的共享目录中。
(2)该文件必须不存在。
1.1.2 使用实例
暂无。
2. 有名管道打开
2.1 open()
2.1.1 函数说明
在linux下可以使用man 2 open命令查看该函数的帮助手册。
/* 需包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* 函数声明 */
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);【函数说明】该函数。
【函数参数】
pathname:const char *类型,表示需要被打开的文件名(可包括路径名)。flags:int类型,表示打开文件所采用的操作。
(1)flag 常量常见可取的值
| O_RDONLY | 只读方式打开文件。 | 这三个参数互斥 |
| O_WRONLY | 可写方式打开文件。 | |
| O_RDWR | 读写方式打开文件。 | |
| O_CREAT | 如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限。 | |
| O_EXCL | 如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。 | |
| O_NOCTTY | 使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端。 | |
| O_TRUNC | 如文件已经存在,那么打开文件时先删除文件中原有数据。 | |
| O_APPEND | 以添加方式打开文件,所以对文件的写操作都在文件的末尾进行。 | |
| O_NONBLOCK | 以非阻塞的方式打开文件。 | |
【注意事项】前三个参数必须指定,且只能指定一个,后边的几个可以与前边搭配使用。
(2)flag常量与标准I/O文件打开权限关系、
| r | O_RDONLY | |
| r+ | O_RDWR | |
| w | O_WRONLY | O_CREAT | O_TRUNC, 0664 | |
| w+ | O_RDWR | O_CREAT | O_TRUNC, 0664 | |
| a | O_WRONLY | O_CREAT | O_APPEND, 0664 | |
| a+ | O_RDWR | O_CREAT | O_APPEND, 0664 | |
【返回值】int类型,成功时返回文件描述符(非负整数);出错时返回EOF(一般是-1)。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* 至少应该有的语句 */
int fd;/* 接收文件描述符 */
/* 以只写方式打开文件file.txt。如果文件不存在则创建,如果文件存在则清空 */
fd = open("file.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);【注意事项】该函数可以打开设备文件,但是不能创建设备文件。
2.2 使用说明
此函数是文件IO中的相关函数,在对有名管道文件进行操作时,需要注意以下几点:
(1)程序不能以O_RDWR(读写)模式打开FIFO文件进行读写操作,而其行为也未明确定义,因为如一个管道以读/写方式打开,进程可以读回自己的输出,同时我们通常使用FIFO只是为了单向的数据传递。
(2)open函数的打开方式还可以有以下情况的搭配:
open(const char *path, O_RDONLY); //1
open(const char *path, O_RDONLY | O_NONBLOCK);//2
open(const char *path, O_WRONLY); //3
open(const char *path, O_WRONLY | O_NONBLOCK);//4第二个参数中的选项O_NONBLOCK,选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。
(3)对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。
(4)对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。
(5)据完整性,如果有多个进程写同一个管道,使用O_WRONLY方式打开管道,如果写入的数据长度小于等于PIPE_BUF(4K),或者写入全部字节,或者一个字节都不写入,这样系统就可以确保数据决不会交错在一起。
3. 有名管道读写
3.1 读写函数
先说一下读写吧,有名管道的读写与无名管道一样,当打开之后,可以通过read函数读取管道中的数据,可以使用write函数来写入数据。
/* 需包含的头文件 */
#include <unistd.h>
/* 函数声明 */
ssize_t read(int fildes, void *buf, size_t nbyte); /* 数据读取 */
ssize_t write(int fildes, const void *buf, size_t nbyte); /* 数据写入 */3.2 使用实例
下边的实例实现了两个进程的通信。
3.2.1 fifo_read.c
【说明】对于以只读方式(O_RDONLY)打开的FIFO文件:如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。
/* 头文件 */
#include <stdio.h> /* perror fgets */
#include <unistd.h> /* write */
#include <string.h> /* strlen */
#include <sys/types.h>/* mkfifo open */
#include <sys/stat.h> /* mkfifo open */
#include <fcntl.h> /* open */
#include <stdlib.h> /* exit */
/* 主函数 */
int main(int argc, char *argv[])
{
int ret;
int fd;
char buff[32];
fd = open("/home/hk/MyFifo", O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}
printf("after open MyFifo!\n");
while(1)
{
memset(buff, 0, 32);
ret = read(fd, buff, 32);
if(ret > 0)
printf("read fifo=%s\n",buff);
else if(ret == 0)
exit(0);
}
return 0;
}3.2.2 fifo_write.c
【说明】对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。
/* 头文件 */
#include <stdio.h> /* perror fgets */
#include <unistd.h> /* write */
#include <string.h> /* strlen */
#include <sys/types.h>/* mkfifo open */
#include <sys/stat.h> /* mkfifo open */
#include <fcntl.h> /* open */
/* 主函数 */
int main(int argc, char *argv[])
{
int ret;
int fd;
char buff[32];
/* 1. 创建有名管道文件 */
ret = mkfifo("/home/hk/MyFifo", 0666);/* 注意不要创建在共享目录 */
if(ret < 0)
{
/* 若文件已存在,则打印出错信息即可,程序不需要结束 */
perror("mkfifo");
}
/* 2. 使用文件IO打开有名管道文件 */
fd = open("/home/hk/MyFifo", O_WRONLY|O_NONBLOCK);
if(fd < 0)
{
perror("open");
return -1;
}
printf("after open MyFifo!\n");
while(1)
{
fgets(buff, 32, stdin);
write(fd, buff, strlen(buff));
}
return 0;
}3.2.3 Makefile
CC = gcc
DEBUG = -g -O2 -Wall
CFLAGS += $(DEBUG)
# 所有.c文件去掉后缀
TARGET_LIST = ${patsubst %.c, %, ${wildcard *.c}}
all : $(TARGET_LIST)
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: all clean clean_o
clean : clean_o
@rm -vf $(TARGET_LIST)
clean_o :
@rm -vf *.o3.2.4 测试效果
在终端执行以下命令编译程序:
make # 编译程序,将会生成两个可执行程序
./fifo_write # 一个终端向管道写入数据
./fifo_read # 在另一个终端读取管道数据然后,两个终端都会阻塞等待另一个进程打开有名管到文件,之后我们在运行写入管道可执行程序的终端中输入数据,便可以在另一个终端中收到了。
【注意事项】需要注意的是,尽量不要设置不阻塞,否则现象并不明显,很可能会出现以下问题:
open: No such device or address