Day05 linux高级系统设计 - 管道

复制文件描述符

dup函数

作用:

文件描述符复制

语法:

        #include <unistd.h>

        int dup (int oldfd);

参数:

        所需复制得文件描述符

返回值:

        复制到的文件描述符

功能:

        从文件描述符表中,找一个最小可能的文件描述符(通过返回值返回)作为oldfd复制

示例1:

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    int newpd = dup(1);
    write(newpd,"hello world\n",12);
    return 0;
}

dup2函数(推荐)

函数:

#include <unistd.h>

int dup2(int oldfd,int newfd);

参数:

oldfd:原文件描述符

newfd:指定复制到的文件描述符,如果该文件描述符已存在,那将已存在的关闭

示例:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
    int fd = open("huange.txt",O_WRONLY | O_CREAT | O_APPEND,0666);
    int pd = dup2(fd,1);
    printf("娶你明年\n");
    return 0;
}

无名管道

概述

又名管道 (pipe)
无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符 ,1 个描述符写 fd[1], 1 个描述符读 fd[0]
核心:0读1写
特点
1.管道不是普通的文件,不属于某个文件管理系统,存在于内存中
2.半双工:数据在同一时间只能在单向传输
补充:
单工:指数据只能在单向传输数据
双工:指在两台通讯设备之间,允许有双向的数据传输
全双工:允许两台通讯设备之间同时进行双向数据传输。一般的电话,手机就是全双工系统,因为在讲话的同时也能听到对方的声音。
半双工:允许两台通讯设备之间进行双向数据传输,但不能同时进行。因此同一时间只允许一个设备进行传输数据,若另一设备需要传输数据,则需要等待上一个传输数据的设备传输结束后,才可以传输数据。
3.数据只允许从管道的一端写入,从管道的另一端读取
4.写入管道中的数据遵循先入先出的原则        
5.管道所传输的数据是无格式的,这要求管道的读出方和写入放提前商量好一个文件的格式,如多少字节算一则消息
6.管道在内存中对应一个缓冲区。不通的系统所对应的大小不一样。
7.数据从管道中读取是一次性操作,管道中数据一旦被读取,管道就会抛弃该数据,以便于释放空间来写入其它数据
8.管道没有名字,只能在具有公共祖先的进程中使用
        补充: 管道可以用于任意两个或更多相关进程之间的通信,只要在创建子进程 的系列调用之前通过一个共同的祖先进程创建管道即可。
        如管道可用于一个进程和其子孙进程之间的通信。第一个进程创建管道,然后创建子进程,接着子进程再创建第一个进程的孙子进程。
        管道通常用于两个兄弟进程之间的通信—— 它们的父进程创建了管道,并创建两个子进程。

pipe函数

作用:用于创建无名管道

语法:

#include <unistd.h>

int fd[] ;

pipe(fd);

参数:

fd为int型数据的首元素地址,其存放了管道的文件描述符反对fd[0],fd[1]

f[0]为读而打开管道,f[1]为写而打开

返回值:

        成功:0

        失败:-1

int fd[2];

pipe(fd);

示例:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
    int fd[2];
    pipe(fd);
    int i = 0;
    for(i = 0;i < 2;i++)
    {
        int pid = fork();
        if(pid == 0)
        {
            break;
        }
    }
    if(i == 0)
    {
        close (fd[0]);
        char buf[] = "hello to2";
        write(fd[1],buf,sizeof(buf));
        close(fd[1]);
        _exit(0);
    }
    else if(i == 1)
    {
        close (fd[1]);
        char buf[60] = {0};
        read(fd[0],buf,sizeof(buf));
        close(fd[0]);
        printf("%s\n",buf);
        _exit(0);
    }
    else if(i == 2)
    {
        while(1)
        {
            int pid = waitpid(-1,NULL,WNOHANG);
            if(pid == 0)
            {
                break;
            }
        }
    }
    return 0;
}

读写特点

