linux系统编程之管道(一)

今天继续研究linux系统编程,继上次对信号进行研究以后,此次开始一个新的章节-----管道,在正式开始以前,先把今天让本身很激动的事说一下,小小的兴奋,通过下周后本身的创业产品用户量就有一个质的飞越了,百万级的,虽然说离最终的目标还有很远,可是,这说明本身团队最近几个月的辛苦付出是值得的,也让本身对此次的创业更加有期待了,小小骄傲一下,个人学习任务还得继续坚持,谁叫咱是搞技术的呢,言归正传,正入正题:html

什么是管道:

实际上,咱们在linux中常常会用到带"管道"的命令,如:
 
那管道的本质是什么呢?既然它是一个数据流,那就必定得要有一个缓冲区来保存数据, 因此说,咱们能够把管道当作是具备 固定大小的一个 内核缓冲区。
管道限制:
关于上面提到的第二点,为啥只能用于具备共同祖先的进程呢?须要先理解下面的函数才能明白,因此先学习下面的用法,回过头来再来理解这句话。
匿名管道pipe:
建立管道后示意图:
回到以前提出的问题来,为啥管道只能用于具备共同祖先的进程呢?缘由在于: 管道的文件描述符其它进程是没有办法获取,只能经过子进程继承父进程得来了。
经过管道的这些文件描述符,咱们就能够实现进程间的通讯了,好比:子进程往管道的写入端点中写入数据,父进程能够从管道的读端点获取数据,下面就以实际代码来讲明一下父子进程的数据传递:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    int pipefd[2];
    if (pipe(pipefd) == -1)//建立一个管道以后,就会获得两个文件描述符
        ERR_EXIT("pipe error");

    pid_t pid;
    pid = fork();//建立父子进程来演示数据通信的目的
    if (pid == -1)
        ERR_EXIT("fork error");

    if (pid == 0)
    {//子进程发送数据
        close(pipefd[0]);//关闭子进程管道的读端,由于没有用
        write(pipefd[1], "hello", 5);//向子进程管道的写端传入数据
        close(pipefd[1]);//传递完以后将其关掉
        exit(EXIT_SUCCESS);
    }

    //父进程读取数据
    close(pipefd[1]);//关闭父进程的写端,由于没有用
    char buf[10] = {0};
    read(pipefd[0], buf, 10);//从管道的读端读入数据
    close(pipefd[0]);//关闭管道的读端
    printf("buf=%s\n", buf);
    
    return 0;

}

编译运行:linux

这个经过管道达到进程间传递数据的例子比较简单,下面用程序来模拟下面的这个shell命令的效果:web

咱们能够用子进程来运行ls,父进程来运行wc -w命令,具体代码以下:shell

第一步:编程

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    int pipefd[2];
    if (pipe(pipefd) == -1)
        ERR_EXIT("pipe error");

    pid_t pid;
    pid = fork();
    if (pid == -1)
        ERR_EXIT("fork error");

    if (pid == 0)
    {//子进程运行ls命令,
        execlp("ls", "ls", NULL);
        fprintf(stderr, "error execute ls\n");
        exit(EXIT_FAILURE);
    }

    //父进程运行wc -w命令
    execlp("wc", "wc", "-w", NULL);
    fprintf(stderr, "error execute wc\n");//若是执行execlp运行失败了,才会执行到这
    exit(EXIT_FAILURE);

}

 

【说明】:关于execlp函数的使用,能够参考博文:http://www.cnblogs.com/webor2006/p/3507913.html函数

第二步,重定向文件描述符,这是实现的关键:学习

由于ls命令标准是输出到标准输出设备(屏幕)当中,wc命令是从标准输入设备获取数据,而如今,咱们但愿ls命令输出到管道的写端,而wc命令是从管道的读端获取数据,那该怎么办呢?文件描述符的复制既可达到这个目的,具体代码以下:spa

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    int pipefd[2];
    if (pipe(pipefd) == -1)
        ERR_EXIT("pipe error");

    pid_t pid;
    pid = fork();
    if (pid == -1)
        ERR_EXIT("fork error");

    if (pid == 0)
    {//子进程运行ls命令,
        dup2(pipefd[1], STDOUT_FILENO);//将标准输出复制到管道的写端,也就是说标准输出定位到了管道的写端 close(pipefd[1]);//这时管道的读写端都没用了,将其关闭 close(pipefd[0]);
        execlp("ls", "ls", NULL);//这时ls输出则为管道的写端了,因为文件描述符重定向了
        fprintf(stderr, "error execute ls\n");
        exit(EXIT_FAILURE);
    }

    //父进程运行wc -w命令
    dup2(pipefd[0], STDIN_FILENO);//将标准输入重定向管道的读端,因此wc命令这时就会从管道的读端来获取数据喽 close(pipefd[0]); close(pipefd[1]);
    execlp("wc", "wc", "-w", NULL);
    fprintf(stderr, "error execute wc\n");//若是执行execlp运行失败了,才会执行到这
    exit(EXIT_FAILURE);

}

编译运行:3d

下面再来看一个有关文件描述符复制的程序,先看效果,再来分析其原理:code

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


int main(int argc, char *argv[])
{
    close(0);
    open("Makefile", O_RDONLY);
    close(1);
    open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);

    execlp("cat", "cat", NULL);

    return 0;
    
}

编译运行:

为啥能实现文件的拷贝效果呢?我们来分析一下程序:

默认状况下:

而下面这句代码事后:

close(0);

open("Makefile", O_RDONLY);

close(1);

open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);

而最后一句关键代码以下:

execlp("cat", "cat", NULL);

咱们一般用cat能够查看一个文件内容:

可是,若是cat不带参数,那是什么意思呢?

如效果所示,不带参数的cat命令其实是从标准输入获取数据,写入到标准输出当中,因此也就是从Makefile文件获取数据,写入到Makefile2文件当中,若是Makefile2文件不存在则会主动建立一个,因此就实现了一个cp命令喽,是否是颇有技巧。

【说明】:关于文件描述符的复制,能够参考博文:http://www.cnblogs.com/webor2006/p/3498443.html

好了,今天的内容学到这,虽然说内容很少,可是得好好理解,下回见!!

相关文章
相关标签/搜索