Linux -- FIFO命名管道

接口

#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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值