以一个简单的客户—服务器的例子说明管道和FIFO。
对于命名管道的操作与文件操作非常相似,对文件操作中使用的函数read(),write(),close()等函数都可以是用来对管道进行操作。
命名管道的创建:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int mkfifo(const char *pathname, mode_t mode);
//mode S_IRUSR S_IWUSR
//mode O_CREAT | O_EXCL
if (access(write_fifo_name,F_OK) == -1)
{
//mkfifo(write_fifo_name, S_IRUSR|S_IWUSR) == -1; //创建命名管道
}
该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字,第二个参数与打开普通的open()函数中的mode参数相同,如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。
管道仅需要创建而不需要打开,因为使用它们的进程通过继承获得了管道的文件描述符,但命名管道则需要打开,因为使用它们的进程可以没有任何关系,对命名管道的打开通常使用文件打开函数open().
write_fd = open(write_fifo_name,O_WRONLY) == -1;
FIFO 常用的打开方式:
O_RDONLY:以只读并且阻塞的方式打开,如果已经有相应进程为写而打开该FIFO,则打开操作将成功返回,否则,可能阻塞直到有相应进程为写而打开该FIFO。
O_WRONLY:以只写并且阻塞的方式打开,如果已经有相应进程为读而打开该FIFO,则打开操作将成功返回,否则,可能阻塞直到有相应进程为读而打开该FIFO。
O_RDONLY|O_NONBLOCK:以只读并且非阻塞得方式打开,无论是否已经具有相应进程为写进程而打开该FIFO,均立即成功返回。
O_WRONLY|O_NONBLOCK:以只写并且非阻塞得方式打开,立即返回,如果已经有相应进程为读而打开该FIFO,则可以根据返回得文件描述符进行写操作,如果没有进程以只读方式打开,则返回错误-1.
如果以O_RDONLY模式打开FIFO,且已经有相应进程以写模式打开FIFO,那么打开操作将成功返回,否则,打开该FIFO的进程将可能被阻塞直到有相应进程以写模式打开FIFO(当前打开操作设置了阻塞标志(O_NONBKOCK))或成功返回(当前打开操作没有设置阻塞标志)。
如果以O_WRONLY模式打开FIFO;……
如果有进程以写模式打开了FIFO,但该FIFO中没有数据,那么读FIFO的进程将被阻塞(打开操作设置了阻塞标志),或返回-1(当前打开操作没有设置阻塞标志)
如果打开的FIFO设置了阻塞标志,对于读操作来说,造成阻塞的原因有两种:当前FIFO内有数据,但有其他进程在读这些数据,另外就是FIFO内没有数据,解除阻塞的则是FIFO中有新的数据写入(不管写入的数据量有多少,也不管读操作请求多少数据,都将解阻塞)
以读模式打开的FIFO的阻塞标志只对打开进程的第一个读操作有效,如果该进程有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其他将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样不再阻塞(此时,读操作返回0)
如果没有进程以写模式打开FIFO,则设置了阻塞标志的读操作会阻塞。
如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作会返回FIFO中现有的数据量。
如果写打开时设置了阻塞标志,一个进程对FIFO的写操作可能被阻塞,当要写入FIFO的数据量 不大于PIPE_BUF时,Linux将保证写入的原子性,当FIFO的空闲缓冲区不足以容纳要写入的字节数时,写进程将被阻塞,直到缓冲区中能够容纳要写入的字节数时才被唤醒写入没有写入的数据字节,当要写入FIFO的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性,FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
如果写打开时没有设置阻塞标志,当要写入FIFO的数据量大于PIPE_BUF时,Linux将不再保证写入原子性,在写满所有FIFO空闲缓冲区后,写操作返回,当要写入FIFO的数据量不大于PIPE_BUF时,Linux将保证写入的原子性,如果当前FIFO空想缓冲区能够容纳请求写入的字节数,写完成后成功返回,如果当前FIFO空闲缓冲区不能容纳请求写入的字节数,则返回错误。
1.建立一个头文件
#pragma once
#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/wait.h>
#includ<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
using namespace std;
const char *write_fifo = "write_fifo";
const char *read_fifo = "read_fifo";
2.建立一个服务器的文件,即vim ser.cpp
#includ"utili.h"
int main()
{
int res = mkfifo(write_fifo,0_CREAT | 0_excl|0755);
if(res != 0)
{
perror("mkfifo");
return -1;
}
int write_fd//fd[1]文件描述符
write_fd = open(write_fifo,0_WRONLY);//open()打开的是一个路径
if(write_fd == -1)
{
perror("open write_fifo");
unlink(write_fifo);//删除write_fifo
return -1;
}
int read_fd;
while(read_fd = open(read_fifo,O_RDONLY))
{
sleep(1);
}
printf("Client Connect Servicer OK.\n");
char sendbuf[BUFFER_SIZE];
char recvbuf[BUFFER_SIZE];
while(1)//无限的循环
{
printf("Ser :>");
scanf("%s",sendbuf);
write(write_fd,sendbuf,strlen(endbuf)+1);
read(read_fd,recvbuf,BUFFER_SIZE);
printf("Cli:> %s\n",recvbuf);
}
unlink(write_fifo);
return 0;
}
3.建立一个客户端的文件,vim cli.cpp
#include"utili.h"
int main()
{
int read_fd = open(write_fifo,O_RDONLY);
if(read_fd == -1)
{
perror("open");
return -1;
}
int res = mkfifo(read_fifo,O_CREAT|O_EXCL|0755);
if(res != 0)
{
perror("mkfifo");
return -1;
}
int write_fd = open(read_fifo,O_WRONLY);
if(write_fd == -1)
{
perror("open read_fifo");
unlink(read_fifo);
return -1;
}
char sendbuf[BUFFER_SIZE];
char recvbuf[BUFFER_SIZE];
while(1)
{
read(read_fd,recvbuf,BUFFER_SIZE);
printf("Ser:> %s\n",recvbuf);
printf("Cli:> ");
scanf("%s",sendbuf);
write(write_fd,sendbuf,strlen(sendbuf)+1);
}
return 0;
}
4.建立一个Makefile vim Makefile
all:ser cli
ser:ser.cpp
g++ ser.cpp -o ser
cli:cli.cpp
g++ cli.cpp -o cli
clean:
rm ser
rm cli
对全部文件进行编译,make
出现:
分开运行 ./ser
./cli