镜缘浮影 小人本住在 苏州的城外 家里有屋又有田 生活乐无边

进程间通讯(二).fifo

2017-01-12
wilmosfang
c
原文地址 http://soft.dog/2017/01/12/c-ipc-fifo/

前言

UNIX/Linux 是多任务的操作系统,通过多个进程分别处理不同事务来实现,如果多个进程要进行协同工作或者争用同一个资源时,互相之间的通讯就很有必要了

进程间通信,Inter process communication,简称 IPC,在 UNIX/Linux 下主要有以下几种方式:

  • 无名管道 ( pipe )
  • 有名管道 ( fifo )
  • 信号 ( signal )
  • 信号量 ( semaphore )
  • 消息队列 ( message queues )
  • 共享内存 ( shared memory )
  • 套接字 ( socket )

这里分享一下我在学习进程通讯过程中的笔记和心得


概要


FIFO

FIFO 有时被称为命名管道

匿名管道 pipe 只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同的创建它们的祖先进程,而通过 FIFO 不相关的进程也能交换数据

sys/stat.h 中有关于 mkfifo 的原型声明

/* Create a new FIFO named PATH, with permission bits MODE.  */
extern int mkfifo (__const char *__path, __mode_t __mode)
     __THROW __nonnull ((1));

下面通过一个例子,演示一下 FIFO 的使用方法


代码示例

要求

有A、B两个进程(非亲缘),实现如下功能:

  • 1.A进程将字符串如“12345”发送给B进程
  • 2.B进程收到后显示为“54321”,并将首尾两个字符去掉后,反传给A进程
  • 3.A进程收到后显示为“432”

要求:用fifo做

提示:要用2个fifo

代码示例

fifoB.c

#include <unistd.h> //access,read,write,close 都在这里声明
#include <sys/stat.h> //mkfifo 在这里声明
#include <errno.h> //EEXIST 和 errno 在这里定义
#include <stdio.h>
#include <stdlib.h> //exit 在这里声明
#include <fcntl.h> //O_RDONLY 和 O_WRONLY 还有open在这里定义和声明

#define MAXSIZE 1000

//process B

int slen(char *s) //定义一个计算字符串长度的函数
{
  int i=0;
  for(;*s!='\0';s++)i++;
  return i;
}

char * srev(char *s) //这个函数用来翻转字符串
{
  int i=0,len=slen(s);
  char tmpc='\0';
  for(i=0;i<len/2;i++)
  {
    tmpc=s[i];
    s[i]=s[len-1-i];
    s[len-1-i]=tmpc;
  }
  return s;
}

int main() 
{
  char * rfifo="/tmp/abfifo"; 
  char * wfifo="/tmp/bafifo";
  char buffer[MAXSIZE],tmpc='\0';
  int i=0,rfd=0,wfd=0,res=-1,len=0,rres=0; //在定义变量的同时进行初始化,是一个好习惯
  
  
  // mkfifo abfifo
  if(-1 == access(rfifo,F_OK)) //如果rfifo不存在,则创建
  {
    if(0 > (mkfifo(rfifo,0600)) && (EEXIST != errno)) //如果创建rfifo失败,并且出错不是文件已经存在,则提示并返回
    {
      printf("cannot create fifo file %s\n",rfifo);
      exit(res);
    }
  }

  // mkfifo bafifo
  if(-1 == access(wfifo,F_OK)) //如果wfifo不存在,则创建
  {
    if(0 > (mkfifo(wfifo,0600)) && (EEXIST != errno))  //如果创建wfifo失败,并且出错不是文件已经存在,则提示并返回
    {
      printf("cannot create fifo file %s\n",wfifo);
      exit(res);
    }
  }
  if(-1 == (rfd = open(rfifo,O_RDONLY))) //打开rfifo
  {
    printf("cannot open file:%s\n",rfifo);
    return res;
  }

  if(-1 == (wfd = open(wfifo,O_WRONLY))) //打开wfifo
  {
    printf("cannot open file:%s\n",wfifo);
    return res;
  }
  
  for (i=0;1 == (rres=read(rfd,&tmpc,sizeof(char)));i++) //从rfifo中遍历所有内容,都临时存放到tmpc中(一次一个字节)
  {
    buffer[i]=tmpc; //将tmpc存放到buffer中
    printf("%c",tmpc);  //将tmpc进行打印
    if ('\0' == tmpc) break; //如果遇到字符串结尾,就跳出
  }

  if(-1 == rres) //如果read的返回值为-1,就代表读错误
  {
    printf("read error on %s\n",rfifo);
    return res;
  }
  printf("\n");
  srev(buffer); //将buffer内容翻转
  printf("%s\n",buffer); //将翻转后的buffer输出
  
  len=slen(buffer); //求buffer的长度
  if( -1 ==(write(wfd,&buffer[1],sizeof(char)*(len-2)))) //将首尾去除后中间的部分写到wfifo中
  {
    printf("write error on : %s\n",wfifo);
    return res;
  }
    
  close(rfd);
  close(wfd); //收尾的文件描述符关闭
  res=0;
  return res;
}

