版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rikeyone/article/details/88969454
接口
#include <sys/stat.h>
int mkfifo(const char *path, mode_t mode);
int mkfifoat(int fd, const char *path, mode_t mode);
成功返回0,出错返回-1
FIFO也叫命名管道,可以由不相关的进程使用。它会在文件系统中创建一个FIFO类型的文件,存在于路径名path的位置上。FIFO支持同时拥有多个写者,为了保证不同写进程之间的数据不存在交叉,必须使每次写入的操作是原子的,因此需要确保不同进程每次写入的数据都要少于PIPE_BUF,这样才能保证数据不存在交叉。
特别注意:
必须FIFO的两端存在读者和写者,数据才能传递过去。如果是以只读打开FIFO文件,会等待对方以写打开,如果是以只写打开FIFO文件,会等待对方以只读打开。如果以读写打开,不会进行等待,此时写入的数据没有读者存在,数据会丢失。
示例
fifo server:
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include "include/debug.h"
#define CMD_SIZE (1024 + 50) // must < PIPE_BUF to make it read and write atomic
static char command[CMD_SIZE];
void sigpipe_handler(int signo)
{
pr_err("client has exited abnormally!\n");
}
int signal_setup(void)
{
struct sigaction act, oact;
act.sa_handler = sigpipe_handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
if (sigaction(SIGPIPE, &act, &oact) < 0)
err_exit("sigaction error\n");
return 0;
}
int fifo_setup(void)
{
int ret;
unlink("/tmp/fifo_server");
ret = mkfifo("/tmp/fifo_server", (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH));
if (ret < 0)
err_exit("server fifo create error\n");
return ret;
}
int check_client_fifo(char *path)
{
int ret;
struct stat st;
ret = stat(path, &st);
if (ret == -1) {
pr_info("%s don't exsit, create it by server!\n", path);
ret = mkfifo(path, (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH));
if (ret < 0)
pr_err("client fifo create error:%s\n", path);
}
return ret;
}
int readn(int fd, char *ptr, int size)
{
int ret;
int len = size;
char *buf = ptr;
while(len > 0) {
ret = read(fd, buf, len);
if (ret < 0) {
if (errno == EINTR)
continue;
else {
pr_err("read error\n");
return -1;
}
} else if (ret == 0) {
pr_info("no client exist!\n");
break;
} else {
len -= ret;
buf += ret;
}
}
return size - len;
}
int main_receive(int fd, char *ptr, int max)
{
int ret, len;
char buf[5] = {0};
ret = readn(fd, buf, 4);
if (ret < 0)
return -1;
len = atoi(buf);
pr_info("receive length:%d\n", len);
if (len > CMD_SIZE)
return -1;
ret = readn(fd, ptr, len);
if (ret < 0)
return -1;
return len;
}
/* pid:msg */
void split_command(char *cmd, int size, char **pid, char **msg)
{
char *p;
p = strtok(cmd, ":");
if (p) {
pr_info("%s pid:%s\n", __func__, p);
*pid = p;
}
p = strtok(NULL, ":");
if (p) {
pr_info("%s msg:%s\n", __func__, p);
*msg = p;
}
}
void handle_msg(char *pid, char *msg)
{
char path[1024];
if (!pid)
return;
sprintf(path, "/tmp/fifo_server_%s", pid);
if (!strcmp(msg, "disconnect")) {
unlink(path);
} else {
/*
* any other parser for msg
*/
}
}
int send_msg_ack(char *pid, char *msg)
{
int ret, fd;
char buf[1024];
char path[1024];
if (!pid)
return -1;
sprintf(path, "/tmp/fifo_server_%s", pid);
if (check_client_fifo(path) < 0)
return -1;
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
sprintf(buf, "%s:ack!", msg);
ret = write(fd, buf, strlen(buf) + 1);
if (ret < 0)
pr_err("write error\n");
pr_info("write client fifo successfully!\n");
close(fd);
return ret;
}
/* we just print the msg */
int main_parse(char *cmd, int size)
{
char *pid = NULL;
char *msg = NULL;
split_command(cmd, size, &pid, &msg);
send_msg_ack(pid, msg);
handle_msg(pid, msg);
}
/*
* this is a simple server program
*
* you may optimize it by using multi-threads or multi-processes
*
*/
int main(int argc, char *argv[])
{
int fd, len;
signal_setup();
fifo_setup();
fd = open("/tmp/fifo_server", O_RDWR);
if (fd < 0)
err_exit("server fifo open error\n");
while(1) {
len = main_receive(fd, command, CMD_SIZE);
if (len < 0)
err_exit("server fifo read error\n");
main_parse(command, len);
}
return 1;
}
fifo client:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include "include/debug.h"
#define CMD_SIZE (1024+50)
char command[CMD_SIZE];
char ack[CMD_SIZE];
int main (int argc, char *argv[])
{
int len, ret, fd1, fd2, count = 0;
char path[1024];
struct stat st;
if (argc != 2) {
pr_info("Usage: %s msg\n", argv[0]);
exit(1);
}
fd1 = open("/tmp/fifo_server", O_RDWR);
if (fd1 < 0) {
pr_err("open error\n");
exit(1);
}
len = snprintf(command + 4, CMD_SIZE, "%d:%s", (int)getpid(), argv[1]);
len++; //add for '\0'
snprintf(command, 4, "%d", len);
ret = write(fd1, command, len + 4);
if (ret < 0) {
pr_err("write error\n");
return -1;
}
snprintf(path, 1024, "/tmp/fifo_server_%d", (int)getpid());
do {
ret = stat(path, &st);
if (ret < 0) {
pr_info("client fifo doesn't exist, wait for %d times\n", count);
usleep(3000);
count++;
} else if (ret == 0)
break;
} while (count >= 3);
/* open it read only, so server close this client fifo will make read return */
fd2 = open(path, O_RDONLY);
if (fd2 < 0) {
pr_err("open error\n");
exit(1);
}
len = read(fd2, ack, CMD_SIZE);
pr_info("receive:%s\n", ack);
return 0;
}