学习笔记-进程间通信(Interprocess communication,IPC) --概念、 pipe and FIFOs

面试时一再地被问到进程间通信的情况,而我的理解一直停留在概念上和名词上,必须要做点实验加深理解。

一、概述

由于网上资料和各种书籍关于IPC的讲述有少许区别或完整性不够,所以要先选择要查阅的资料,这里我主要基于书《APUE》和《UNIX网络编程第二卷进程间通信》学习IPC。

《UNIX网络编程第二卷进程间通信》中主要叙述4种不同形式的IPC:

1.消息传递(管道、FIFO、消息队列)

2.同步(互斥锁、条件变量、读写锁、文件与记录锁、信号量(semaphore)

3.共享内存区(匿名共享内存区、有名共享内存区)

4.远程过程调用(Solaris门、Sun RPC)

该书讲它不讨论通过计算机通信网络实现的IPC,而主要讨论不涉及网络的IPC,所以,我认为加上

5.Socket(Unix Domain Socket、TCP/IP Socket)就构成完整的IPC.

(面试时最好回答管道、FIFO、信号量等,而不是消息传递、同步等,因为更多书是按照前者分类IPC)

由于每个进程有各自独立的地址空间,进程间通信就要在地址空间外借助于一些其他的东西,图1.1[1]可以形象说明IPC借助什么进行通信。

image

 

二、pipes和FIFOs

1. pipes(管道) :使用最广泛,具有亲缘关系的进程间才能使用管道进行IPC。创建的pipes存在于内核中。一般情况下,管道是半双工管道(不是全双工,也不是单工),即在某一时刻从一端进从一端出,不能在一端即能进也能出(补:单工的意思是任何时刻只能一端进一端出)。有些系统提供了全双工管道,不过,使用半双工管道基本就可以解决很多问题了。用两个半双工管道就能实现全双工管道的功能。

shell中使用管道举例:ls -a | grep "jun"

自己的程序中使用管道的方法:父进程创建pipe,然后fork子进程,这时父子进程通过pipe进行通信了。

程序中使用管道例子代码:(为了抓住重点,删除了处理错误的代码)

int main()

{

    int fd[2]; //管道是一种文件,有read端和write端,要用到两个file descriptor

    pipe(fd); //创建管道

    pid = fork();

    if (pid > 0) { //父进程

       close(fd[0]); //fd[0]是read端,fd[1]是write端,这里关闭read端。close()是File I/O的API。

       write(fd[1], ”hello pipe/n",  11); //通过write端,写入数据。write()是File I/O的API。

    }

    else {  //子进程

       close(fd[1]); //关闭write端

       int n = read(fd[0], buf, MAX_SIZE);

       write(1, buf, n); //整数1,在我所知道的操作系统的实现中是标准输出的File descriptor

    }

    exit(0);

}

程序代码中使用管道的套路一般是:pipe;fork;exec*,标准I/O提供了popen和pclose函数整合有关管道的函数,使得可以在自己的程序中用更少的代码实现管道。

 

 

2. FIFOs, FIFOs are sometimes called named pipes(有名管道)。FIFOs突破了只能在具有亲缘关系的进程间使用的限制,这通过创建一个FIFO类型的文件实现。FIFO数据流是半双工。

shell中使用FIFO举例:

        mkfifo fifo1  //创建名为fifo1的有名管道,使用ls命令可以看到fifo1文件(文件类型字母是p)

        cat < fifo1 & //后台运行cat,重定向fifo1为输入

        ls –a > fifo1 //ls -a的结果重定向到fifo1,也就是通过fifo1传递数据给cat命令

在Unix/Linux中提供了命令mkfifo用于创建FIFO和unlink或rm删除所创建的FIFO。

程序中使用FIFO例子代码:(一个很简单的server-client程序,没有出错处理)

server.c

#define FIFO "fifo"
#define FILE_MODE (S_IRUSR | S_IWUSR)

int main()
{
    int readfd, n;
    char buf[1024];
    mkfifo(FIFO, FILE_MODE); //创建FIFO,即在文件系统中建一个文件
    readfd = open(FIFO, O_RDONLY); //既然是个文件,就能用open
    while ((n = read(readfd, buf, 1024)) > 0) { 
        buf[n] = '/0';
        printf("server:%s/n", buf);
    }

    return 0;
}

 

client.c

#define FIFO "fifo"  //FIFO的名字就是文件名
#define FILE_MODE (S_IRUSR | S_IWUSR)

int main()
{
    int writefd;
    int n;
    char buf[1024];
    writefd = open(FIFO, O_WRONLY); //先运行服务器,FIFO已经存在
    while ((n = read(0, buf, 1024)) > 0) {
        buf[n-1] = '/0';
        printf("%s/n", buf);
        n = write(writefd, buf, strlen(buf));
    }

  unlink(FIFO); //删除FIFO

  return 0;
}

测试方法,先server &, 然后client。

 

小结[1]

image 

参考资料

1、《UNIX网络编程第二卷进程间通信》

2、《APUE》

 

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