【Linux操作系统 教程】进程间的五种通信方式详解之——管道

进程间的五种通信方式详解之——管道

进程间的通信方式有以下几种:

  • 管道
  • 消息队列
  • 共享内存
  • 信号量
  • Socke
  • 信号
  • 文件锁

下面就来详细讲解其中一种通信方式——通过管道通信

1、管道

不同的进程可以通过“管道”进行通信,管道具体分为以下两种:

1. 1 匿名管道

1.1.1匿名管道的概念
       所谓“匿名管道”就是在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利。
       匿名管道用于进程之间通信,且仅限于本地父子进程之间通信,一般使用fork函数实现父子进程的通信,结构简单,实现容易,类似于一根非水平状态的水管,一端进水另一端出水。

1.1.2 匿名管道的特点:
   ①只提供单向通信,也就是说,两个进程都能访问这个文件,假设进程1往文件内写东西,那么进程2 就只能读取文件的内容。
  ②只能用于具有血缘关系的进程间通信,通常用于父子进程建通信
  ③管道是基于字节流来通信的
  ④依赖于文件系统,它的生命周期随进程的结束结束(随进程)
  ⑤其本身自带同步互斥效果
  
1.1.3 匿名管道的应用:
   匿名管道主要用于本地父进程和子进程之间的通信,在父进程中的话,首先是要创建一个匿名管道,在创建匿名管道成功后,可以获取到对这个匿名管道的读写句柄,然后父进程就可以向这个匿名管道中写入数据和读取数据了,但是如果要实现的是父子进程通信的话,那么还必须在父进程中创建一个子进程,同时,这个子进程必须能够继承和使用父进程的一些公开的句柄,为什么呢?因为在子进程中必须要使用父进程创建的匿名管道的读写句柄,通过这个匿名管道才能实现父子进程的通信,所以必须继承父进程的公开句柄。同时在创建子进程的时候,必须将子进程的标准输入句柄设置为父进程中创建匿名管道时得到的读管道句柄,将子进程的标准输出句柄设置为父进程中创建匿名管道时得到的写管道句柄。然后在子进程就可以读写匿名管道了。

1.1.4 匿名管道代码举例:

#include 
#include 
#include 
#include 

#define BUFFER_SIZE 25
#define READ_END	0
#define WRITE_END	1

int main(void)
{
	char write_msg[BUFFER_SIZE] = "Greetings";
	char read_msg[BUFFER_SIZE];
	pid_t pid;
	int fd[2];

	/* create the pipe */
	if (pipe(fd) == -1) {
		fprintf(stderr,"Pipe failed");
		return 1;
	}

	/* now fork a child process */
	pid = fork();

	if (pid < 0) {
		fprintf(stderr, "Fork failed");
		return 1;
	}

	if (pid > 0) {  /* parent process */
		/* close the unused end of the pipe */
		close(fd[READ_END]);

		/* write to the pipe */
		write(fd[WRITE_END], write_msg, strlen(write_msg)+1); 

		/* close the write end of the pipe */
		close(fd[WRITE_END]);
	}
	else { /* child process */
		/* close the unused end of the pipe */
		close(fd[WRITE_END]);

		/* read from the pipe */
		read(fd[READ_END], read_msg, BUFFER_SIZE);
		printf("child read %s\n",read_msg);

		/* close the write end of the pipe */
		close(fd[READ_END]);
	}

	return 0;
}
1.2 命名管道

