Linux中的高级IO函数(一)pipe socketpair dup

news2025/1/16 19:55:14

Linux提供了很多高级的I/O函数。它们并不像Linux基础I/O函数(比如open和read)那么常用(编写内核模块时一般要实现这些I/O函数),但在特定的条件下却表现出优秀的性能。这些函数大致分为三类:

用于创建文件描述符的函数,包括pipe、socketpair、dup/dup2函数。
用于读写数据的函数,包括readv/writev、sendfile、mmap/munmap、splice和tee函数。
用于控制I/O行为和属性的函数,包括fcntl函数。

本节先介绍第一类

一、pipe函数

pipe 函数用于创建一个管道,以实现进程间通信。

#include <unistd.h>

int pipe(int fd[2]);

pipe函数创建的这两个文件描述符fd[0]和fd[1]分别构成管道的两端,往fd[1]写入的数据可以从fd[0]读出。并且,fd[0]只能用于从管道读出数据,fd[1]则只能用于往管道写入数据,而不能反过来使用。如果要实现双向的数据传输,就应该使用两个管道。

默认情况下,这一对文件描述符都是阻塞的,若用read来读取一个空的管道,则read将被阻塞,直到管道内有数据可读。如果应用程序将fd[0]和fd[1]都设置为非阻塞的,则read和write会有不同的行为。

  • 如果管道的写端文件描述符fd[1]的引用计数减少至0,即没有任何进程需要往管道中写入数据,则针对该管道的读端文件描述符fd[0]的read操作将返回0,即读取到了文件结束标记(End Of File,EOF);

  • 如果管道的读端文件描述符fd[0]的引用计数减少至0,即没有任何进程需要从管道读取数据,则针对该管道的写端文件描述符fd[1]的write操作将失败,并引发SIGPIPE信号。

管道容量的大小默认是65536字节。我们可以使用fcntl函数来修改管道容量。

下面举一个在多进程程序中管道通信的例子:

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

int main()
{
    int pipefd[2];
    char buf[1024];

    // 创建管道
    if (pipe(pipefd) == -1)
    {
        perror("pipe error! \n");
        return 1;
    }

    for (int i = 0; i < 8; i++)
    {
        // 创建子进程
        pid_t pid = fork();

        if (pid == -1)
        {
            perror("fork error!\n");
            return 1;
        }
        else if (pid == 0)
        {
            // 子进程关闭写端
            close(pipefd[1]);
            // 从管道读取数据
            memset(buf, 0, 1024);
            read(pipefd[0], buf, sizeof(buf));
            // 打印数据
            printf("pid = %d :Child process received: %s\n", getpid(), buf);
            // 子进程关闭读取端
            close(pipefd[0]);
            // 子进程直接break,以免创建更多的子进程
            break;
        }
    }

    if (getpid() != 0)
    {
        // 父进程关闭读取端
        close(pipefd[0]);
        // 向管道写入数据
        const char *message = "Hello from parent process";
        for (int i = 0; i < 8; i++)
        {
            write(pipefd[1], message, strlen(message));
            sleep(1);
        }
        // 在所有数据都写入后再关闭写入端
        close(pipefd[1]);
    }

    return 0;
}

生成了八个子进程,八个子进程阻塞在read处,等待父进程的消息,父进程发了八次消息,每次间隔1秒。看一下仿真

image-20240421223438140

二、socketpair函数

socketpair可以用于创建一堆相互连接的套接字,通常用于在进程之间通信

int socketpair(int domain, int type, int protocol, int sv[2]);

参数说明:

  • domain:指定地址族,通常为 AF_UNIX 表示 UNIX 域套接字。
  • type:指定套接字类型,通常为 SOCK_STREAM 表示面向连接的套接字。
  • protocol:指定使用的协议,通常为 0 表示默认协议。
  • sv[2]:用于存放创建的两个套接字的文件描述符的数组。

下面举个多进程的例子

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int socketfd[2];
    char buf[1024];

    // 创建一对相互连接的套接字
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, socketfd) == -1) {
        perror("socketpair");
        return -1;
    }


    for (int i = 0; i < 8; i++)
    {
        // 创建子进程
        pid_t pid = fork();

        if (pid == -1)
        {
            perror("fork error!\n");
            return 1;
        }
        else if (pid == 0)
        {
            // 子进程关闭写端
            close(socketfd[1]);
            // 从管道读取数据
            memset(buf, 0, 1024);
            read(socketfd[0], buf, sizeof(buf));
            // 打印数据
            printf("pid = %d :Child process received: %s\n", getpid(), buf);
            // 子进程关闭读取端
            close(socketfd[0]);
            // 子进程直接break,以免创建更多的子进程
            break;
        }
    }

    if (getpid() != 0)
    {
        // 父进程关闭读取端
        close(socketfd[0]);
        // 向管道写入数据
        const char *message = "Hello from parent process";
        for (int i = 0; i < 8; i++)
        {
            write(socketfd[1], message, strlen(message));
            sleep(1);
        }
        // 在所有数据都写入后再关闭写入端
        close(socketfd[1]);
    }

    return 0;
}

例子和上面的 pipe 是一样的,看仿真:

