简介
无名管道在这篇笔记中有详细的介绍,包括半双工的pipe
和全双工的socketpair
;两者的共同特点是只能用于有亲缘关系的进程,比如父子进程。如果想要在任意进程间使用管道进行通信,则需要借助有名管道,即mkfifo
来实现:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
成功返回0,失败返回-1.
pathname
:FIFO的名称mode
:权限,在stat.h
头文件中指定的,默认是O_CREATE | O_EXCL
参数;如果已经存在,则返回EEXIST
错误。也就是说,存在的情况下,使用open函数进程打开。
在使用FIFO时,必须先写再读,如果先读,则进程阻塞,这可能会产生死锁,一定要注意这点。
还有一个重要的地方:管道和FIFO的write都是原子操作,不必加锁,直接进行write操作即可。
与文件的不同之处
FIFO文件在使用上和普通文件有相似之处,但是也有不有
不同之处:
- 读取fifo文件的进程只能以”RDONLY”方式打开fifo文件。
- 写fifo文件的进程只能以”WRONLY”方式打开fifo
- fifo文件里面的内容被读取后,就消失了。但是普通文件里面的内容读取后还存在。
作者:萌小宏
来源:CSDN
原文:https://blog.csdn.net/best_fiends_zxh/article/details/52923560
作为守护进程的实例
我们开启一个后台的守护进程,而且该守护进程拥有一个FIFO。客户端通过FIFO向守护进程发送数据。注意,客户可以很容易向服务器发送数据,但是服务器却很难通过原始的FIFO发送数据。更好的解决办法是,守护进程维护的FIFO仅仅作为一个消息通信装置,用户把自己的请求(比如新建的FIFO名称)发送给守护进程,然后守护进程通过该消息寻找指定的FIFO,然后进行通信,给出一个参考图。
代码实例
注意,这里为了方便,把数据改成了可读可写的类型。实际过程中,可以根据需要选择权限,但是注意,不要弄错了,否则会永远阻塞。。
Server
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <strings.h>
#include <unistd.h>
#include <time.h>
#include <cstring>
const char *PATH = "/home/erick/Desktop/fifo.serv";
bool stop_server = false;
void sig_handler(int sig) {
if (sig != SIGINT) {
return;
}
stop_server = true;
puts("stop server...");
}
int main() {
if ((mkfifo(PATH, 0666) < 0) && (errno != EEXIST)) {
perror("mkfifo() error\n");
exit(EXIT_FAILURE);
}
if ((open(PATH, O_CREAT | O_EXCL | O_RDWR, 7777) < 0) && (errno != EEXIST)) {
perror("pipe file does not exists\n");
exit(EXIT_FAILURE);
}
int readfd = open(PATH, O_RDWR);
int dummyfd = open(PATH, O_RDWR); // 永远不使用
struct sigaction sa;
bzero(&sa, sizeof(sa));
sa.sa_flags = SA_RESTART;
sa.sa_handler = sig_handler;
if (sigaction(SIGINT, &sa, nullptr) < 0) {
perror("sigaction() error\n");
exit(EXIT_FAILURE);
}
while (!stop_server) {
char buf[1024];
memset(buf, 0, sizeof(buf));
read(readfd, buf, 1024);
printf("get child req: %s\n", buf);
int writefd = open(buf, O_RDWR);
if (writefd < 0) {
printf("open %s failed\n", buf);
exit(EXIT_FAILURE);
}
// 获取当前系统时间
time_t t = time(nullptr);
struct tm tm = *localtime(&t);
snprintf(buf, 1024, "now: %d-%d-%d %d:%d:%d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec);
write(writefd, buf, strlen(buf));
sleep(1);
}
close(readfd);
close(dummyfd);
exit(EXIT_SUCCESS);
}
服务器发送给客户端当前的系统时间:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <signal.h>
const char *PATH = "/home/erick/Desktop/fifo.serv";
bool stop_server = false;
void sig_handler(int sig) {
if (sig != SIGINT) {
return;
}
stop_server = true;
puts("stop server...");
}
int main() {
// 有名管道设置为自己的进程号
char path[100];
snprintf(path, 100, "/home/erick/Desktop/fifo.%ld", (long) getpid());
if (mkfifo(path, 0666) < 0) {
perror("mkfifo() error\n");
exit(EXIT_FAILURE);
}
if ((open(path, O_CREAT | O_EXCL | O_RDWR, 7777) < 0) && (errno != EEXIST)) {
perror("pipe file does not exists\n");
exit(EXIT_FAILURE);
}
puts("before");
int readfd = open(path, O_RDWR);
puts("readfd");
int serverfd = open(PATH, O_RDWR);
if (serverfd < 0) {
perror("open() error\n");
exit(EXIT_FAILURE);
}
puts("server");
struct sigaction sa;
bzero(&sa, sizeof(sa));
sa.sa_flags = SA_RESTART;
sa.sa_handler = sig_handler;
if (sigaction(SIGINT, &sa, nullptr) < 0) {
perror("sigaction() error\n");
exit(EXIT_FAILURE);
}
if (write(serverfd, path, strlen(path)) < 0) {
perror("write error\n");
exit(EXIT_FAILURE);
}
char buf[1024];
if (read(readfd, buf, 1024) < 0) {
perror("read() error\n");
exit(EXIT_FAILURE);
}
printf("system time: %s\n", buf);
exit(EXIT_SUCCESS);
}