本章将说明进程之间相互通信的其它技术----进程间通信(IPC)

管道

管道只能在具有公共祖先的两个进程之间只用。通常,一个管道由一个进程创建,在进程调用fork后,这个管道就能在父进程和子进程之间使用了。

管道是通过调用pipe函数创建的:

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

经由参数fd返回两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。fd[1]是输出,fd[0]是输入。

下图演示从父进程到子进程的管道(父进程关闭管道的读端(fd[0]),子进程关闭管道的写端(fd[1]))

下面程序创建了一个父进程到子进程的管道,并且父进程经由该管道向子进程传送数据

 #include "apue.h"

 int
main(void)
{
int n;
int fd[];
pid_t pid;
char line[MAXLINE]; if (pipe(fd) < )
err_sys("pipe error");
if ((pid = fork()) < ) {
err_sys("fork error");
} else if (pid > ) { /* parent */
close(fd[]);
write(fd[], "hello world\n", );
} else { /* child */
close(fd[]);
n = read(fd[], line, MAXLINE);
write(STDOUT_FILENO, line, n);
}
exit();
}

函数popen和pclose

这两个函数实现的操作是:创建一个管道,fork一个子进程,关闭未使用的管道端,执行一个shell运行命令,然后等待命令终止。

#include <stdio.h>
FILE *popen(const char *cmdstring,const char *type);
int pclose(FILE *fp);

函数popen先执行fork,然后调用exec执行cmdstring,并且返回一个标准I/O文件指针。

如果type是“r”,则文件指针连接到cmdstring的标准输出

如果type是“w”,则文件指针连接到cmdstring的标准输入

pclose函数关闭标准I/O流,等待命令终止,然后返回shell的终止状态。

下面程序演示使用popen向分页程序传送文件

 #include "apue.h"
#include <sys/wait.h> #define PAGER "${PAGER:-more}" /* environment variable, or default */ int
main(int argc, char *argv[])
{
char line[MAXLINE];
FILE *fpin, *fpout; if (argc != )
err_quit("usage: a.out <pathname>");
if ((fpin = fopen(argv[], "r")) == NULL)
err_sys("can't open %s", argv[]); if ((fpout = popen(PAGER, "w")) == NULL)
err_sys("popen error"); /* copy argv[1] to pager */
while (fgets(line, MAXLINE, fpin) != NULL) {
if (fputs(line, fpout) == EOF)
err_sys("fputs error to pipe");
}
if (ferror(fpin))
err_sys("fgets error");
if (pclose(fpout) == -)
err_sys("pclose error"); exit();
}

下面是程序运行的结果

考虑下面一个应用程序:它向标准输出写一个提示,然后从标准输入读一行,使用popen在应用程序和输入之间插入一个程序以便对输入进行变换

下面程序将演示这个过滤程序,它将输入的大写字符转换成小写字符

 #include "apue.h"
#include <ctype.h> int
main(void)
{
int c; while ((c = getchar()) != EOF) {
if (isupper(c))
c = tolower(c);
if (putchar(c) == EOF)
err_sys("output error");
if (c == '\n')
fflush(stdout);
}
exit();
}

将这个过滤程序编译成可执行文件,然后下面程序用popen调用它

 #include "apue.h"
#include <sys/wait.h> int
main(void)
{
char line[MAXLINE];
FILE *fpin; if ((fpin = popen("myuclc", "r")) == NULL)
err_sys("popen error");
for ( ; ; ) {
fputs("prompt> ", stdout);
fflush(stdout);
if (fgets(line, MAXLINE, fpin) == NULL) /* read from pipe */
break;
if (fputs(line, stdout) == EOF)
err_sys("fputs error to pipe");
}
if (pclose(fpin) == -)
err_sys("pclose error");
putchar('\n');
exit();
}

协同进程

UNIX系统过滤程序从标准输入读取数据,向标准输出写数据。

当一个过滤程序既产生某个过滤程序的输入,又读取该过滤程序的输出时,它就变成了协同进程。

我们通过实例来观察协同进程。进程创建两个管道:一个是协同进程的标准输入,一个是协同进程的标准输出。

下面程序是一个简单的协同进程,它从其标准输入读取两个数,计算它们的和,然后将和写至其标准输出。

 #include "apue.h"

 int
