Linux管道(pipe)的那些事
来源:互联网 发布:秦舞阳13岁杀人知乎 编辑:程序博客网 时间:2024/06/08 17:05
1 管道(pipe)
1.1 实现机制
1.2 操作举例
- ls | head -3
- barry.txt
- bob
- example.png
- ls -l /etc | less
- (Full screen of output you may scroll. Try it yourself to see.)
1.3 命名管道
由于基于fork机制的限制,管道只能用于父进程和子进程之间,或者有相同祖先的两个子进程之间的通信。为了解决这一问题,Linux提供了FIFO方式连接进程。FIFO又叫做命名管道(named PIPE)。
FIFO (First in, First out)为一种特殊的文件类型,它在文件系统中有对应的路径。FIFO只是借用了文件系统来为管道命名。当一个进程以读(r)的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道,当删除FIFO文件时,管道连接也随之消失。所以FIFO实际上也由内核管理,不与硬盘打交道。之所以叫FIFO,是因为管道本质上是一个先进先出的队列数据结构,最早放入的数据被最先读出来,从而保证信息交流的顺序。命名管道的好处在于我们可以通过文件的路径来识别管道,从而让没有亲缘关系的进程之间建立连接。
用ls命令查看所创建的管道:
$ ls -lF /tmp/my_fifo
prwxr-xr-x 1 root root 0 05-08 20:10 /tmp/my_fifo|
2 fork
2.1 进程
在说fork之前,我们先来复习一下操作系统中进程的相关内容:Linux中有一个叫进程表的结构用来存储当前正在运行的进程。可以使用“ps aux”命令查看所有正在运行的进程。
进程在linux中呈树状结构,init为根节点,其它进程均有父进程,某进程的父进程就是启动这个进程的进程,这个进程叫做父进程的子进程。
2.2 fork
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本,子进程数据空间中的内容是父进程的完整拷贝。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间,它们之间共享的存储空间只有代码段。,但只有一点不同,如果fork成功,子进程中fork的返回值是0, 父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。
从性能方面考虑,父进程到子进程的数据拷贝并不是创建时就拷贝了的,而是采用了写时拷贝(copy-on -write)技术来处理。用fork创建的子进程和父进程作为异步的并发进程而单独执行,它们都有独自的进程标识符(PID)。异步是指它们各行其事,相互间不进行同步;并发是指它们可同时执行。所以我们无法知道子进程和父进程哪一个先执行完。
2.3 举例
- #include <unistd.h>
- #include <stdio.h>
- int main ()
- {
- pid_t fpid; //fpid表示fork函数返回的值
- int count=0;
- fpid=fork();
- if (fpid < 0)
- printf("Error in fork!");
- else if (fpid == 0) {
- printf("I'm child process, my process id is %d\n",getpid());
- count++;
- }
- else {
- printf("I'm parent process, my process id is %d\n",getpid());
- count++;
- }
- printf("Count value: %d\n", count);
- return 0;
- }
i'm child process, my process id is 5574
Count value: 1
i'm parent process, my process id is 5573
Count value: 1
每个进程的PID都可以通过getpid()函数获得,另外还可以通过getppid()函数获得其父进程的PID.
调用fork()(fpid=fork())后生成一个子进程,在子进程中,fork()函数的返回值为0,在父进程中,fork()的返回值为子进程的PID。此后,两个进程根据不同的判断条件(fpid<0; fpid==0)执行不同的代码指令,它们的执行是相互独立的。这两个进程都有一个count变量,这两个变量虽然值相等,但其实它们属于不同的进程,是不同的变量,存放在不同的内存地址中。另外,子进程生成之后是从fork()函数之后的代码开始执行的,而不是从#include <unistd.h>处。 这是因为fork操作复制并使用了原进程的程序计数器的缘故。
3. 文件描述符(file descriptor)
3.1 文件描述符
3.2 与文件有关的三个表
3.3 输入输出重定向
4.重定向文件描述符
exec 3>&1 #将文件描述符3重定向至1,任何发送给文件描述符3的内容都将输出至终端显示器
exec 1>file #将发送至文件描述符1的内容重定向至文件file
echo "this should be put in the file"
exec 1>&3 #将此时的标准输出重定向至文件描述符3,而3指向的是终端显示器,因此此时正常输出至显示器
echo "this is the normal output"
3.4 文件描述符的设置
为了防止系统资源的耗尽,linux内核对文件打开的数量进行了限制。这种限制有两个层面,一个是用户层面的限制,一个是系统层面的限制。
ulimit命令看到的是用户级的最大文件描述符限制,也就是说每一个用户登录后执行的程序占用文件描述符的总数不能超过这个限制
[root@localhost ~]# ulimit -n
10240
设置进程能打开的最大文件句柄数:ulimit -n xxx
[root@localhost ~]# ulimit -n 10240
10240
sysctl命令和proc文件中查看到的数值是一样的,这属于系统级限制,它是限制所有用户打开文件描述符的总和
[root@localhost ~]# sysctl -a | grep -i file-max --color
fs.file-max = 392036
[root@localhost ~]# cat /proc/sys/fs/file-max
392036
修改系统层面的限制需要修改/proc/sys/fs/file-max中的值并且使用"sysctl -p"使之永久生效。3.5 文件描述符复制与管道的建立
为了生成linux中的管道,首先使用pipe()函数得到一对文件描述符,它们是只读文件描述符和只写文件描述符。fork()函数执行之后,子进程会将父进程的数据拷贝一份,同样,子进程也会拥有父进程所有文件描述符的副本。这时在父进程中关闭读文件描述符,只留下写文件描述符;而在子进程则关闭写文件描述符,只留下读文件描述符。当父进程进行写操作而子进程进行读操作时,就相当于两个进程在通信,管道就形成了。其实,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。
管道建立图示:
- Linux管道(pipe)的那些事
- linux脚本编程---“|”管道pipe的使用
- linux下的pipe()管道函数
- Linux Shell 管道命令(pipe)的使用
- Linux管道PIPE的原理和应用
- linux--管道pipe
- Linux 管道(pipe)
- Linux pipe(管道)
- linux管道pipe
- linux管道(pipe)
- linux管道pipe详解
- 【linux】匿名管道pipe
- Linux 管道pipe
- linux管道pipe详解
- linux pipe 无名管道
- linux管道pipe详解
- Linux下的管道pipe----管道容量和实现机制
- linux 管道(linux Pipe与named Pipe)
- init: function() {表示什么意思
- HTML基础
- iOS ShareSDK微博分享失败无反应,没有输出失败的原因之一..
- jQuery中的隐藏和显示
- 使用EL表达式正确情况下报错:javax.servlet.jsp cannot be resolved to a type
- Linux管道(pipe)的那些事
- openssl 的编译 (windows)
- Variant Call Format (VCF) 笔记
- 《SLA By Short Brain》六原则
- 关于ssdb编译的小问题-cannot stat `ssdb-server': No such file or directory
- ES6爬坑之路之const关键字
- 利用随机数函数掷骰子
- activity生命周期(这篇足够了)
- MVP泛型+xRecyclerView+Retrofit+OkHttp+RxJava多条目,横向