<-- Home

Linux进程间的简单通信

每个进程有不同的用户地址空间,进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。

管道通信

管道是一种最基本的通信机制。在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,进程1往管道中写数据,进程2读管道中的数据,这样就实现了进程间的通信。对用户程序来说,管道就像一个文件。
创建管道的函数:

#include<unistd.h>
int pipe(int filedes[2]);

调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。pipe函数调用成功返回0,调用失败返回-1。

管道通信的过程:

  • 父进程创建管道;
  • 父进程fork出子进程;
  • 父进程关闭fileds[0],子进程关闭fileds[1];

管道是用队列实现的,由于是队列,故在两个进程间通信只能由一端到另一端,如果要双向通信则需创建两个管道。由于是通过文件描述符来指向管道的,那么只能在父子进程间通信。

#include<stdio.h>
#include<unistd.h>

int main(void)
{
    int fd[2];
    int n;
    pid_t pid;
    char line[80];
    pipe(fd);
    pid = fork();
    if(pid > 0){
        close(fd[0]);
        write(fd[1], "hello world\n", 12);
        waitpid(pid,NULL,0); 
    }else if(pid == 0){
        close(fd[1]);
        n = read(fd[0], line, 80);
        printf("msg:%s", line);
    }
    return 0;
}

FIFO

用命令行来测试:
先创建一个FIFO文件

mkfifo test

然后读取FIFO的内容,此时因为FIFO为空,所以会挂起

cat ./test

打开另一个终端往test中写入信息

echo "just a test" > ./test

这时你会发现第一个终端会输出”just a test”并且退出。

用程序测试:

和命令行一样采取阻塞式,代码如下

写入FIFO:

//write.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>

int main()
{
    int fd = 0;
    int n;
    int re;
    char buf[15] = "hello world\n";
    unlink("myfifo");   //删除已经存在的FIFO文件
    re = mkfifo("myfifo", 0777 );   //建立新的FIFO文件并设置读写权限
    fd = open("myfifo", O_WRONLY);
    if(fd >= 0){
        n = write(fd, buf, 15);
        close(fd);
        printf("%d writed\n", n);
    }else{
        perror("open error");
        exit(1);
    }
    return 0;
}

读取FIFO:

//read.c
#include<stdio.h> 
#include<stdlib.h> 
#include<unistd.h> 
#include<sys/types.h> 
#include<sys/stat.h> 
#include<fcntl.h> 
#include<errno.h>

int main()
{
    char buf[80];
    int n = 0;
    int fd;
    fd = open("myfifo", O_RDONLY);
    if(fd < 0){
        perror("open error");
        exit(-1);
    }
    n = read(fd, buf, 80);
    close(fd);
    printf("n = %d\n", n);
    printf("msg:%s\n", buf);
    return 0;
}

在两个不同的终端分别运行写和读的2个程序可看到结果。
上面的代码是阻塞式的,如果不希望阻塞可以将打开FIFO文件的函数换成

 fd = open("myfifo", O_RDONLY | O_NONBLOCK);

UNIX Domain Socket

socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。UNIX Domain Socket是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制 由于是全双工,则可以互相通信,不存在读写端,这里称为服务端和客户端。

服务端工作流程:

  1. socket 建立一个socket
  2. bind 将这个socket绑定在文件(端口)上
  3. listen 开始监听
  4. accept 如果客户端连接,则接受并建立一个新的socket来和客户端通信
  5. read/write 读取或者发送消息
  6. close 关闭连接

客户端工作流程:

  1. socket 建立一个socket
  2. connect 主动连接服务端的文件(端口)
  3. read/write 接收或者发送消息
  4. close 关闭连接