image-20240422101424911

三、dup函数和dup2函数

dup函数和dup2函数可以把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接

#include <unistd.h>

int dup(int file_descriptor);
int dup2(int oldfd, int newfd);

dup函数创建一个新的文件描述符,该新文件描述符和原有文件描述符file_descriptor指向相同的文件、管道或者网络连接。并且dup返回的文件描述符总是取系统当前可用的最小整数值。

dup2函数可以将 oldfd 重定向到 newfd。如果newfd已经被程序使用,则系统会先将newfd所指的文件关闭。

**注意:**通过dup和dup2创建的文件描述符并不继承原文件描述符的属性,比如close-on-execnon-blocking等。

3.1、使用 dup2 将标准输出重定向到一个文件

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

int main() {
    // 打开或创建文件,以便将标准输入内容写入到该文件中
    int file_fd = open("output.txt", O_RDWR | O_CREAT, 0666);
    if (file_fd == -1) {
        perror("open");
        return 1;
    }

    // 将标准输出重定向到文件描述符指向的文件
	dup2(file_fd, STDOUT_FILENO)
    printf("newfd: hello, world\n");

    const char* buf = "oldfd: hello, world\n";
    write(file_fd, buf, strlen(buf)); 
    close(file_fd);
    return 0;
}

将标准输出重定向到 output.txt 文件,所以printf打印函数打印的字符串直接输出到了文件中,而不是控制台上。

image-20240422104522033

3.2、使用 dup2 将标准输入重定向到一个文件

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

int main() {
    // 打开或创建文件,以便将标准输入内容写入到该文件中
    int file_fd = open("output.txt", O_RDWR | O_CREAT, 0666);
    if (file_fd == -1) {
        perror("open");
        return 1;
    }

	dup2(file_fd, STDIN_FILENO)
    char buff[1024] = {0};
    read(STDIN_FILENO,buff,1024);
    printf("%s",buff);

    close(file_fd);
    return 0;
}

相当于直接从文件中读取数据作为标准输入了

image-20240422104857848

3.2、使用 dup 将标准输出重定向到一个文件

int main() {
    // 打开或创建文件,以便将标准输入内容写入到该文件中
    int file_fd = open("output.txt", O_RDWR | O_CREAT, 0666);
    if (file_fd == -1) {
        perror("open");
        return 1;
    }

    close(STDOUT_FILENO);
    dup(file_fd);

    printf("123");

    close(file_fd);
    return 0;
}

image-20240422110314962

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS开发案例:【闹钟】

介绍 使用后台代理提醒&#xff0c;实现一个简易闹钟。要求完成以下功能&#xff1a; 展示指针表盘或数字时间。添加、修改和删除闹钟。展示闹钟列表&#xff0c;并可打开和关闭单个闹钟。闹钟到设定的时间后弹出提醒。将闹钟的定时数据保存到轻量级数据库。 相关概念 [Canva…

数据结构入门——排序(代码实现)(下)