1.默认用read函数从管道中读取数据时是阻塞的

2.调用write函数写入时,当管道的缓冲区已满时,也会发生阻塞,管道的缓冲区大小时65kb

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

4.从管道中读取数据的特点  编程时可通过fcntl函数设置文件的阻塞特性。设置为阻塞:fcntl(fd,FSETFL,O_NONBLOCK);

示例一:缓冲区已满时 write 也会阻塞。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    int fd[2] ;
    pipe(fd);
    int pid = fork();
    if(pid == 0)
    {
        close(fd[1]);
        sleep(2);
        close(fd[0]);
        _exit(0);
    }
    else if(pid > 0)
    {
        close(fd[0]);//关闭读
        int count = 0;
        for(int i = 0;i < 10000;i++)
        {
            int buf[1024] = {0};
            printf("buf=%ld\n",sizeof(buf)/sizeof(buf[0]));
            write(fd[1],buf,1024);
            count += 1024;
            printf("i=%d\ncount%d\n",i,count);
        }  
        close(fd[1]);
        wait(NULL);
    }
    return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
    //证明:调用 write 函数向管道里写数据,当缓冲区已满时 write 也会阻塞
    int fd[2];
    pipe(fd);

    for (int i = 0; i < 1000; i++)
    {
        char buf[1024] = {0};
        write(fd[1],buf,1024);
        printf("i = %d\n",i);
    }

    printf("OVER\n");
    
    return 0;
}

示例二:通信过程中,写端关闭,读端将解阻塞

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    int fd[2];
    pipe(fd);
    int pid = fork();
    if(pid == 0)
    {
        close(fd[1]);
        int buf[100] = {0};
        printf("开始读取\n");
        read(fd[0],buf,100);
        close(fd[0]);
        printf("读取结束\n");
        _exit(0);
    }
    else if(pid > 0)
    {
        close(fd[0]);
        sleep(2);
        close(fd[1]);
        sleep(5);
        _exit(0);
    }
    return 0;
}

示例三:通信过程中 读端关闭 写端将收到SIGPIPE信号 退出写端进程

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
    int fd[2];
    pipe(fd);
    int pid = fork();
    if (pid == 0)
    {
        close(fd[1]);

        while (1)
        {
            int i = 0;
            char buf[128] = "";
            int len = read(fd[0], buf, sizeof(buf) / sizeof(buf[0]));
            i++;
            printf("len=%d\n", len);
            if (i == 5)
            {
                break;
            }
        }
    }
    else if (pid > 0)
    {
        close(fd[0]);
        while(1)
        {
            printf("给父进程%u写数据\n",getpid());
            write(fd[1],"hello pipe",10);
            sleep(1);
        }
        close(fd[1]);
        wait(NULL);
    }
    return 0;
}

综合案例

要求 : 使用代码实现 ps -A | grep bash
示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
    int pd[2];
    pipe(pd);
    int pid = fork();
    if(pid < 0)
    {
        printf("输入有误\n");
        return 0;
    }
    else if(pid  == 0)
    {
        close(pd[1]);
        dup2(pd[0],0);
        execl("/bin/grep","/bin/grep","bash",NULL);
        _exit(-1);
    }
    else if(pid > 0)
    {
        close(pd[0]);
        dup2(pd[1],1);
        execl("/bin/ps","ps","-A",NULL);
        wait(NULL);
    }
    return 0;
}

有名管道

概述:

又名命名管道(FIFO)

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

概述

mkfifo函数

作用:创建有名管道

语法:

        #include <sys/types.h>

        #include <sys/stat.h>

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

参数:

        pathname:文件名

mode:文件操作模式,一般使用0666(所有用户可读可写)

返回值

        成功:0

        失败:-1 一般失败是因为有与pathname名相同的文件

读写特点

