本章将说明进程之间相互通信的其它技术----进程间通信(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. 【Luogu】P4219大融合(LCT)

    题目链接 LCTrotate打错尬死 容易发现本题就是问两边子树大小乘积,于是开个数组动态维护LCT每个节点虚子树上有多少点,在Access和Link的时候更新即可. #include<cstd ...

  2. HDU 5884 Sort(二分答案+计算WPL的技巧)

    Sort Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  3. IO文本操作

    创建文件并写入内容 StreamWriter sw = new StreamWriter(url, “false 覆盖,true 追加”, Encoding.UTF8); sw.Write(“内容”) ...

  4. 【一个比较bug free的二分写法】

    lower_bound: [l, r)区间内大于等于val的第一个位置 int lower_bound(int l, int r, int val){ while(l < r){ ); if(a ...

  5. 【自己D自己】WC2019总结

    好吧写着写着写成自黑文了. 这是我时隔一个月写的,寒假非常自闭,肝童年游戏赛尔号来着…… 没玩过的无视 作为一个 $BJ$ 蒟蒻,第一次飞到广州二中这么远的地方(我没出过国,去广州算是很远的一次了). ...

  6. iOS 之 判断是否是第一次打开app

    /** App判断第一次启动的方法 */ NSString *key = @"isFirst"; BOOL isFirst = [[NSUserDefaults standardU ...

  7. Long.ValueOf("String") Long.parseLong("String") 区别 看JAVA包装类的封箱与拆箱

    IP地址类型转换原理: 将一个点分十进制IP地址字符串转换成32位数字表示的IP地址(网络字节顺序). 将一个32位数字表示的IP地址转换成点分十进制IP地址字符串. 1.Long.ParseLong ...

  8. 收集邮票(bzoj 1426)

    Description 有n种不同的邮票,皮皮想收集所有种类的邮票.唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n.但是由于凡凡也 ...

  9. URAL1960 Palindromes and Super Abilities

    After solving seven problems on Timus Online Judge with a word “palindrome” in the problem name, Mis ...

  10. 【BZOJ4504&&Hihocoder1046】K个串(主席树,堆)

    题意:一个长度为n的数字序列,选出其中的一个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次)询问第k大的和是多少 1 <= n <= 100000, 1 < ...