main(void)
{
int n, int1, int2;
char line[MAXLINE]; while ((n = read(STDIN_FILENO, line, MAXLINE)) > ) {
line[n] = ; /* null terminate */
if (sscanf(line, "%d%d", &int1, &int2) == ) {
sprintf(line, "%d\n", int1 + int2);
n = strlen(line);
if (write(STDOUT_FILENO, line, n) != n)
err_sys("write error");
} else {
if (write(STDOUT_FILENO, "invalid args\n", ) != )
err_sys("write error");
}
}
exit();
}

对此程序进行编译,将其可执行文件目标代码存入名为add2的文件。

使用下面程序来调用add2协同进程,并将协同进程送来的值写到其标准输出。

 #include "apue.h"

 static void    sig_pipe(int);        /* our signal handler */

 int
main(void)
{
int n, fd1[], fd2[];
pid_t pid;
char line[MAXLINE]; if (signal(SIGPIPE, sig_pipe) == SIG_ERR)
err_sys("signal error"); if (pipe(fd1) < || pipe(fd2) < )
err_sys("pipe error"); if ((pid = fork()) < ) {
err_sys("fork error");
} else if (pid > ) { /* parent */
close(fd1[]);
close(fd2[]); while (fgets(line, MAXLINE, stdin) != NULL) {
n = strlen(line);
if (write(fd1[], line, n) != n)
err_sys("write error to pipe");
if ((n = read(fd2[], line, MAXLINE)) < )
err_sys("read error from pipe");
if (n == ) {
err_msg("child closed pipe");
break;
}
line[n] = ; /* null terminate */
if (fputs(line, stdout) == EOF)
err_sys("fputs error");
} if (ferror(stdin))
err_sys("fgets error on stdin");
exit();
} else { /* child */
close(fd1[]);
close(fd2[]);
if (fd1[] != STDIN_FILENO) {
if (dup2(fd1[], STDIN_FILENO) != STDIN_FILENO)
err_sys("dup2 error to stdin");
close(fd1[]);
} if (fd2[] != STDOUT_FILENO) {
if (dup2(fd2[], STDOUT_FILENO) != STDOUT_FILENO)
err_sys("dup2 error to stdout");
close(fd2[]);
}
if (execl("./add2", "add2", (char *)) < )
err_sys("execl error");
}
exit();
} static void
sig_pipe(int signo)
{
printf("SIGPIPE caught\n");
exit();
}

FIFO

FIFO有时被称为命名管道。与未命名管道不一样:通过FIFO,不相关的进程也能交换数据。

FIFO是一种文件类型,创建FIFO类似于创建文件

#include <sys/stat.h>
int mkfifo(const char *path,mode_t mode);
int mkfifoat(int fd,const char *path,mode_t mode);

考虑这样一个过程,它需要对一个经过过滤的输出流进行两次处理

使用FIFO和UNIX程序tee(1)就可以实现这样的过程而无需使用临时文件。

tee程序将其标准输入同时复制到其标准输出以及其命令行中命名的文件中。

mkfifo fifo1
prog3 < fifo1 &
prog1 < infile | tee fifo1 | prog2

创建FIFO,然后在后台启动prog3,从FIFO读数据。然后启动prog1,用tee将其输出发送到FIFO和prog2

FIFO的另一个用途是在客户进程和服务器进程之间传送数据。

如果有一个服务器进程,它与很多客户进程有关,每个客户进程都可将其请求写到一个该服务器进程创建的总所周知的FIFO中。

服务器可以使用下面的安排来响应客户进程(为每一个客户进程创建一个FIFO用来响应)

XSI IPC

有3中成果XSI IPC的IPC:消息队列、信号量以及共享存储器。

它们有如下相类似的特征:

1.标识符和键

2.权限结构

3.结构限制

消息队列

消息队列是消息的链接表,存储在内核中,由消息队列标识符标识。

信号量

信号量是一个计数器,用于为多个进程提供对共享数据对象的访问。

共享存储

共享存储允许两个或多个进程共享一个给定的存储区。