1 open 打开管道 不指定 O_NONBLOCK ( 阻塞 )
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 也会阻塞。
2 open 打开管道 指定 O_NONBLOCK ( 非阻塞 )
1 、先以只读方式打开:如果没有进程 , 已经为写而打开一个 FIFO, 只读 open 成功,并且 open 不阻塞。
2 、先以只写方 式打开:如果没有进程 , 已经为读而打开一个 FIFO ,只写 open 将出错返回-1
3 read write 读写命名管道中读数据时不阻塞。
4 、通信过程中,读进程退出后, 写进程向命名管道内写数据时,写进程也会(收到SIGPIPE 信号)退出。
3 、 注意: open 函数以可读可写方式打开 FIFO 文件时的特点:
1 open 不阻塞。
2 、调用 read 函数从 FIFO 里读数据时 read 会阻塞。
3 、调用 write 函数向 FIFO 里写数据 , 当缓冲区已满时 write 也会阻塞

综合案例

09_demoA.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    int i = 0;
    for(i = 0;i < 2;i++)
    {
        int pid = fork();
        if(pid == 0)
        {
            break;
        }
    }
    if(i == 0)
    {
        mkfifo("./zjrtozl",0666);//张晋若发送消息
        int fd = open("./zjrtozl",O_WRONLY);
        while(1)
        {
            char buf[100] = {0};
            fgets(buf,100,stdin);                                        

            printf("张晋若:%s\n",buf);
            write(fd,buf,strlen(buf));
            buf[strlen(buf)-1] = 0;
            if(strcmp("886",buf) == 0)
            {
                break;
            }
        }
        close(fd);
        _exit(0);
    }
    else if(i == 1)
    {
        mkfifo("./zltozjr",0666);//张立接收消息
        int fd = open("./zltozjr",O_RDONLY);
        while(1)
        {
            char buf[100] = {0};
            read(fd,buf,sizeof(buf));
            printf("张立:%s\n",buf);
            buf[strlen(buf)-1] = 0;
            if(strcmp("886",buf) == 0)
            {
                break;
            }
        }
        close(fd);
        _exit(0);
    }
    else if(i == 2)
    {
        while(1)
        {
            int pid = waitpid(-1,NULL,WNOHANG);
            if(pid == -1)
            {
                break;//子进程已经回收完毕
            }
        }
    }
    return 0;
}

09_demoB.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    int i = 0;
    for(i=0;i < 2;i++)
    {
        int pid = fork();
        if(pid == 0)
        {
            break;
        }
    }
    if(i == 0)
    {
        mkfifo("./zltozjr",0666);//张立发消息
        int fd = open("./zltozjr",O_WRONLY);
        while(1)
        {
            char buf[120] = {0};
            fgets(buf,120,stdin);
            printf("张立:%s\n",buf);
            write(fd,buf,strlen(buf));
            buf[strlen(buf)-1] = 0;
            if(strcmp("886",buf) ==0)
            {
                break;
            }
        }
        close(fd);
        _exit(0);
    }
    else if(i == 1)
    {
        mkfifo("./zjrtozl",0666);//张晋若接收消息
        int fd =open("./zjrtozl",O_RDONLY);
        while(1)
        {
            char buf[] = {0};
            read(fd,buf,sizeof(buf));
            printf("张晋若:%s\n",buf);
            buf[strlen(buf)-1] = 0;
            if(strcmp("886",buf)==0)
            {
                break;
            }
        }
        close(fd);
        _exit(0);
    }
    else if(i == 2)
    {
        while(1)
        {
            int pid = waitpid(-1,NULL,WNOHANG);
            if(pid == -1)
            {
                break;//子进程回收完毕
            }
        }
    }
    return 0;
}

总结

无名管道与有名管道的使用场景

1.无名管道应用于有血缘关系的进程中

2.有名管道应用于没有血缘关系的进程中

有名管道与无名管道的区别

1.无名管道是基于内存的,无需文件管理系统

2.有名管道是基于文件和内存的,需要文件管理系统

dup

作用;复制文件描述符

意义:可以通过dup函数实现文件的重定向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/231105.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SSH原理与应用与探索

