从一个进程连接数据到另一个进程时,使用管道(pipe),通常是把一个进程的输出通过管道连接到另一个进程的输入, linux的bash就是通过管道连接的
例如:ps |grep hh,键盘敲入ps命令,ps执行后的输出作为grep的输入,执行后输出到终端屏幕
请求popen调用执行另外一个程序的时候,首先启动的是shell,即系统中的sh,之后把参数传给它
好处:所以的参数都是shell来解析的,因此可以使shell得到拓展(如*.c所指的就是所有c文件)
坏处:针对每个popen调用,要启动一个被请求的程序,还要启动一个shell,每个popen调用都多启动两个进程,从系统资源来说成本较高,调用效率也略低
在一个popen调用中使用了cat程序、wc程序、shell程序,并进行了异常重定向,但是只能看到最终popen调用的输出结果
#include
#include
#include
#include
int main()
{
FILE *read_fp;
char buffer[BUFSIZ + 1];
int chars_read;
memset(buffer, '\0', sizeof(buffer));
read_fp = popen("cat popen*.c | wc -l", "r");//以读的方式打开管道,此时调用进程可通过fread()读取调用线程的数据
//因为不知道要读取的数据的大小,因此循环使用fread函数进行读取,直到fread函数返回的读取的数据为0
if (read_fp != NULL) {
chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
while (chars_read > 0) {
buffer[chars_read - 1] = '\0';
printf("Reading:-\n %s\n", buffer);
chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
}
pclose(read_fp);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
比popen调用更底层,对读写数据更多控制
#include
#include
#include
#include
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));//从1端写数据
printf("Wrote %d bytes\n", data_processed);
data_processed = read(file_pipes[0], buffer, BUFSIZ);//从0端读数据,数据符合先进先出的FIFO规则
printf("Read %d bytes: %s\n", data_processed, buffer);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
fork()创建的进程后,新进程的许多属性跟原进程是相同的,新进程几乎跟原进程一模一样,但是有自己的数据空间、环境和文件描述符
父进程的原来数据空间、环境和文件描述符依然存在
#include
#include
#include
#include
int main()
{
int data_processed;
int file_pipes[2];
const char some_data[] = "123";
char buffer[BUFSIZ + 1];
pid_t fork_result;
memset(buffer, '\0', sizeof(buffer));
if (pipe(file_pipes) == 0) {//主进程中创建管道
fork_result = fork();//创建一个分离的子进程
if (fork_result == -1) {
fprintf(stderr, "Fork failure");
exit(EXIT_FAILURE);
}
// We've made sure the fork worked, so if fork_result equals zero, we're in the child process.
if (fork_result == 0) {//子进程中动作——读
data_processed = read(file_pipes[0], buffer, BUFSIZ);
printf("Read %d bytes: %s\n", data_processed, buffer);
exit(EXIT_SUCCESS);
}
// Otherwise, we must be the parent process.
else {//父进程中动作——写
data_processed = write(file_pipes[1], some_data,
strlen(some_data));
printf("Wrote %d bytes\n", data_processed);
}
}
exit(EXIT_SUCCESS);
}
先用fork()创建一个跟主进程分离开的子进程,再在子进程中使用exec系列函数,从子进程中创建一个复制进程,
此时的情况是:子进程已被exec创建的进程替代了,要想在复制进程中使用主进程的数据,该数据必须作为参数,在exec创建进程的时候传递过去
/程序1——生产者/
#include
#include
#include
#include
int main()
{
int data_processed;
int file_pipes[2];
const char some_data[] = "123";
char buffer[BUFSIZ + 1];
pid_t fork_result;
memset(buffer, '\0', sizeof(buffer));
if (pipe(file_pipes) == 0) {//程序1的主进程创建管道
fork_result = fork();
if (fork_result == (pid_t)-1) {
fprintf(stderr, "Fork failure");
exit(EXIT_FAILURE);
}
if (fork_result == 0) {//已经在由fork创建的相对独立的进程内,此时再调用execl函数替换进程为Ppip4
sprintf(buffer, "%d", file_pipes[0]);
(void)execl("pipe4", "pipe4", buffer, (char *)0);//子进程内调用execl函数创建一个子进程的复制进程
exit(EXIT_FAILURE);
}
else {
data_processed = write(file_pipes[1], some_data,
strlen(some_data));//程序1的父进程动作,向管道写数据
printf("%d - wrote %d bytes\n", getpid(), data_processed);
}
}
exit(EXIT_SUCCESS);
}
/程序2——消费者 程序名:pipe4/
// The 'consumer' program, pipe4.c, that reads the data is much simpler.
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int data_processed;
char buffer[BUFSIZ + 1];
int file_descriptor;
memset(buffer, '\0', sizeof(buffer));
sscanf(argv[1], "%d", &file_descriptor);
data_processed = read(file_descriptor, buffer, BUFSIZ);//程序2的动作,从管道中读数据
printf("%d - read %d bytes: %s\n", getpid(), data_processed, buffer);
exit(EXIT_SUCCESS);
}
#include
#include
#include
#include
int main()
{
int data_processed;
int file_pipes[2];
const char some_data[] = "123";
pid_t fork_result;
if (pipe(file_pipes) == 0) {//创建管道,父子进程共4个文件描述符,t file_pipes[2]各有一份副本在父子进程
fork_result = fork();//创建分离进程
if (fork_result == (pid_t)-1) {
fprintf(stderr, "Fork failure");
exit(EXIT_FAILURE);
}
if (fork_result == (pid_t)0) {//在子进程中
close(0);//关闭标准输入
dup(file_pipes[0]);//打开一个新的文件描述符,该dup()函数总取最小的可用值,因此管道输入——》标准输入
close(file_pipes[0]);//关闭管道原来用来读取的数据的文件描述
close(file_pipes[1]);//因为子进程中不会向管道写输入,故关闭
execlp("od", "od", "-c", (char *)0);
exit(EXIT_FAILURE);
}
else {//现在在父进程中
close(file_pipes[0]);//父进程不会从管道读取数据,因此关闭管道读取端
data_processed = write(file_pipes[1], some_data,
strlen(some_data));
close(file_pipes[1]);//父进程向管道写完数据后,关闭管道写入端
printf("%d - wrote %d bytes\n", (int)getpid(), data_processed);
}
}
exit(EXIT_SUCCESS);
}
主要用在不相关的进程间交换数据
mode_t mode=0777,umask=0022,则创建的特殊文件的权限值为755
例子如下:
程序中没有删除创建的文件,时因为我们无法知道是否有其他程序正在使用它
// Let's start with the header files, a #define and the check that the correct number
// of command-line arguments have been supplied.
#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "/tmp/my_fifo"
int main(int argc, char *argv[])
{
int res;
int open_mode = 0;
int i;
if (argc < 2) {
fprintf(stderr, "Usage: %s \n", *argv);
exit(EXIT_FAILURE);
}
// Assuming that the program passed the test, we now set the value of open_mode
// from those arguments.
for(i = 1; i < argc; i++) {
if (strncmp(*++argv, "O_RDONLY", 8) == 0)
open_mode |= O_RDONLY;
if (strncmp(*argv, "O_WRONLY", 8) == 0)
open_mode |= O_WRONLY;
if (strncmp(*argv, "O_NONBLOCK", 10) == 0)
open_mode |= O_NONBLOCK;
}
// We now check whether the FIFO exists and create it if necessary.
// Then the FIFO is opened and output given to that effect while the program
// catches forty winks. Last of all, the FIFO is closed.
if (access(FIFO_NAME, F_OK) == -1) {
res = mkfifo(FIFO_NAME, 0777);//创建命令管道文件
if (res != 0) {
fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO\n", getpid());
res = open(FIFO_NAME, open_mode);//打开命名管道文件
printf("Process %d result %d\n", getpid(), res);
sleep(5);
if (res != -1) (void)close(res);//关闭命名管道文件
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
使用方式1:
//以读的方式打开命名管道,open调用将阻塞,直到另一个进程已写方式打开命名管道
$./fifo2 O_RDONLY &
//以写的方式打开命名管道,open函数将阻塞,直到另一个进程以读方式打开命名管道
$./fifo2 O_WDONLY
使用方式2:
//读方式+标志模式打开管道,open函数不会阻塞
$./fifo2 O_RDONLY O_NONBLOCK &
$./fifo2 O_WDONLY
使用方式3:
//写方式+标志模式打开管道
$./fifo2 O_WDONLY O_NONBLOCK &
$./fifo2 O_RDONLY
//生产者程序fifo3.c
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF //定义在limit.h中,在linux和unix系统中一般是4096
#define TEN_MEG (1024 * 1024 * 10) //单次进入管道的长度
int main()
{
int pipe_fd;
int res;
int open_mode = O_WRONLY;
int bytes_sent = 0;
char buffer[BUFFER_SIZE + 1];
if (access(FIFO_NAME, F_OK) == -1) {//检测FIFO管道文件是否存在
res = mkfifo(FIFO_NAME, 0777);//若不存在则创建管道文件
if (res != 0) {
fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);//以读的方式打开管道,此时阻塞直到有进程以写方式打开管道
printf("Process %d result %d\n", getpid(), pipe_fd);
if (pipe_fd != -1) {
while(bytes_sent < TEN_MEG) {
res = write(pipe_fd, buffer, BUFFER_SIZE);
if (res == -1) {
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
bytes_sent += res;
}
(void)close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
//消费者程序fifo4.c
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF
int main()
{
int pipe_fd;
int res;
int open_mode = O_RDONLY;
char buffer[BUFFER_SIZE + 1];
int bytes_read = 0;
memset(buffer, '\0', sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);//以读的方式打开管道,阻塞,直到有进程以写的方式打开管道
printf("Process %d result %d\n", getpid(), pipe_fd);
if (pipe_fd != -1) {
do {
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes_read += res;
} while (res > 0);
(void)close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}