1.2.1 命名管道概念:
       所谓“命名管道”就是在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利,没有血缘关系的进程也可以进程间通信。
       命名管道可在同一台计算机的不同进程之间或在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信。
       命名管道(NamedPipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是:命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。

1.2.2 命名管道的特点:

  1. 命名管道可以在无关的进程之间交换数据,进行通信。(这一点与匿名管道不同)
  2. 命名管道有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

1.2.3 命名管道的作用:
       在计算机编程里,命名管道是一种从一个进程到另一个进程用内核对象来进行信息传输。和一般的管道不同,命名管道可以被不同进程以不同的方式方法调用(可以跨权限、跨语言、跨平台)。只要程序知道命名管道的名字,发送到命名管道里的信息可以被一切拥有指定授权的程序读取,但对不具有制定授权的。命名管道是一种FIFO(先进先出,First-In First-Out)对象。

1.2.4 命名管道的代码举例:
pip_write.c

以下代码是通过命名管道进行写操作。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFO_NAME "test_fifo"
#define BUFFER_SIZE (512)

#define TESTF_IN "test.dat"
#define OP_LEN (100)

FILE* fp_in;

int main(char argc, char* argv[])
{
    int pipe_fd;
    int res;
    int open_mode = O_WRONLY;
    int bytes_sent = 0;
    int bytes_read = 0;
    char buffer[BUFFER_SIZE + 1];

    if (access(FIFO_NAME, F_OK) == -1)
    {
        res = mkfifo(FIFO_NAME, 0777);
        if (res != 0)
        {
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }

    printf("Process %d opening FIFO O_WRONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);


    if (pipe_fd != -1)
    {

        if ((fp_in = fopen(TESTF_IN, "r")) < 0)
        {
         fprintf(stderr, "Open input file failed:%s\n", TESTF_IN);
            exit(EXIT_FAILURE);
        }

        while ((bytes_read = fread(buffer, sizeof(char), OP_LEN, fp_in)))
        {
            printf("PRODUCE: %d, %s", bytes_read, buffer);

            res = write(pipe_fd, buffer, bytes_read);
            if (res == -1)
            {
                fprintf(stderr, "Write error on pipe\n");
                exit(EXIT_FAILURE);
            }    
            bytes_sent += res;        

            memset(buffer, 0, BUFFER_SIZE);
            if (feof(fp_in) != 0)
            {
                printf("read over\n");
                break;
            }
        }

        (void)close(pipe_fd);
        fclose(fp_in);
    }
    else
    {
        exit(EXIT_FAILURE);
    }

    return 0;
}

pip_read.c

以下代码是通过命名管道进行读操作。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define FIFO_NAME "test_fifo"
#define BUFFER_SIZE (512)
#define OP_LEN (100)

int main(char argc, char* argv[])
{
    int pipe_fd;
    int res;
    int open_mode = O_RDONLY;
    char buffer[BUFFER_SIZE + 1];
    int bytes_read = 0;

    memset(buffer, '\0', sizeof(buffer));

    printf("Process %d opening FIFO O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);

    if (pipe_fd != -1)
    {
        do
        {
            res = read(pipe_fd, buffer, OP_LEN);
            printf("CONSUME: %d, %s\n", res, buffer);
            bytes_read += res;

            memset(buffer, 0, BUFFER_SIZE);

            if (res < OP_LEN)
            {
                printf("read over\n");
                break;
            }
        } while (res > 0);
        (void)close(pipe_fd);
    }
    else
    {
        exit(EXIT_FAILURE);
    }

    printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
    exit(EXIT_SUCCESS);
    return 0;
}

以上是进程间的五种通信方式详解之——管道方式的详解
对于另外几种进程间的通信方式会后续更新,敬请期待… …

【Linux操作系统 教程】进程间的五种通信方式详解之——管道_第1张图片

【Java Web】相关技术文章:
【Java Web总结】Java Web项目中 的.classpath、.mymetadata、.project文件作用
【Java Web问题解决】Tomcat报错javax.servlet.ServletException: Error instantiating servlet class.报错404
【比较】什么是“服务器端跳转”“客户端跳转”,二者有什么区别?
【总结】表单提交的get和post有什么不同?
【Java Web问题解决】Tomcat报错:java.lang.ClassCastException: cannot be cast to javax.servlet.Filter解决办法
【Java Web问题解决】Filter过滤器初始化方法init()执行了两次原因及解决方法
【总结】Java Web 中的4种属性范围(page、request、session、application)
【Java Web问题解决】Tomcat报错:java.sql.SQLException: No suitable driver found for jdbc:mysql://
【Java Web问题解决】Tomcat启动时控制台出现中文乱码的问题解决方法
【示例项目】java实现通过身份证号码判断籍贯所在地区
【总结】HTTP协议中的状态码(200、403、404、500等)
【Java Web问题解决】提交表单后显示乱码原因及解决办法
【Java Web总结】JSP页面的生命周期详解
【Java Web总结】JSP页面实现类详解
【Java Web 问题解决】Tomcat启动失败 报错:Server Tomcat v9.0 Server at localhost failed to start.
【Java Web问题解决】连接数据库出错:java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/
【Java Web问题解决】使用过滤器Filter解决提交表单后显示乱码问题
【Java Web问题解决】过滤器Filter进行编码过滤后页面空白、显示不了原因及解决办法

【Linux 操作系统】相关技术文章:
【Linux问题解决】Ubuntu Linux 安装gcc4.9 g++4.9报错“没有可供安装的候选者”解决办法
【Linux教程】Ubuntu Linux 更换源教程
【Linux教程】如何实现在Ubuntu Linux和windows之间复制粘贴、拖拽复制文件?
【Linux问题解决】操作系统用C语言多线程编程 对‘pthread_create’未定义的引用 报错解决办法
【Linux教程】Linux中用C语言多线程编程之pthread_join()函数
【Linux操作系统、C语言】在Linux中用C语言进行OpenMP并行程序设计之常见指令、库函数和指令总结
【VMware 虚拟机问题解决】VMware Workstation pr无法在Windows上运行的解决方案
【Linux 问题解决】Ubuntu执行apt-get命令报错:无法获得锁 /var/lib/dpkg/lock…解决方案

【Python】相关技术文章:
【总结】Python与C语言、Java等语言基本语法的不同点
【总结】分析Python中的循环技巧
【总结】Python语言是编译型语言还是解释型语言?(Python程序执行过程)
【总结】Python2 和 Python3 的区别
利用Python一层循环打印 * 型三角形
【总结】Python与C语言、Java等语言基本语法的不同点
【总结】你知道吗?——元组其实是可变的序列!
【Python爬虫教程】Python爬虫基本流程及相关技术支持
【Python问题解决】PyCharm中debug报错:using cython not found. pydev debugger: process 13108 is connecting原因及解决
【Python总结】闭包及其应用

【IntelliJ IDEA教程】相关技术文章:
【Intellij IDEA教程】怎么自动清除无效的import导入包、清除无效的import导入包的快捷键
【IntelliJ IDEA教程】在IntelliJ IDEA启动项目 Warning:java: 源值1.5已过时, 将在未来所有发行版中删除 解决办法
【IntelliJ IDEA教程】提示信息Unmapped Spring configuration files found.Please configure Spring facet. 解决办法
【IntelliJ IDEA教程】怎么取消IntelliJ IDEA对单词拼写的检查

【Jupyter Notebook教程】相关技术文章:
【Python教程】Jupyter Notebook把一段很长的代码分成多行的解决办法】

你可能感兴趣的:(操作系统)