apue学习笔记(第十五章 进程间通信)的更多相关文章

  1. 学习笔记 第十五章 JavaScript基础

    第15章   JavaScript基础 [学习重点] 了解JavaScript基础知识 熟悉常量和变量 能够使用表达式和运算符 正确使用语句 能够掌握数据类型和转换的基本方法 正确使用函数.对象.数组 ...

  2. apue学习笔记(第五章 标准I/O)

    本章讲述标准I/O库 流和FILE对象 对于标准I/O库,它们的操作是围绕流进行的.流的定向决定了所读.写的字符是单字节还是多字节的. #include <stdio.h> #includ ...

  3. JavaScript高级程序设计学习笔记第十五章--使用Canvas绘图

    一.基本用法 1.要使用<canvas>元素,必须先设置其 width 和 height 属性,指定可以绘图的区域大小.能通过 CSS 为该元素添加样式,如果不添加任何样式或者不绘制任何图 ...

  4. VSTO学习笔记(十五)Office 2013 初体验

    原文:VSTO学习笔记(十五)Office 2013 初体验 Office 2013 近期发布了首个面向消费者的预览版本,我也于第一时间进行了更新试用.从此开始VSTO系列全面转向Office 201 ...

  5. Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装项目其它需要包 清除冗余文件并重新规划项目目录 配置文件 规划示例路由,并新建相关文件 实现数据访问和业务逻辑相关方法 编写mys ...

  6. [转]Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    本文转自:https://www.cnblogs.com/zhongweiv/p/nodejs_koa2_webapp.html 目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装 ...

  7. python3.4学习笔记(二十五) Python 调用mysql redis实例代码

    python3.4学习笔记(二十五) Python 调用mysql redis实例代码 #coding: utf-8 __author__ = 'zdz8207' #python2.7 import ...

  8. Nodejs学习笔记(十五)—Node.js + Koa2 构建网站简单示例

    前言 前面一有写到一篇Node.js+Express构建网站简单示例:http://www.cnblogs.com/zhongweiv/p/nodejs_express_webapp.html 这篇还 ...

  9. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第五章:渲染流水线

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第五章:渲染流水线 学习目标 了解几个用以表达真实场景的标志和2D图像 ...

  10. UNP学习笔记(第五章 TCP客户/服务程序实例)

    我们将在本章使用前一章中介绍的基本函数编写一个完整的TCP客户/服务器程序实例 这个简单得例子是执行如下步骤的一个回射服务器: TCP回射服务器程序 #include "unp.h" ...

随机推荐

  1. 【bzoj3091】城市旅行 LCT区间合并

    题目描述 输入 输出 样例输入 4 5 1 3 2 5 1 2 1 3 2 4 4 2 4 1 2 4 2 3 4 3 1 4 1 4 1 4 样例输出 16/3 6/1 题解 LCT区间合并 前三个 ...

  2. 删除ARCSDE表空间和用户后,新建时出现error -1:O的解决办法

    对于刚开始使用arcsde的用户,可能会出现各种问题,慢慢来就会找到解决办法 当我们删除用户和表空间时,在服务器本地还保留这sde.dbf文件(删除时选择了删除本地文件,不知道为什么), 我们可以换一 ...

  3. XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki Problem J Stairways解题报告(分块+维护凸壳)

    首先ORZ一发Claris聚聚的题解:http://www.cnblogs.com/clrs97/p/8689215.html,不然我可能没机会补过这道神题了. 这里写一个更详细的题解吧(我还是太菜了 ...

  4. UVALive 6609 Minimal Subarray Length(RMQ-ST+二分)

    题意:给定长度为N的数组,求一段连续的元素之和大于等于K,并且让这段元素的长度最小,输出最小长度即可,若不存在这样的元素集合,则输出-1 题目链接:UVAlive 6609 做法:做一个前缀和pref ...

  5. SPOJ 10628 Count on a tree(Tarjan离线 | RMQ-ST在线求LCA+主席树求树上第K小)

    COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to  ...

  6. 使用 Nginx 过滤网络爬虫

    现在有许多初学者学习网络爬虫,但他们不懂得控制速度,导致服务器资源浪费.通过 Nginx 的简单配置,能过滤一小部分这类爬虫. 方法一:通过 User-Agent 过滤 Nginx 参考配置如下: l ...

  7. windows 批处理删除指定目录下 指定类型 指定天数之前文件

    删除D:\test下5天前所有文件,如下: @echo offset SrcDir=D:\testset DaysAgo=5forfiles /p %SrcDir% /s /m *.* /d -%Da ...

  8. Mybatis Plugin插件安装破解及使用

    2018年2月更新 2018年2月份,提供一个网上比较多的一个版本V3.21版本,下载资源里面有个已整合版直接解压放入C:\Users\你的用户名\.IntelliJIdea2017.3\config ...

  9. T-SQL百万记录中分组取最大值方法ROW_NUMBER() OVER()

    SELECT SysUserID, UserID, ROW_NUMBER() OVER(PARTITION BY UserID ORDER BY AddTime DESC) AS nums AND S ...

  10. Java数据结构-------Set

    三种常用Set:HashSet.LinkedHashSet.TreeSet set类继承关系: 概述 Set是对对应Map的一种封装,Set中的元素不可以重复. HashSet对应 HashMap.L ...