fifoA.c

#include <stdio.h>
#include <malloc.h>
#include <string.h> //strlen,strcpy,strcat
#include <fcntl.h> //open,O_WRONLY,O_RDONLY 
#include <stdlib.h> //exit
#include <unistd.h> //write,access,read,close

//process A

int main(int argc,char *argv[])
{
  char * rfifo="/tmp/bafifo"; 
  char * wfifo="/tmp/abfifo";
  char tmpc='\0', * buffer=NULL;
  int len=0,rfd=0,wfd=0,res=-1,i=0,rres=0; //在定义变量的同时进行初始化,是一个好习惯

  if(2 != argc) //如果参数个数不达标,就提示并返回
  {
    printf("argument unmber error: need a string\n");
    return res;
  }
  len=strlen(argv[1]);
  buffer=(char *)malloc(sizeof(char)*(len+1)); //分配动态内存
  if (-1==(wfd=open(wfifo,O_WRONLY))) //打开wfifo
  {
    printf("open fifo file error:%s\n",wfifo);
    exit(res);
  }
  
  strcpy(buffer,argv[1]); //将参数复制给buffer
  strcat(buffer,""); //buffer构成字符串

  if (-1==write(wfd,buffer,sizeof(char)*(len+1))) //将buffer内容写到wfifo中
  {
    printf("write error on : %s\n",wfifo);
    exit(res);
  }
  
  if(-1 == access(rfifo,F_OK)) //检测rfifo是否已经存在
  {
    printf("cannot access fifo file %s\n",rfifo);
    return res;
  }

  if(-1 == (rfd = open(rfifo,O_RDONLY))) //打开rfifo
  {
    printf("cannot open pipe file:%s\n",rfifo);
    return res;
  }

  for (i=0;1 == (rres=read(rfd,&tmpc,sizeof(char)));i++) //从rfifo中遍历所有内容,并且逐个存到tmpc中
  {
    buffer[i]=tmpc; //将tmpc 存到 buffer中
    printf("%c",tmpc); //将tmpc输出
    if ('\0' == tmpc) break; //遇到字符串结束符就跳出
  }
  if(-1 == rres) //如果读的返回结果为-1就表明读错误
  {
    printf("read error on %s\n",rfifo);
    return res;
  }
  printf("\n");

  close(wfd);
  close(rfd); 
  free(buffer); //进行收场的清理工作
  res=0;
  return res;
}

编译执行

emacs@ubuntu:~/c$ alias  gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc  fifoB.x fifoB.c
emacs@ubuntu:~/c$ gtc  fifoA.x fifoA.c
emacs@ubuntu:~/c$ 

先执行fifoB.x,因为等待输入,所以会在终端挂起

emacs@ubuntu:~/c$ ./fifoB.x 



执行fifoA.x,会立即返回

emacs@ubuntu:~/c$ ./fifoA.x 12345
432
emacs@ubuntu:~/c$ 

这时fifoB.x 也有返回了

emacs@ubuntu:~/c$ ./fifoB.x 
12345
54321
emacs@ubuntu:~/c$

编译执行过程中没有报错,从结果来看,符合预期


access

access 函数的相关声明在 unistd.h

/* Values for the second argument to access.
   These may be OR'd together.  */
#define R_OK    4               /* Test for read permission.  */
#define W_OK    2               /* Test for write permission.  */
#define X_OK    1               /* Test for execute permission.  */
#define F_OK    0               /* Test for existence.  */

/* Test for access to NAME using the real UID and real GID.  */
extern int access (__const char *__name, int __type) __THROW __nonnull ((1));

使用 F_OK 来检查是否存在


errno

这个变量在 errno.h

#ifndef errno
extern int errno;
#endif

其值的宏在 asm-generic/errno-base.h 这个文件中

emacs@ubuntu:/usr/include$ cat asm-generic/errno-base.h
#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H

#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */

#endif
emacs@ubuntu:/usr/include$

#define EEXIST 17 /* File exists */ 可以获知这个宏定义代表文件已经存在


总结

以下函数可以进行有名管道的创建

  • mkfifo

通过各方面资料弄懂其参数的意义和返回值的类型,是熟练掌握的基础

原文地址 http://soft.dog/2017/01/12/c-ipc-fifo/

评论