进程间通信: 管道

xiaoxiao2021-02-28  109

管道

管道可以实现在两个进程间通信, 通过使用popen和pclose函数, 其原型如下:

#include <stdio.h> FILE *popen(const char* command, const char *open_mode); int pclose(FILE *steam_to_close);

open函数允许一个程序将另一个程序作为新的进程启动, 并传递数据给它或者接收它传递的数据。 command是要运行的程序名和相应的参数。 open_mode 必须是”r” 或者”w”; r表示获取到一个可读的FILE, 可以从中读取command程序的数据。 w表示获取到一个可写的FILE,可以向其写数据, command程序可以读取写过来的数据。 每个popen调用必须是r或者w,不能同时进行读写, 如果要想实现双向通信, 可以使用两个管道。

pipe调用

#include <unistd.h> int pipe(int file_descriptor[2]);

file_descriptor是两个整数类型的文件描述符组成的数组指针。调用成功后返回0, 失败返回-1,并设置errno。 pipe将两个文件描述符以一种特殊的方式连接起来, 写到file_descriptor[1]中的数据都可以从file_descriptor[0]中读出来。而且只能这样读写, 反过来调用将失败。数据基于FIFO原则进行处理, 即读出顺序是按写入顺序来的。

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(){ int data_processed; int file_pipes[2]; const char some_data[] = "123"; char buffer[BUFSIZ + 1]; memset(buffer, '\0', sizeof(buffer)); if (pipe(file_pipes) == 0) { data_processed = write(file_pipes[1], some_data, strlen(some_data)); printf("Wrote %d bytes\n", data_processed); data_processed = read(file_pipes[0], buffer, BUFSIZ); printf("Read %d bytes: %s\n", data_processed, buffer); exit(EXIT_SUCCESS); } exit(EXIT_FAILURE); }

这个程序创建一个管道, 然后用文件描述符file_pipes[1]写数据, 用file_pipes[0]中读出数据。 编译输出结果如下:

$ ./pipe1 Wrote 3 bytes Read 3 bytes: 123

fork创建新进程时, 原先打开的文件描述符仍是打开的状态。 如果在原先的进程中创建一个管道, 然后再调用fork创建新进程,即可通过管道在两个进程间通信。

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main() { int data_processed; int file_pipes[2]; const char some_data[] = "123"; char buffer[BUFSIZ + 1]; pid_t child_pid; memset(buffer, '\0', sizeof(buffer)); if (pipe(file_pipes) == 0) { child_pid = fork(); if (child_pid == -1) { fprintf(stderr, "fork failure..."); exit(EXIT_FAILURE); } if (child_pid == 0) { data_processed = read(file_pipes[0], buffer, BUFSIZ); printf("Read %d bytes: %s\n", data_processed, buffer); exit(EXIT_SUCCESS); } else { data_processed = write(file_pipes[1], some_data, strlen(some_data)); printf("Wrote %d bytes\n", data_processed); } } exit(EXIT_SUCCESS); }

上面这个程序通过pipe调用创建一个管道,fork创建一个子进程,在主线程中写入数据,并在子线程中读出。

$ ./pipe2 Wrote 3 bytes Read 3 bytes: 123

命名管道

命名管道是特殊类型的文件, 在文件系统中文件名的形式存在, 其行为与上面的管道类似。 如果想在不相关的进程间通信, 可以采用命名管道。 命令行创建命名管道方法是mkfifo filename。 在程序中可以通过调用函数mkfilo。其原型是

#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *filename, mode_t mode);

程序:

#include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { int res = mkfifo("/tmp/test_fifo", 0777); if (res == 0) { printf("FIFO created\n"); exit(EXIT_SUCCESS); } } $ ls -lF /tmp/ prwxrwxr-x 1 wlyuan wlyuan 0 57 17:25 test_fifo|

输出结果中第一字符p表示这是一个管道, 删除可以通过rm或者unlink调用。

使用test_fifo

