【linux】dup文件描述符复制函数和管道详解

目录

一、文件描述符复制

1、dup函数(复制文件描述符)

​编辑 2、dup2函数(复制文件描述符)

​编辑 二、无名管道pipe

1、概述 

2、无名管道的创建

 3、无名管道读写的特点

 4、无名管道ps -A | grep bash实现

 三、有名管道FIFO(命名管道)

1、概述 

2、创建有名管道 mkfifo

3、有名管道读写的特点


一、文件描述符复制

        让新的文件描述符 指向 旧的文件描述符。(新旧文件描述符指向同一个文件) 使用的函数dup、dup2。

1、dup函数(复制文件描述符)

#include<unistd.h>

int dup(int oldfd);

        dup函数的功能:从系统中寻找最小可用的文件描述符 作为oldfd的副本。 新文件描述符 通过dup的返回值返回。

 2、dup2函数(复制文件描述符)

        #include<unistd.h>

        int dup2(int oldfd, int newfd);  

dup2的功能:将newfd作为oldfd的副本。 如果newfd事先存在 dup2会先close(newfd),然后将newfd作为oldfd的副本。 

 二、无名管道pipe

1、概述 

         管道(pipe)又称无名管道。 无名管道是一种特殊类型的文件,在应用层体现为 两个打开的文件描述符(读端和写端)。

管道的特点:

  1. 半双工,数据在同一时刻只能在一个方向上流动。
  2. 数据只能从管道的一端写入,从另一端读出
  3. 写入管道中的数据遵循先入先出的规则。
  4. 管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据 的格式, 如多少字节算一个消息等。
  5. 管道不是普通的文件,不属于某个文件系统,其只存在于内存
  6. 管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
  7. 从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。
  8. 管道没有名字,只能在具有公共祖先的进程之间使用 

2、无名管道的创建

        #include<unistd.h>

        int pipe(int filedes[2]);

功能:经由参数filedes返回两个文件描述符

参数: filedes为int型数组的首地址,其存放了管道的文件描述符fd[0]、fd[1]。 filedes[0]为读而打开,filedes[1]为写而打开管道,filedes[0]的输出是filedes[1]的输 入。

返回值: 成功:返回 0 失败:返回-1

注意:在使用无名管道的时候 必须事先确定,谁发,谁收的问题。一旦确定不可更改。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
    //创建一个无名管道
    int fd[2];
    pipe(fd);
    //创建一个子进程
    //父进程发 子进程收
    pid_t pid=fork();
    if(pid==0)//子进程
    {
        //子进程的写端无意义(可以事先关闭)
        close(fd[1]);
        //子进程接收父进程消息
        unsigned char buf[128]="";
        printf("子进程%d正在等待父进程的消息\n", getpid());
        read(fd[0],buf,sizeof(buf));
        printf("子进程%d读到的消息为:%s\n", getpid(), buf);
        //子进程读完数据 应该关闭读端
        close(fd[0]);
        //显示退出
        _exit(-1);
    }
    if(pid>0) //父进程
    {
        //父进程的读端无意义(可以事先关闭)
        close(fd[0]);
        //写端写入数据
        printf("3秒后父进程%d写入数据hello pipe\n",getpid());
        sleep(3);
        write(fd[1],"hello pipe",strlen("hello pipe"));
        printf("父进程:%d完成写入\n", getpid());
        //通信完成 应该关闭写端
        close(fd[0]);
        //等待子进程退出
        wait(NULL);
    }
    return 0;
}

 3、无名管道读写的特点

1、默认用read函数从管道中读数据是阻塞的。

2、调用write函数向管道里写数据,当缓冲区已满时write也会阻塞。

