版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liu_zhen_kai/article/details/82020719
管道的概念
在之前的进程控制中,常会用到 | 将两个命令连起来来进行对显示信息的筛选,从而改变标准的标准输出方式。
[liu@localhost ~]$ ps -ef | grep 'ssh'
root 2329 1 0 04:20 ? 00:00:00 /usr/sbin/sshd
root 3378 2329 0 04:28 ? 00:00:00 sshd: liu [priv]
liu 3382 3378 0 04:28 ? 00:00:01 sshd: liu@pts/1
root 4052 2329 0 05:50 ? 00:00:00 sshd: liu [priv]
liu 4056 4052 0 05:50 ? 00:00:00 sshd: liu@pts/2
root 4244 2329 0 06:18 ? 00:00:00 sshd: liu [priv]
liu 4248 4244 0 06:18 ? 00:00:00 sshd: liu@pts/3
liu 4271 4249 0 06:19 pts/3 00:00:00 grep ssh
PIPE无名管道是一种特殊的文件类型,在内核中对应的资源即一段特殊的内存空间(缓冲区),内核在这段空间以循环队列的方式来实现一个进程向另外一个进程发生信息,这段空间由内核维护。
PIPE无名管道和普通文件差别很大,PIPE的生命周期与两个进程一样,在进程退出时,系统回收资源。可以用read,write进行操作,但是不支持lseek修改当前读写位置,因为要遵循FIFO原则。
- PIPE无名管道只能用于具有亲缘关系的进程之间进行通信。
无名管道的工作原理
打开无名管道,返回两个文件描述符,一个读,一个写,在使用的时候关闭一个,然后另外一个进程关闭另一个,就实现了管道通信。
图解:
无名管道文件的操作
在Linux C编程中使用无名管道前要先创建无名管道,函数声明如下:
int pipe(int pipefd[2]);
pipe() creates a pipe, a unidirectional data channel that can be
used for interprocess communication. The array pipefd is used to
return two file descriptors referring to the ends of the pipe.
pipe接口创建一个管道,可以用来进程之间的通信
参数pipefd[2]:
pipefd[0] refers to the read end of the pipe. pipefd[1] refers to
the write end of the pipe.
pipefd[0]代表只从管道读 pipefd[1]代表只写如管道
例子
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5
6 int main(void)
7 {
8 int pipefd[2];
9 if(pipe(pipefd) < 0)
10 {
11 perror("pipe"),
12 exit(1);
13 }
14
15 pid_t pid = fork();
16
17 if(pid < 0)
18 {
19 perror("fork"),
20 exit(1);
21 }
22 else if(pid == 0)
23 {
24 close(pipefd[1]);
25 char buff[1000];
26 read(pipefd[0], buff, 999);
27 printf("this is child process : %s\n", buff);
28 }
29
30 close(1);
31 write(pipefd[1], "this is from parents", 30);
32
33 return 0;
34 }
结果
[liu@localhost 05]$ ./pipe
this is child process : this is from parents
//子进程接收到了来自父进程的信息
在使用无名管道的时候注意:
- 当以阻塞方式读无名管道,如果当前没有一个进程打开了该管道的写端,读操作都按以下方式返回
1.如果管道无数据,立即返回0
2.如果管道有数据大于要读出的数据,立即读取期望大小的数据
3.如果管道数据小于期望读出数据,立即读取所有数据并返回
- 如有以阻塞方式读无名管道,某个进程可以访问管道的写端,按如下操作:
1.管道中无任何数据,读操作阻塞
2.管道中有数据小于期望数据,读取现有数据返回
3.如果管道有数据大于要读出的数据,读取期望大小的数据返回
- 如以阻塞方式写无名管道,如果当前没任何一个进程可以访问读端,那么写操作(进程)将会收到SIGPIPE信号,write调用将返回-1,如果当前有进程访问读端,而且管道中有空间,则写入成功
- 如果以阻塞方式写无名管道,若当前管道已满,那么写操作将会阻塞挂起,如果当前有多个进程尝试写入操作,当一个进程唤醒写入操作时,唤醒哪个未知,因此,多个进程对管道的写入可能存在交叉性写入的风险,多个进程写操作时需要设置对应的避免竞争的机制同时建议每次读取的数据小于PIPE_BUF
文件重定向操作
文件重定向符号:
- < 文件输入重定向
- >/>>输出重定向 不追加/追加
其中标准输出和标准输入是不需要指名,但是标准错误需要
几个例子:
cat<tmp.txt//cat将tmp.txt内容作为标准输入
cat>test02<test01//将正确信息输入到test02,在将标准输入重新定向到test01
cat>test02 2>error < test01//将标准输出输出到test02 将标准错误输出输出到文件error 标准输入重新定向为test01
cat>test02 2>&1<test01//将命令的正确信息输入到test02 将标准错误输出定向到1也是test02,
将test01作为标准输入
cat 2>&1 1>test02<test01//标准错误输出定向到1,此时1还是定向为标准输出,所以标准错误输出输出到显示终端,标准输出输出到test02 而将其标准输入重新定向为test01
文件重定向编程:
- dup
dup() uses the lowest-numbered unused descriptor for the new descriptor.
int dup(int oldfd);//dup,用打开的某个描述符复制一个新的文件描述符,返回的文件描述符必定是当前文件系统中最小的。
- dup2
dup2() makes newfd be the copy of oldfd
int dup2(int oldfd, int newfd);//新文件描述符复制旧文件描述符对应的文件
综合运用:实现代码实现 ps -ef | grep ‘ssh’
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5
6 int main()
7 {
8 int fd[2];
9 if(pipe(fd) < 0)
10 {
11 perror("pipe"),
12 exit(1);
13 }
14
15 pid_t pid = fork();
16
17 if(pid < 0)
18 {
19 perror("fork"),
20 exit(1);
21 }
22 else if(pid == 0)
23 {
24 close(fd[1]);
25 dup2(fd[0], 0);
26 execlp("grep", "grep", "ssh", NULL);
27 }
28 else
29 {
30 close(fd[0]);
31 dup2(fd[1], 1);
32 execlp("ps", "ps", "-ef", NULL);
33 }
34 }
[liu@localhost Pipe]$ cc ps_ssh.c -o psh
[liu@localhost Pipe]$ ./psh
root 2375 1 0 09:56 ? 00:00:00 /usr/sbin/sshd
root 2639 2375 0 09:57 ? 00:00:00 sshd: liu [priv]
liu 2643 2639 0 09:57 ? 00:00:00 sshd: liu@pts/0
liu 2924 2923 0 11:29 pts/0 00:00:00 grep ssh
[liu@localhost Pipe]$