【Linux 应用编程】进程管理 - 进程间通信IPC之管道 pipe 和 FIFO
IPC(InterProcess Communication,进程间通信)是进程中的重要概念。Linux 进程之间常用的通信方式有:
- 文件:简单,低效,需要代码控制同步
- 管道:使用简单,默认阻塞
- 匿名管道 pipe:只存在于内核缓冲区,只能用于有血缘关系的进程
- 有名管道 FIFO:在文件系统中存在,可用于无血缘关系的进程
- 信号量:使用复杂,但开销小,操作系统本身支持信号量
- 内存映射区 mmap:进程有无血缘关系都可以
- 本地套接字 socket:稳定可靠
管道概念
通过管道,可以把一个进程的输出作为另一个进程的输入。管道分为两种:
- 匿名管道:主要用于两个进程间有父子关系的进程间通信
- 命名管道:主要用于没有父子关系的进程间通信
通常用的管道,都是匿名管道。管道实际上是一块内核缓冲区,不占用磁盘空间,但操作方式跟文件是一样的。
在 Linux 的命令行终端中,管道是再常用不过的命令技巧了。通过管道可以把前一个命令的输出作为后一个命令的输入。在 Linux 命令中通常通过符号 | 来使用管道。
例如,查看所有的进程,然后按关键字过滤:
ps aux | grep mysql
匿名管道
匿名管道是不能在文件系统中以任何方式看到的半双工管道,不占磁盘空间。管道的特点有:
- 一端只读或只写。读端、写端分别是一个文件描述符
- 操作管道的进程退出后,管道自动释放
- 管道默认是阻塞的
- 半双工,数据在管道内只能单向传输
匿名管道无法判断消息是否被读完,通常仅用于父进程向子进程传递数据,或子进程向父进程传递数据。此时可以在父子进程中关闭不用的读或写端。
管道是基于环形队列这个数据结构实现的,数据先进先出。默认的缓冲区大小是 4 KB,大小会自动调整。
- 单工:数据单向流动,例如遥控器
- 双工:数据可以同时双向流动,例如手机
- 半双工:数据可以双向流动,但不可同时双向流动,例如对讲机
pipe 函数
pipe() 函数用于创建匿名管道。.
函数原型:
#include <unistd.h>
int pipe(int pipefd[2]);
参数:返回文件描述符数组,对应管道的两端,往写端写的数据会被内核缓存起来,直到读端将数据读完。其中:
- pipefd[0]:读端
- pipefd[1]:写端
返回值:成功返回 0,否则返回-1。
示例:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd[2];
int ret, pid;
char buf;
ret = pipe(fd);
if (ret == -1)
{
perror("pipe error");
exit(1);
}
pid = fork();
if (pid == 0)
{
close(fd[1]);
while(read(fd[0], &buf, 1) > 0)
{
// 两种方式都可以实现输出
printf("%c\n", buf);
write(STDOUT_FILENO, &buf, 1);
}
close(fd[0]);
}
else if (pid > 0)
{
close(fd[0]);
write(fd[1], "hello world", 12);
close(fd[1]);
wait(NULL);
}
else
{
perror("fork error");
exit(1);
}
close(fd[0]);
close(fd[1]);
return 0;
}
命名管道
命名管道也叫 FIFO 文件,在文件系统中以文件名的形式存在,大小为0。匿名管道无法在无关进程之间通信,但 FIFO 可以借助文件系统中的文件,使得同一主机内的所有的进程都可以通信。
进程通过 FIFO 通信时,首先要打开管道文件,然后使用 read 、write 函数通信。
mkfifo 函数
函数原型:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:
- pathname:指定文件名
- mode:指定文件的读写权限
返回值:
函数成功返回 0,否则返回-1 并设置 errno,常见 errno 有:
- EACCES:pathname 所在的目录不允许执行权限
- EEXIST:pathname 已经存在
- ENOENT:目录部分不存在
- ENOTDIR:目录部分不一个目录
- EROFS:路径指向一个只读的文件系统
示例:
创建两个进程,一个将文件内容读到 FIFO,另一个从 FIFO 读内容写到另一个文件。
写 FIFO 的文件示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define BUFSIZE 1024
int main(int argc, char *argv[]){
int ret;
int datafd, fifofd;
int bytes;
char buffer[BUFSIZE];
const char *fifoname = "/tmp/fifo";
if (argc != 2) {
printf("please input filename\n");
exit(EXIT_FAILURE);
}
if (access(fifoname, F_OK) < 0) {
ret = mkfifo(fifoname, 0777);
if (ret < 0) {
perror("mkfifo error");
exit(EXIT_FAILURE);
}
}
fifofd = open(fifoname, O_WRONLY);
datafd = open(argv[1], O_RDONLY);
if (fifofd < 0) {
perror("open fifo error");
exit(EXIT_FAILURE);
}
if (datafd < 0) {
perror("open file error");
exit(EXIT_FAILURE);
}
bytes = read(datafd, buffer, BUFSIZE);
while (bytes > 0) {
ret = write(fifofd, buffer, bytes);
if (ret < 0) {
perror("write fifo error");
exit(EXIT_FAILURE);
}
bytes = read(datafd, buffer, BUFSIZE);
}
close(fifofd);
close(datafd);
return 0;
}
读 FIFO 的文件示例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define BUFSIZE 1024
int main(int argc, char *argv[]) {
char *fifoname = "/tmp/fifo";
int fifofd, datafd;
int bytes, ret;
char buffer[BUFSIZE];
if (argc != 2) {
printf("please input a filename");
exit(EXIT_FAILURE);
}
fifofd = open(fifoname, O_RDONLY);
datafd = open(argv[1], O_WRONLY);
if (fifofd < 0) {
perror("open fifo error");
exit(EXIT_FAILURE);
}
if (datafd < 0) {
perror("open file error");
exit(EXIT_FAILURE);
}
bytes = read(fifofd, buffer, BUFSIZE);
while(bytes > 0) {
ret = write(datafd, buffer, bytes);
if (ret < 0) {
perror("write file error");
exit(EXIT_FAILURE);
}
bytes = read(fifofd, buffer, BUFSIZE);
}
close(datafd);
close(fifofd);
return 0;
}
【Linux 应用编程】进程管理 - 进程间通信IPC之管道 pipe 和 FIFO的更多相关文章
- 【Linux 应用编程】进程管理 - 进程间通信IPC之共享内存 mmap
IPC(InterProcess Communication,进程间通信)是进程中的重要概念.Linux 进程之间常用的通信方式有: 文件:简单,低效,需要代码控制同步 管道:使用简单,默认阻塞 匿名 ...
- Linux系统编程@进程管理(一)
课程目标: 构建一个基于主机系统的多客户即时通信/聊天室项目 涉及的理论知识 进程控制:僵尸进程/孤儿进程.进程控制.守护进程... 进程间通信:管道.命名管道.信号... 多线程编程: 锁.信号量. ...
- Linux系统编程@进程管理(二)
1.创建守护进程(Deamon) 守护进程的概念与作用 后台服务程序 – 系统服务,进程名字往往以’d’结尾,生存周期比较长(系统装入时启动,关闭时候终止.系统装入两种启动方式:1从启动脚本.etc/ ...
- Linux系统编程@进程通信(一)
进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...
- UNIX环境高级编程——进程管理和通信(总结)
进程管理与通信 进程的管理 进程和程序的区别: 进程: 程序的一次执行过程 动态过程,进程的状态属性会发生变化 程序:存放在磁盘上的指令.数据的有序集合 是个文件,可直观看到 程序program ...
- Linux学习之进程管理(十九)
Linux学习之进程管理 进程查看 查看系统中所有进程,使用BSD操作系统的格式 语法:ps aux 选项: a:显示所有前台进程 x:显示所有后台进程 u:显示这个进程是由哪个用户产生的 语法:ps ...
- [linux] C语言Linux系统编程进程基本概念
1.如果说文件是unix系统最重要的抽象概念,那么进程仅次于文件.进程是执行中的目标代码:活动的.生存的.运行的程序. 除了目标代码进程还包含数据.资源.状态以及虚拟化的计算机. 2.进程体系: 每一 ...
- 【Linux学习】Linux系统管理1—进程管理
Linux系统管理1-进程管理 一.Linux的三种进程 Linux包括3中不同类型的进程: 交互进程:由一个shell启动的进程.交互进程可以在前后台运行 批处理进程:该进程和终端无联系,是一个进程 ...
- Linux下的进程管理
在操作系统系统中,进程是一个非常重要的概念. 一.Linux中进程的相关知识 1.什么是进程呢? 通俗的来说进程是运行起来的程序.唯一标示进程的是进程描述符(PID),在linux内核中是通过task ...
随机推荐
- Wannafly挑战赛22 C 多项式(大数,多项式极限)
链接:https://ac.nowcoder.com/acm/contest/160/C 来源:牛客网 多项式 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言 ...
- SSM框架中IoC、DI与AOP的理解
框架封装了普通项目中程序员需要重复书写的代码和调用过程,就比如说在传统的jsp项目中,我们的controller接收到前端的请求然后程序员就需要去开发Dao层,里面还涉及数据库的连接和存储过程的代码, ...
- css不常见属性之pointer-events
MDN 上介绍为 CSS 属性指定在什么情况下 (如果有) 某个特定的图形元素可以成为鼠标事件的 target.pointer-events 属性值有: /* Keyword values */ po ...
- Puppet学习笔记(CentOS6.3+Puppet3.01)
Puppet学习笔记(CentOS6.3+Puppet3.01) 技术 Add comments Oct262012 下了决心,好好学习puppet,周末专门去参加一个puppet的培训,难得朋友那 ...
- 基础习题1——print
引号内,空格就表示空格,引号外,逗号表示空格 print('100+200 =',100 + 200)===100+200 = 300 print('hello','world')===hello w ...
- 【JZOJ2156】【2017.7.10普及】复仇者vsX战警之训练
题目 月球上反凤凰装甲在凤凰之力附身霍普之前,将凤凰之力打成五份,分别附身在X战警五大战力上面辐射眼.白皇后.钢力士.秘客和纳摩上(好尴尬,汗). 在凤凰五使徒的至高的力量的威胁下,复仇者被迫逃到昆仑 ...
- 查看有没有绑这个host
1.查看有没有绑这个host ping broker.vs.amap.com
- XML 属性
XML 属性 从 HTML,你会回忆起这个:<img src="computer.gif">."src" 属性提供有关 <img> 元素 ...
- POJ 2492 A Bug's Life (带权并查集 && 向量偏移)
题意 : 给你 n 只虫且性别只有公母, 接下来给出 m 个关系, 这 m 个关系中都是代表这两只虫能够交配, 就是默认异性, 问你在给出的关系中有没有与异性交配这一事实相反的, 即同性之间给出了交配 ...
- TCP大文件上传与UDP协议
一.UCP大文件上传(解决粘包问题) ①客户端 import socket, os, json, struct client = socket.socket() client.connect(('12 ...