Secure Shell(SSH 安全外壳协议) 是由 IETF(The Internet Engineering Task Force) 制定的建立在应用层基础上的安全网络协议。它是专为远程登录会话(甚至可以用Windows远程登录Linux服务器进行文件互传)和其他网络服务提供安全性的协议&#xff0c;可有效弥补网络中的漏洞。通…

Java预科知识

以下内容是根据狂神的Java说、chatgpt和csdn相关博客&#xff0c;结合自己的理解完成的。 Java了解 基于Java 开发了巨多的平台&#xff0c;系统&#xff0c;工具 构建工具&#xff1a; Ant, Maven, Jekins应用服务器&#xff1a;Tomcat, Jetty, Jboss, Websphere, weblogic…

vue的data

类型&#xff1a;Object | Function 限制&#xff1a;组件的定义只接受 function。 详细&#xff1a; Vue 实例的数据对象。Vue 会递归地把 data 的 property 转换为 getter/setter&#xff0c;从而让 data 的 property 能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个…

vertica主键列能插入重复值的处理办法

问题描述 开发同事反馈在vertica中创建含主键列的表中插入重复数据时没有进行校验&#xff0c;插入重复值成功。经过测试着实可以插入重复值&#xff0c;这个坑有些不一样。 创建表和插入语句如下&#xff1a; --创建表 CREATE TABLE dhhtest(ID VARCHAR(64) PRIMARY KEY );…

快速认识什么是:Docker

Docker&#xff0c;一种可以将软件打包到容器中并在任何环境中可靠运行的工具。但什么是容器以及为什么需要容器呢&#xff1f;今天就来一起学快速入门一下Docker吧&#xff01;希望本文对您有所帮助。 假设您使用 Cobol 构建了一个在某种奇怪风格的 Linux 上运行的应用程序。您…

linux 定时任务

使用 crontab Usage: crontab [-u user] [-e|-l|-r] Crontab 的格式说明如下: * 逗号(‘,’) 指定列表值。如: “1,3,4,7,8″ * 中横线(‘-’) 指定范围值 如 “1-6″, 代表 “1,2,3,4,5,6″ * 星号 (‘*’) 代表所有可能的值 */15 表示每 15 分钟执行一次 # Use the ha…

Python中type、object和class的关系,一图即懂

没有文字描述&#xff0c;因为图足够好&#xff01; 查看一个类的基类可以使用__bases__方法查看一个实例对应的类是谁&#xff0c;可以用__class__方法 class Animal:pass class User:pass class Dog(Animal, User):passif __name__ "__main__":print(Dog.__bases…

linux初级学习

(420条消息) 红帽认证-RHCSA_rhcsa红帽认证_yyyzf的博客-CSDN博客 OS&#xff1a;用户和机器的接口&#xff0c;UI:CMD,GUI shell: 通用格式 命令 选项&#xff08;调控功能&#xff09; 参数&#xff08;操作对象&#xff09;参数 省略参数对象一般使用当前目录作为参数对…

Linux下C++静态链接库的生成以及使用

目录 一.前言二.生成静态链接库三.使用静态链接库 一.前言 这篇文章简单讨论一下Linux下如何使用gcc/g生成和使用C静态链接库&#xff08;.a文件&#xff09;。 二.生成静态链接库 先看下目录结构 然后看下代码 //demo.h#ifndef DEMO_H #define DEMO_H#include<string&g…

C#-快速剖析文件和流,并使用

目录 一、概述 二、文件系统 1、检查驱动器信息 2、Path 3、文件和文件夹 三、流 1、FileStream 2、StreamWriter与StreamReader 3、BinaryWriter与BinaryReader 一、概述 文件&#xff0c;具有永久存储及特定顺序的字节组成的一个有序、具有名称的集合&#xff1b; …

题目:挑选子串(蓝桥OJ 1621)

