本章将说明进程之间相互通信的其它技术----进程间通信(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. preg_replace_callback 正则替换回调方法用法,

    Example #1 preg_replace_callback() 和 匿名函数 <?php /* 一个unix样式的命令行过滤器,用于将段落开始部分的大写字母转换为小写. */ $fp = ...

  2. BZOJ 4326:NOIP2015 运输计划(二分+差分+lca)

    NOIP2015 运输计划Description公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所 ...

  3. redis学习(三)五种数据结构

    Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合). 1.string string类型是Redis最基 ...

  4. 【10】react 之 react-router

    1.1.  路由 路由:URL与处理器的映射. 浏览器当前的 URL 发生变化时,路由系统会做出一些响应,用来保证用户界面与 URL 的同步. 1.2.  Router安装 npm i react-r ...

  5. Shell 环境中的输入输出重定向

    Linux Shell 环境中的输入输出重定向,用符号<和>来表示.0.1和2分别表示标准输入.标准输出和标准错误. 1.重定向标准输出到文件: cat fo > foo.txt 2 ...

  6. Program "D:\AndroidDevelopment\android-ndk-r9\ndk-build.cmd" not found in PATH

    1.问题描述 2.解决方法:修改ndk-build.cmd的配置路径, 修改成本地ndk-build.cmd所在路径,如下

  7. 补不manjaro系统

    昨天无意间看到:使用不同的主题时,使用midna图标时,关机的按钮和其他的不同,经过摸索,只需要更改替换3个图标即可: (1)进入目录/usr/share/icons/breeze/actions/t ...

  8. Codeforces Gym101522 C.Cheering-字符串 (La Salle-Pui Ching Programming Challenge 培正喇沙編程挑戰賽 2017)

    C.Cheering To boost contestants' performances in the 20th La Salle - Pui Ching Programming Challenge ...

  9. Java编程风格学习(一)

    最近在看一本有关Java编程规范的书,书中精炼阐述了使用java语言时应该遵循的一些原则.接下来的一段时间我将在这里总结我的学习内容,也希望这一系列文章能够对有需要的人有所帮助.不考虑任何编码规范的代 ...

  10. js 拦截全局 ajax 请求

    你是否有过下面的需求:需要给所有ajax请求添加统一签名.需要统计某个接口被请求的次数.需要限制http请求的方法必须为get或post.需要分析别人网络协议等等,那么如何做?想想,如果能够拦截所有a ...