Linux Programing -- ch13-- 进程间通信:管道

个人学习总结: 欢迎访问 http://aquariushome.duapp.com

1. popen & pclose

#include <stdio.h>

FILE *popen(const char *command, const char *open_mode);
int pclose(FILE *stream_to_close);

popen 函数允许一个程序将另一个程序作为新到程序启动,并可以传递数据给它或者通过它接收数据。command 字符差要运行到程序到命令和相应到参数。open_mode 必须所 “r” 或者 “w”。

调用 plclose 来关闭管道,pclose 调用只在 popen 启动到进程结束后才会返回,返回进程退出码。如果在调用 pclose 之前执行来一个 wait 语句,被调用进程到退出码就会丢失,因为调用进程已经结束,此时 pclose 返回 -1 并设置 errno 为 ECHILD。

例程:

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

int main(int argc, char *argv[])
{
    FILE *read_fp;
    char buffer[BUFSIZ + 1];
    int chars_read;
    memset(buffer, '\0', sizeof(buffer));
    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); 
}

2. pipe 调用

#include <unistd.h>

int pipe(int file_description[2]);

函数填入两个文件描述符,file_description[1] 用于写入 file_description[0] 用于读出。函数失败返回 -1,错误类型:

  1. EMFILE:进程使用的文件描述符过多。
  2. ENFILE:系统的文件表已满。
  3. EMFILE:文件描述符无效。

例程:

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

int main(int argc, char *argv[])
{
    pid_t new_pid;
    int file_pipes[2]; 

    if(pipe(file_pipes) != 0){
        perror("open pipe failed");
        exit(EXIT_FAILURE);
    }

    new_pid = fork();
    switch(new_pid){
    case -1:
        {
            perror("fork failed");
            exit(EXIT_FAILURE);
        }
    case 0:
        {
            close(0);
            dup(file_pipes[0]);
            close(file_pipes[0]);
            close(file_pipes[1]);
            execlp("od", "od", "-c", (char *)0);
        }

        break;
    default:
        {
            close(file_pipes[0]);

            char buf[1024];
            int count;
            memset(buf, 0, sizeof(buf));
            while((strncmp(buf, "end", 3) != 0)){
                fgets(buf, 1024, stdin);
                count = write(file_pipes[1], buf, strlen(buf));

                printf("%d - wrote %d byts\n", (int)getpid(), count); 
            }

            close(file_pipes[1]);
        }
        break;
    }

    exit(EXIT_SUCCESS);
}

3. 命名管道

可以使用命令创建命名管道:

$mkfifo /tmp/my_fifo
$cat /tmp/my_fifo &
$echo abc > /tmp/my_fifo
abc

创建命名管道后所有操作可以就想对待文件一样操作,API:

#include <sys/types.h>
#include <sys/stat.h>

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

当使用 open(const char *filename, int nflags) 打开命名管道时, nflags 只能去取 O_RDONLY或者O_WRONLY,这个函数会阻塞除非有另一个进程以相反的方式打开,如果要取消阻塞则可以与上 O_NONBLOCK

命名管道一次写入数据是有限制的,它由#define PIPE_BUF定义,一般在 limits.h 头文件中,大小一般是 4096 字节。

例程:服务器创建一个管道,循环接收客户端的数据,接收到数据后将小写转为大写,打开客户端创建的管道传给客户端。客户端接收数据,关闭管道。

client.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"
#define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"

struct data_to_pass_st{
    pid_t client_pid;
    char some_data[PIPE_BUF -1];
};

server.c

#include "client.h"

int main(int argc, char *argv[])
{
    int res;
    int server_fifo_fd, client_fifo_fd;
    struct data_to_pass_st my_data;

    mkfifo(SERVER_FIFO_NAME, 0777);

    do{
        server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY); 
        if(server_fifo_fd == -1){
            fprintf(stderr, "open server fifo failed");
            exit(EXIT_FAILURE);
        }
        res = read(server_fifo_fd, &my_data, sizeof(my_data));
        if(res > 0){
            char *tmp = my_data.some_data;
            while(*tmp){
                *tmp = toupper(*tmp);
                tmp++;
            }

            char client_fifo[256];
            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);
            }
        }

        close(server_fifo_fd);
    }while(1);

    unlink(SERVER_FIFO_NAME);
    exit(EXIT_SUCCESS);
}

client.c

#include "client.h"

int main(int argc, char *argv[])
{
    int res, i;
    int server_fifo_fd, client_fifo_fd;
    struct data_to_pass_st my_data;
    char client_fifo[256];

    my_data.client_pid = getpid();
    sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);
    mkfifo(client_fifo, 0777);

    server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY);
    if(server_fifo_fd == -1){
        fprintf(stderr, "open server fifo failed");
        exit(EXIT_FAILURE);
    }

    sprintf(my_data.some_data, "hello");
    write(server_fifo_fd, &my_data, sizeof(my_data));

    client_fifo_fd = open(client_fifo, O_RDONLY);
    if(client_fifo_fd != -1){
        res = read(client_fifo_fd, &my_data, sizeof(my_data));
        if(res > 0){
            printf("%d -- received: %s\n", getpid(), my_data.some_data);
        }
        close(client_fifo_fd);
    }

    close(server_fifo_fd);
    unlink(client_fifo);
    exit(EXIT_SUCCESS);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值