$ cat < /tmp/test_fifo & [2] 5629 $ echo "test fifo" > /tmp/test_fifo $ test fifo

open打开fifo命名管道

与打开普通文件的区别: 1. 不能以O_RDWR方式打开fifo,结果未定义。如果想实现在进程间双向传递数据, 最好使用一对fifo。 2. open_flag(open函数的第二个参数) O_NONBLOCK的用法。

open(const char *path, O_RDONLY);

open调用将阻塞, 除非有一个进程以写的方式打开同一个FIFO,否则它不会返回。

open(const char *path, O_RDONLY | O_NONBLOCK);

这个open调用将成功并立即返回,即使没有其他进程以写的方式打开fifo。

open(const char *path, O_WRONLY);

这种方式将阻塞,直到有进程以读的方式打开同一个fifo。

open(const char *path, O_WRONLY | O_NONBLOCK);

立即返回, 如果没有进程以读的方式打开fifo, 将返回错误-1, fifo也不会打开。

一个客户服务器例子 该例子是通过客户端向服务器server管道写入数据, 服务器端将读取到客户端写入的数据, 将其转换成大写字母, 再写入客户端的管道, 客户端读取结果显示。 client.h

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <fcntl.h> #include <limits.h> #include <sys/types.h> #include <sys/stat.h> #define SERVER_FIFO_NAME "/tmp/serv_fifo" #define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo" #define BUFFER_SIZE 20 struct data_to_pass_st { pid_t client_pid; char some_data[BUFFER_SIZE-1]; };

server.c

#include "client.h" #include <ctype.h> int main() { int server_fifo_fd, client_fifo_fd; struct data_to_pass_st my_data; int read_res; char *tmp_char_ptr; char client_fifo[256]; mkfifo(SERVER_FIFO_NAME, 0777); server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY); if (server_fifo_fd == -1) { fprintf(stderr, "server fifo open failure..."); exit(EXIT_FAILURE); } sleep(10); do { read_res = read(server_fifo_fd, &my_data, sizeof(my_data)); if (read_res > 0) { tmp_char_ptr = my_data.some_data; while(*tmp_char_ptr) { *tmp_char_ptr = toupper(*tmp_char_ptr); tmp_char_ptr++; } } sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid); client_fifo_fd = open(client_fifo, O_WRONLY); if (client_fifo_fd != -1) { write(client_fifo_fd, &my_data, sizeof(my_data)); close(client_fifo_fd); } } while(read_res > 0); close(server_fifo_fd); unlink(SERVER_FIFO_NAME); exit(EXIT_SUCCESS); }

client.c

#include "client.h" #include <ctype.h> int main() { int server_fifo_fd, client_fifo_fd; struct data_to_pass_st my_data; int times_to_send; char client_fifo[256]; server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY); if (server_fifo_fd == -1) { fprintf(stderr, "open server fifo failure..."); exit(EXIT_FAILURE); } my_data.client_pid = getpid(); sprintf(client_fifo, CLIENT_FIFO_NAME, getpid()); if (mkfifo(client_fifo, 0777) == -1) { fprintf(stderr, "client open fifo failure..."); exit(EXIT_FAILURE); } for (times_to_send = 0; times_to_send < 5; times_to_send++) { sprintf(my_data.some_data, "data from %d", my_data.client_pid); printf("%d send %s, ", my_data.client_pid, my_data.some_data); write(server_fifo_fd, &my_data, sizeof(my_data)); client_fifo_fd = open(client_fifo, O_RDONLY); if (client_fifo_fd != -1) { if (read(client_fifo_fd, &my_data, sizeof(my_data)) > 0) { printf("received: %s\n", my_data.some_data); } close(client_fifo_fd); } } close(server_fifo_fd); unlink(client_fifo); exit(EXIT_SUCCESS); }

编译完成后, 通过下面shell脚本进行测试

#!/bin/sh ./server & for i in 1 2 3 4 5 do ./client & done
转载请注明原文地址: https://www.6miu.com/read-41397.html

最新回复(0)