题目描述&#xff1a; 解题思路&#xff1a; 采用双指针的快慢指针。与蓝桥OJ1372类似。 图解 题解&#xff1a; #include <bits/stdc.h> using namespace std;const int N 1e5 9; int a[N];int main() {ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int n, m…

网站内容审核功能的重要性

网站内容审核功能的重要性在保护用户权益、维护网站形象、遵守法律法规等方面都起到了至关重要的作用。 维护网站的合法性和道德性&#xff1a;网站内容审核功能的存在可以帮助过滤和删除违法、淫秽、恶意、诈骗等不良内容&#xff0c;保证网站内容的合法性和道德性。 保护用…

实现跨VLAN通信、以及RIP路由协议的配置

一、如下图片&#xff1a; 1. 按照拓扑图所示&#xff0c;将8台计算机分别配置到相应的VLAN中。&#xff08;20分&#xff09; 2. 配置实现同一VLAN中的计算机可以通信。&#xff08;22分&#xff09; 3. 配置实现PC1,PC2,PC3,PC4可以互相通信&#xff0c;PC5,PC6,PC7,PC8可以互…

面向对象中的单例模式

1、什么是设计模式 设计模式就是前人根据实际的问题提出的问题解决方案&#xff0c;我们把这种就称之为设计模式。 2、单例模式 单例模式是一种常见的设计模式&#xff01; 所谓的设计模式&#xff0c;不是一种新的语法&#xff0c;而是人们在实际的应用中&#xff0c;面对…

计算机考研408-计算机网络、操作系统整书知识点脑图

计算机网络、操作系统整书知识点脑图 今天突然想起来考研期间为了方便记忆&#xff0c;费了很大力气整理了计算机网络、操作系统两本书知识点的脑图&#xff0c;想着放着也没啥用&#xff0c;分享出来给大家看看 但是思维导图格式的东西好像没法直接发成文章&#xff0c;要是…

(三潮来袭)探寻2023年科技变革潮流与2024年前瞻展望

2023年对于IT行业来说是一个动荡而又充满变革的一年。随着世界逐渐走出前几年的挑战&#xff0c;企业逐渐复苏&#xff0c;但这个行业仍然在经历着激烈的变革。在这个时候&#xff0c;我们看到了一些引人注目的技术变化和未来的趋势。 一、2023年回顾 关键词&#xff1a;Chat…

手持式安卓主板_PDA安卓板_智能手持终端方案

手持式安卓主板方案是一种智能终端设备&#xff0c;具备自动对焦和闪光灯功能&#xff0c;可以在昏暗的环境下快速扫描二维码并轻松采集数据。该方案还提供多渠道支付和数据采集功能&#xff0c;为用户提供了便捷的体验。 该方案的产品基于手持式安卓主板&#xff0c;并搭载了八…

Unity中后处理简介

文章目录 前言一、后处理的原理二、我们看一下Unity文档中&#xff0c;内置的后处理前后的效果后处理前&#xff1a;后处理后&#xff1a; 前言 我们在这篇文章中&#xff0c;了解一下Unity中的后处理效果 后期处理概述 一、后处理的原理 在后处理的过程中&#xff0c;我们主…

爱智EdgerOS之深入解析安全可靠的开放协议SDDC

一、协议简介 在 EdgerOS 的智慧生态场景中&#xff0c;许多智能设备或传感器的生命周期都与 SDDC 协议息息相关&#xff0c;这些设备可能是使用 libsddc 智能配网技术开发的&#xff0c;也有可能是因为主要功能上是使用其他技术如 MQTT、LoRa 等但是设备的上下线依然是使用上…

金蝶云星空表单服务规则设置-基础资料和复选框判断

文章目录 金蝶云星空表单服务规则设置-基础资料和复选框判断语法规则业务需求开发实现测试 金蝶云星空表单服务规则设置-基础资料和复选框判断 语法规则 基础资料判断&#xff1a;标识null 复选框判断打钩&#xff1a;标识true 业务需求 售后单审核时&#xff0c;如果物料启…
最新文章