int GetMidi(int* a, int left, int right) {int mid (left right) / 2;// left mid rightif (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]) // mid是最大值{return left;}else{return right;}}else // a[left] > a[mid…

MySQL-----多表查询(一)

目录 一.多表关系&#xff1a; 1.1 一对多(多对一)&#xff1a; 1.2 多对多: 1.3 一对一: 二.多表查询概述&#xff1a; 三.连接查询&#xff1a; 3.1内连接&#xff1a; 3.2外连接&#xff1a; 3.3自连接查询&#xff1a; 3.4联合查询&#xff1a; 一.多表关系&…

测试的分类(3)

目录 按照测试阶段测试 系统测试 冒烟测试和回归测试的区别 验收测试 单元测试, 集成测试, 系统测试, 回归测试之间的关系 是否按手工进行测试 手工测试 自动化测试 自动化测试和手工测试的优缺点 自动化测试优点 自动化测试缺点 手工测试优点 手工测试缺点 按照…

鸿蒙HarmonyOS应用 - ArkUI组件

ArkUI组件 基础组件 Image 声明Image组件并设置图片源 网络权限&#xff1a;ohos.permission.INTERNET Image(scr: string | PixelMap | Resource)// 1. string&#xff1a;用于加载网络图片&#xff0c;需要申请网络权限 Image("https://xxx.png")// 2. PixelMap…

快递物流订阅推送API接口如何对接

快递物流订阅推送API接口指的是订阅国内物流快递信息&#xff0c;当运单状态发生变化时&#xff0c;会推送到您的回调地址&#xff0c;直到这些运单号生命周期结束。简单点说就是先订阅快递单号再推送物流信息。那么快递物流订阅推送API接口该如何对接呢&#xff1f; 首先我们…

JVM学习笔记(四)类加载与字节码技术

目录 一、类文件结构 二、字节码指令 2.3 图解方法执行流程 1&#xff09;原始 java 代码 2&#xff09;编译后的字节码文件 3&#xff09;常量池载入运行时常量池 4&#xff09;方法字节码载入方法区 5&#xff09;main 线程开始运行&#xff0c;分配栈帧内存 6&…

道路检测车理想伴侣,国产高智能道路病害识别系统,可灵活兼容行车记录仪、无人机等数据源!

什么是视觉AI&#xff1f;通俗地说&#xff0c;视觉AI是机器代替人眼来做测量和判断&#xff0c;例如博雅仔为大家介绍的易模真人手办定制项目是基于公司独有的AI将拍摄到的实际影像“翻译”“制作”成数字3D模型&#xff0c;再经过3D打印固化成纪念手办送到用户朋友们的手上。…

将Python机器学习模型集成到C++ Qt客户端应用程序中|Qt调用python详解

0、前言 有几个不同的选项可以将你的Python机器学习模型集成到你的C Qt客户端应用程序中。以下是一些可能的解决方案&#xff1a; 创建API&#xff1a; 将你的机器学习模型部署为一个API服务。你可以使用像Flask这样的轻量级Web框架来创建一个简单的HTTP服务。这样&#xff0…

如何在一台服务器上同时运行搭载JDK 8, JDK 17, 和 JDK 21的项目:终极指南

&#x1f42f; 如何在一台服务器上同时运行搭载JDK 8, JDK 17, 和 JDK 21的项目&#xff1a;终极指南 &#x1f680; 摘要 在企业开发环境中&#xff0c;常常需要在同一台服务器上运行使用不同Java开发工具包&#xff08;JDK&#xff09;版本的多个项目。本文详细介绍如何在L…

华为鸿蒙应用--封装通用标题栏:CommonTitleBar(鸿蒙工具)-ArkTs

0、效果图 自定义通用标题栏 支持左、中、右常规标题栏设置&#xff1b; 支持自定义视图&#xff1b; 支持搜索功能 一、CommTitleBar代码 import router from ohos.router; import { Constants } from ../../constants/Constants; import { StyleConstants } from ../../…

JavaEE 初阶篇-深入了解 UDP 通信与 TCP 通信(综合案例:实现 TCP 通信群聊)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 UDP 通信 1.1 DatagramSocket 类 1.2 DatagramPacket 类 1.3 实现 UDP 通信&#xff08;一发一收&#xff09; 1.3.1 客户端的开发 1.3.2 服务端的开发 1.4 实现 …

FTP与SMB深度对比:文件传输协议谁更胜一筹?

在数字化时代&#xff0c;文件传输已成为日常工作中不可或缺的一部分。 FTP&#xff08;文件传输协议&#xff09;和SMB&#xff08;服务器消息块&#xff09;是两种最为常见的文件传输协议。它们各自在文件传输领域拥有独特的优势和特点&#xff0c;但同时也存在一些差异。 今…

六个月滴滴实习:轻松、舒心又高薪!

不久前&#xff0c;一位在滴滴后端研发部门实习了六个月的小伙伴在牛客网上分享了他的实习体验&#xff0c; 作者详细描述了他在滴滴的实习生活。 从他的叙述中&#xff0c;我们可以感受到与其他互联网公司相比&#xff0c;滴滴的工作环境显得相对轻松和舒适。 他提到&#x…

【汇编语言】流程转移和子程序

【汇编语言】流程转移和子程序 文章目录 【汇编语言】流程转移和子程序前言一、“转移”综述二、操作符offset三、jmp指令jmp指令——无条件转移jmp指令&#xff1a;依据位移进行转移两种段内转移远转移&#xff1a;jmp far ptr 标号转移地址在寄存器中的jmp指令转移地址在内存…

神经网络进阶

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

socket套接字在tcp客户端与tcp服务器之间的通信,以及socket中常用的高效工具epoll

1.socket&#xff08;套接字&#xff09;的概念 Socket是对TCP/IP协议的封装&#xff0c;Socket本身并不是协议&#xff0c;而是一个调用接口&#xff08;API&#xff09;&#xff0c;通过Socket&#xff0c;我们才能使用TCP/IP协议,主要利用三元组【ip地址&#xff0c;协议&am…

10G网络布线:DAC线缆与AOC光缆的选择指南

在10G网络部署中&#xff0c;选择合适的传输介质是确保网络性能和可靠性的关键。本文将全面比较10G DAC高速线缆和10G AOC有源光缆&#xff0c;帮助您做出明智的选择。 10G DAC高速线缆 VS 10G AOC有源光缆 定义与构造 10G DAC高速线缆&#xff08;Direct Attach Cable&…

配置有效的防爬虫技术保护网站

本文主要介绍了防爬虫的概念、目的以及一些有效的防爬虫手段。防爬虫是指网站采取各种技术手段阻止爬虫程序对其数据进行抓取的过程。为了保护网站的数据和内容的安全性&#xff0c;防止经济损失和恶意竞争&#xff0c;以及减轻服务器负载&#xff0c;网站需要采取防爬虫机制。…

文心一言4.0、智谱清言、MoonshotAI实测对比(上)

前言 前两天看到这张图&#xff0c;又刚好拿到了文心一言的4.0内测号&#xff0c;就想着把新版国内御三家横向对比测评一下。 文末领取免费领取AI学习基地 AI交流群 前一段时间也一直在研究复杂提示词&#xff08;结构化提示词&#xff09;向国内大模型迁移适配的问题&#…