Linux程序设计--13章(管道)

#include <stdio.h>

       FILE *popen(const char *command, const char *open_mode);

       int pclose(FILE *stream);
poen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据
command字符串是要运行的程序名和响应的参数,open_mode是"r"或是"w"
返回的是文件流指针,可用标准库函数来操作
每个管道只能单向通信
pclose返回值是它所关闭的文件流所在进程的退出码
若在close前调用了一个wait则进程退出状态就会丢失,并返回-1设置errno为ECHILD
例:

1)从文件流管道读取数据
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    FILE *read_fp;//定义文件流指针
    char buffer[BUFSIZ + 1];
    int chars_read;
    memset(buffer, '\0', sizeof(buffer));
    //调用popen
    read_fp = popen("uname -a", "r");
    if (read_fp != NULL) {
	    //从文件流指针里读数据
        chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
        if (chars_read > 0) {
            printf("Output was:-\n%s\n", buffer);
        }
	//关闭文件流指针
        pclose(read_fp);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
}

向文件流管道里写数据

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    FILE *write_fp;
    char buffer[BUFSIZ + 1];
	//将第二个参数写到第一个里面
    sprintf(buffer, "Once upon a time, there was...\n");
    //以写方式调用popen
    write_fp = popen("od -c", "w");
    if (write_fp != NULL) {
	    //向文件流写数据
        fwrite(buffer, sizeof(char), strlen(buffer), write_fp);
	//关闭文件流
        pclose(write_fp);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
}


循环读取大量数据:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    FILE *read_fp;
    char buffer[BUFSIZ + 1];
    int chars_read;

    memset(buffer, '\0', sizeof(buffer));
    read_fp = popen("ps ax", "r");
    if (read_fp != NULL) {
        chars_read = fread(buffer, sizeof(char), BUFSIZ/10, read_fp);
	//循环读取大量数据
        while (chars_read > 0) {
            buffer[chars_read - 1] = '\0';
            printf("Reading %d:-\n %s\n", BUFSIZ/10, buffer);
            chars_read = fread(buffer, sizeof(char), BUFSIZ/10, read_fp);
        }
        pclose(read_fp);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
}
如何实现popen?
请求popen调用一个程序时,它首先启动shell,然后将command字符串作为一个参数传递给它
优点:
可以利用shell来分析命令字符串,故而可以通过popen启动非常复杂的shell命令
缺点:
针对每个popen调用,不仅要启动一个被请求的程序,还要启动一个shell
popen启动shell例子:


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    FILE *read_fp;
    char buffer[BUFSIZ + 1];
    int chars_read;

    memset(buffer, '\0', sizeof(buffer));
    //利用shell的参数拓展启动复杂的命令
    read_fp = popen("cat popen*.c | wc -l", "r");
    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);
}

pipe调用:
#include <unistd.h>

       int pipe(int pipefd[2]);

小例子:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    int data_processed;
    int file_pipes[2];
    const char some_data[] = "123";
    char buffer[BUFSIZ + 1];
	//清空buffer
    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);
}

跨越fork的管道
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

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);
}
子进程中调用exec启动新程序并用管道传递数据:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

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 == (pid_t)-1) {
            fprintf(stderr, "Fork failure");
            exit(EXIT_FAILURE);
        }

        if (fork_result == 0) {//子进程
            sprintf(buffer, "%d", file_pipes[0]);//file_pipes[0]存到buffer里
	    //调用新程序并传递参数
            (void)execl("pipe4", "pipe4", buffer, (char *)0);
            exit(EXIT_FAILURE);
        }
        else {//父进程向管道里写数据
            data_processed = write(file_pipes[1], some_data,
                                   strlen(some_data));
            printf("%d - wrote %d bytes\n", getpid(), data_processed);
        }
    }
    exit(EXIT_SUCCESS);
}

// The 'consumer' program, pipe4.c, that reads the data is much simpler.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int data_processed;
    char buffer[BUFSIZ + 1];
    int file_descriptor;

    memset(buffer, '\0', sizeof(buffer));

    //把argv[1]里的数据写道file_fescriptor中
    sscanf(argv[1], "%d", &file_descriptor);
    //从管道里读数据
    data_processed = read(file_descriptor, buffer, BUFSIZ);

    printf("%d - read %d bytes: %s\n", getpid(), data_processed, buffer);
    exit(EXIT_SUCCESS);
}

重定向
#include <unistd.h>

       int dup(int oldfd);
       int dup2(int oldfd, int newfd);
dup的作用是打开一个最小的可用描述符并将其指向参数描述符指向的文件
dup2的作用与之类似,将newfd指向oldfd指向的文件
例子:


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    int data_processed;
    int file_pipes[2];
    const char some_data[] = "123";
    pid_t fork_result;
//创建管道
    if (pipe(file_pipes) == 0) {
	    //创建子进程
        fork_result = fork();
        if (fork_result == (pid_t)-1) {
            fprintf(stderr, "Fork failure");
            exit(EXIT_FAILURE);
        }

        if (fork_result == (pid_t)0) {//子进程
		//关闭标准输入
            close(0);
	    //把0号描述符指向管道读端
            dup(file_pipes[0]);
	    //关闭子进程中管道描述符
            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);
}


命名管道
 #include <sys/types.h>
 #include <sys/stat.h>

       int mkfifo(const char *pathname, mode_t mode);

例:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
    int res = mkfifo("/tmp/my_fifo", 0777);
    if (res == 0)
        printf("FIFO created\n");
    exit(EXIT_SUCCESS);
}
[yan@localhost ch13]$ ls -lF /tmp/my_fifo
prwxrwxr-x. 1 yan yan 0 7月  10 22:01 /tmp/my_fifo|
p和|都表示这是一个管道
权限与创建文件相同,都要与用户掩码的按位取反相与


打开FIFO的四种方式:
O_RDONLY 只读
O_WRONLY 只写
O_RDONLY|O_NONBLOCK 非阻塞读
O_WRONLY|O_NONBLOCK 非阻塞写

// Let's start with the header files, a #define and the check that the correct number
// of command-line arguments have been supplied.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#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 <some combination of\
               O_RDONLY O_WRONLY O_NONBLOCK>\n", *argv);
        exit(EXIT_FAILURE);
    }


    for(i = 1; i < argc; i++) {//设置open_mode
	    //++argv将argv下标+1
        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;
     }


    if (access(FIFO_NAME, F_OK) == -1) {//检查FIFO_NAME代表的文件是否存在
        res = mkfifo(FIFO_NAME, 0777);//不存在就创建
        if (res != 0) {
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }
//打印pid
    printf("Process %d opening FIFO\n", getpid());
    //以open_mode方式打开FIFO
    res = open(FIFO_NAME, open_mode);
    //打印FIFO的描述符
    printf("Process %d result %d\n", getpid(), res);
    sleep(5);
    //关闭指向FIFO的文件描述符
    if (res != -1) (void)close(res);
    printf("Process %d finished\n", getpid());
    exit(EXIT_SUCCESS);
}
测试:
./fifo2 O_RDONLY 阻塞直到有程序以写方式打开FIFO
./fifo2 O_WRONLY 阻塞直到有程序以读方式打开FIFO
./fifo O_RDONLY O_NONBLOCK 即使没有进程以写方式打开FIFO这个open调用也将成功并立刻返回
./fifo O_WRONLY O_NONBLOCK 如果没有进程以读方式打开FIFO这个open调用将返回一个错误-1并且FIFO也不会被打开


FIFO实现进程间通信:
生产者:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024 * 1024 * 10)//10M

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) {
        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) {//写入10M数据,并不关心内容
            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);
}

消费者:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF//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);
}

使用FIFO的客户/服务器:
//clent.h

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define SERVER_FIFO_NAME "/tmp/serv_fifo" //服务FIFO C-->S
#define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo" //回写到客户端的FIFO S-->C

#define BUFFER_SIZE 20

struct data_to_pass_st {//传递的数据结构
    pid_t  client_pid;//客户进程号
    char   some_data[BUFFER_SIZE - 1];//数据
};


//server.c

#include "client.h"//包含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 client_fifo[256];
    char *tmp_char_ptr;
//创建服务FIFO
    mkfifo(SERVER_FIFO_NAME, 0777);
    //以只读方式打开
    server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY);
    if (server_fifo_fd == -1) {
        fprintf(stderr, "Server fifo failure\n");
        exit(EXIT_FAILURE);
    }

    sleep(10); /* lets clients queue for demo purposes */

    do {//从服务端管道读数据结构
        read_res = read(server_fifo_fd, &my_data, sizeof(my_data));
        if (read_res > 0) {

// In this next stage, we perform some processing on the data just read from the client.
// We convert all the characters in some_data to uppercase and combine the CLIENT_FIFO_NAME
// with the received client_pid.
//读出数据并转成大写
            tmp_char_ptr = my_data.some_data;
            while (*tmp_char_ptr) {
                *tmp_char_ptr = toupper(*tmp_char_ptr);
                tmp_char_ptr++;
            }
	    //把client_pid填到CLIENT_FIFO_NAME中(补全),并赋值给client_fifo
            sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);

// Then we send the processed data back, opening the client pipe in write-only, blocking mode.
// Finally, we shut down the server FIFO by closing the file and then unlinking the FIFO.
//打开回写到客户端的FIFO
            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);
    //删除FIFO文件
    unlink(SERVER_FIFO_NAME);
    exit(EXIT_SUCCESS);
}
//clent.c

// Here's the client, client.c. The first part of this program opens the server FIFO,
// if it already exists, as a file. It then gets its own process ID, which forms some
// of the data that will be sent to the server. The client FIFO is created, ready for
// the next section.

#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];
//打开 C-->S FIFO
    server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY);
    if (server_fifo_fd == -1) {
        fprintf(stderr, "Sorry, no server\n");
        exit(EXIT_FAILURE);
    }
//写入进程号
    my_data.client_pid = getpid();
    //补全CLIENT_FIFO_NAME并赋值给client_fifo
    sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);
    if (mkfifo(client_fifo, 0777) == -1) {//创建S-->C FIFO
        fprintf(stderr, "Sorry, can't make %s\n", client_fifo);
        exit(EXIT_FAILURE);
    }


    for (times_to_send = 0; times_to_send < 5; times_to_send++) {//发送五条数据
        sprintf(my_data.some_data, "Hello from %d", my_data.client_pid); 
        printf("%d sent %s, ", my_data.client_pid, my_data.some_data);
	//写入 C-->S FIFO
        write(server_fifo_fd, &my_data, sizeof(my_data));
	//打开 S-->C FIFO 等待读取回写的数据
        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);
    //删除S-->C FIFO
    unlink(client_fifo);
    exit(EXIT_SUCCESS);
}
//

猜你喜欢

转载自blog.csdn.net/yanshaoshuai/article/details/81084967