我是靠谱客的博主 高贵夏天,这篇文章主要介绍进程通信1——管道通信,现在分享给大家,希望可以做个参考。

管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道尾部写入数据,另一个进程(读进程)从管道的头部读出数据。
两个程序之间传递数据的一种简单方法是使用popen和pclose。

复制代码
1
2
3
#include <stdio.h> FILE *popen(const char *command, const char *type); int pclose(FILE *stream);
复制代码
1
2
3
4
popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串是要运行的程序名和相应的参数。type必须是"r"或"w"。 如果type是"r",被调程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE *文件流指针,可以读取被调程序的输出;如果type是"w",调用程序就可以向被调程序发送数据,而被调程序可以在自己的标准输入上读取这些数据。 pclose函数只在popen启动的进程结束后才返回。如果调用pclose时它仍在运行,pclose将等待该进程的结束。

案例:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int main() { FILE *fp = popen("ps -ef", "r"); if (fp == NULL) { perror ("popen"); return -1; } char buf[SIZE] = {0}; int ret = fread(buf, sizeof(char), SIZE-1, fp); // printf ("读到的数据:n %sn", buf); FILE *fp2 = popen("grep a.out", "w"); if (fp2 == NULL) { perror ("popen"); return -1; } fwrite (buf, sizeof(char), ret, fp2); printf ("写入完成n"); pclose (fp); pclose (fp2); return 0; }

从案例可以看出通过popen这个函数可以实现与终端语句ps -ef | grep a.out相同的功能。

管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。

无名管道由pipe()函数创建:
int pipe(int filedis[2]);
当一个管道建立时,它会创建两个文件描述符:
filedis[0]用于读管道,filedis[1]用于写管道。
案例:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int main() { int fd[2]; int ret = pipe(fd); if (ret == -1) { perror ("pipe"); return -1; } ret = write (fd[1], "hello", 5); printf ("写入 %d 个字节n", ret); char ch; while (1) { // 如果管道里面没有数据可读,read会阻塞 ret = read (fd[0], &ch, 1); if (ret == -1) { perror ("read"); break; } printf ("读到 %d 字节: %cn", ret, ch); } close (fd[0]); close (fd[1]); return 0; }

从案例可以看出通过pipe 这个函数,可以创建一个管道,fd[0]用于读,f[1]用于写。

管道用于不同进程间通信。通常先创建一个管道,在通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道描述符。
案例:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
void child_do(int *fd) { // 将管道的写端关闭 close (fd[1]); char buf [SIZE]; while (1) { // 从父进程读取数据 int ret = read (fd[0], buf, SIZE-1); if (ret == -1) { perror ("read"); break; } buf[ret] = ''; printf ("子进程读到 %d 字节数据: %sn", ret, buf); } // 关闭读端 close (fd[0]); } // 父进程通过管道向子进程发送数据 void father_do(int *fd) { // 将管道读端关闭 close (fd[0]); char buf[SIZE]; while (1) { fgets (buf, SIZE, stdin); // 向子进程发送数据 int ret = write (fd[1], buf, strlen(buf)); printf ("父进程发送了 %d 字节数据n", ret); } // 关闭写端 close (fd[1]); } int main() { int fd[2]; // 创建管道 int ret = pipe(fd); if (ret == -1) { perror ("pipe"); return -1; } // 创建子进程 pid_t pid = fork(); switch (pid) { case -1: perror ("fork"); break; case 0: // 子进程 child_do(fd); break; default: father_do(fd); break; } return 0; }

在这个案例中,父进程用来写,子进程则用来读,这样父子进程,就可以通过管道进行通信了。

命名管道(FIFO)和无名管道基本相同,但也有不同点:无名管道只能由父子进程使用;但是通过命名管道,不相关的进程也能交换数据。

复制代码
1
2
3
4
5
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);

pathname: FIFO文件名
mode:属性(同文件操作)

一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。
命名管道的创建比较简单,就不多说了,下面介绍一个用命名管道进行的信息交互的案例:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main() { int fd = open("/home/mkfifo", O_WRONLY); if (fd== -1) { perror ("mkfifo"); return -1; } char buf[SIZE]; while (1) { fgets (buf, SIZE, stdin); write (fd, buf, strlen(buf)); } return 0; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main() { int fd = open("/home/mkfifo", O_RDWR); if (fd == -1) { perror ("mkfifo"); return -1; } char buf[SIZE]; while (1) { int ret = read (fd, buf, SIZE); buf[ret] = ''; printf ("读到 %d 字节: %sn", ret, buf); } return 0; }

在打开两个命名管道后,一个进行读操作,一个进行写操作。

关闭管道只需要将两个文件描述符关闭即可,可以使用普通的close函数逐个关闭。

最后

以上就是高贵夏天最近收集整理的关于进程通信1——管道通信的全部内容,更多相关进程通信1——管道通信内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(95)

评论列表共有 0 条评论

立即
投稿
返回
顶部