3、通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收 到SIGPIPE信号)退出。 

 4、无名管道ps -A | grep bash实现

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
    //创建一个无名管道
    int fd[2];
    pipe(fd);
    //创建两个子进程
    int i=0; 
    for(i=0;i<2;i++)
    {
        pid_t pid=fork();
        if(pid==0)
            break;//防止子进程继续创建子进程
    }
    if(i==0)//子进程1
    {
        //ps ‐A 写端
        close(fd[0]);
        //1作为fd[1]的副本
        dup2(fd[1],1);
        //执行ps ‐A
        execlp("ps","ps","-A",NULL);
        _exit(-1);
    }
    else if(i==1)//子进程2
    {
        //grep bash 读端
        close(fd[1]);
        //0作为fd[0]的副本
        dup2(fd[0],0);
        //执行grep bash
        execlp("grep","grep","bash",NULL);
    }
    if(i==2) //父进程
    {
        //关闭管道读写端
        close(fd[0]);
        close(fd[1]);
        while(1)
        {
            pid_t pid=waitpid(-1,NULL,WNOHANG);
            if(pid>0)
                printf("子进程%d退出了\n", pid);
            else if(pid==0)
                continue;
            else if(pid<0)
                break;
        }
    }
    return 0;
}

 三、有名管道FIFO(命名管道)

1、概述 

         主要用于没有血缘关系的进程间通信。

 

 特点:

  1. 半双工,数据在同一时刻只能在一个方向上流动。
  2. 写入FIFO中的数据遵循先入先出的规则。
  3. FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先 约定好数据的格式,如多少字节算一个消息等。
  4. FIFO在文件系统中作为一个特殊的文件而存在,但FIFO中的内容存放在内存中。
  5. 管道在内存中对应一个缓冲区。不同的系统其大小不一定相同
  6. 从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释 放空间以便写更多的数据。
  7. 当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。
  8. FIFO有名字不相关的进程可以通过打开命名管道进 行通信(重要)

2、创建有名管道 mkfifo

FIFO文件的创建

        #include<sys/type.h>

        #include<sys/stat.h>

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

参数:

pathname:FIFO的路径名+文件名。

mode:mode_t类型的权限描述符。

返回值: 成功:返回 0 失败:如果文件已经存在,则会出错且返回-1

 fifo_write.c:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
    //创建有名管道(确保两个进程识别相同目录)
    mkfifo("my_fifo",0666);
    //open以写的方式的打开 有名管道(阻塞 到 对方 以读的方式打开)
    int fp=open("my_fifo",O_WRONLY);
    if(fp<0)
    {
        perror("open");
        return 0;
    }
    printf("写端open成功\n");
    while(1)
    {
        //获取键盘输入
        unsigned char buf[128]="";
        printf("请输入需要发送的数据:");
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]=0;
        //发送数据
        write(fp,buf,sizeof(buf));
        //退出循环
        if(strcmp(buf,"Bye")==0)
            break;
    }
    close(fp);
}

 fifo.read.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
    //创建有名管道(确保两个进程识别相同目录)
    mkfifo("my_fifo",0666);
    //open以读的方式的打开 有名管道(阻塞 到 对方 以写的方式打开)
    int fp=open("my_fifo",O_RDONLY);
    if(fp<0)
    {
        perror("open");
        return 0;
    }
    printf("读端open成功\n");
    //循环的读取数据
    while(1)
    {
        //接收数据
        unsigned char buf[128]="";
        read(fp,buf,sizeof(buf));
        printf("收到数据:%s\n", buf);
        //退出循环
        if(strcmp(buf,"Bye")==0)
            break;
    }
    close(fp);
}

 

3、有名管道读写的特点

阻塞方式打开管道:

1、open以只方式打开FIFO时,要阻塞到某个进程为而打开此FIFO

2、open以只方式打开FIFO时,要阻塞到某个进程为而打开此FIFO。

3、open以只读、只写方式打开FIFO时会阻塞,调用read函数从FIFO里读数据时read也会阻塞

4、通信过程中若写进程先退出了,则调用read函数从FIFO里读数据时不阻塞;若写进程又 重新运行,则调用read函数从FIFO里读数据时又恢复阻塞

5、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE信号)退出

6、调用write函数向FIFO里写数据,当缓冲区已满时write也会阻塞

 非阻塞方式打开管道:

1、先以只读方式打开:如果没有进程已经为写而打开一个FIFO, 只读open成功,并且 open不阻塞

2、先以只写方式打开:如果没有进程已经为读而打开一个FIFO,只写open 将出错返回-1

3、read、write读写命名管道中读数据时不阻塞

4、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE信号)退出

注意: open函数以可读可写方式打开FIFO文件时的特点:

        1、open不阻塞

        2、调用read函数从FIFO里读数据时read会阻塞

        3、调用write函数向FIFO里写数据,当缓冲区已满时write也会阻塞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值