Linux | 高级I/O函数

文章目录

  • 创建文件描述符的函数
    • pipe函数
    • dup函数、dup2函数
  • 读取或写入数据
    • readv函数、writev函数
  • 零拷贝
    • sendfile函数
    • splice函数
    • tee函数
  • 进程间通信——共享内存
    • mmap函数 和 munmap函数
  • 控制文件描述符
    • fcntl函数


创建文件描述符的函数

pipe函数

不再赘述,详情见我的另一篇博客。

值得一提的是,socket 基础API中有一个 socketpair函数,能够方便地创建双向管道:

#include<sys/types.h>
#include<sys/socket.h>
int socketpair(int domain, int type, int protocol, inf fd[2]);
// domain只能使用 UNIX 本地域协议族 AF_UNIX,因为我们仅能在本地使用这个双向管道。
// 成功时返回0,失败时返回-1并设置error。

dup函数、dup2函数

这两个函数会在 CGI服务器 中用到。CGI服务器: 主要是通过把服务器本地标准输入、输出或者文件重定向到网络连接中,以达到向标准输入、输出缓冲区中输入的信息,能在网络连接中发送的效果。

#include<unistd.h>
int dup( int file_descriptor );
int dup2( int file_descriptor_one, int file_descriptor_two );
  • dup: 创建一个新的文件描述符,该文件描述符和原有文件描述符 file_descriptor 指向相同的文件、管道或网络连接。返回的文件描述符总是取系统当前可用的最小整数值。
  • dup2:dup 类似,只是返回第一个不小于 file_descriptor_two 的整数值。

两个系统调用失败时都返回 -1,并设置 error

通过 dupdup2 创建的文件描述符并不继承原文件描述符的属性。比如:close-on-execnon-blocking 等。

dup 实现一个基本的 CGI服务器 的局部代码:

close( STDOUT_FILENO );
dup( connfd );
printf( "hello\n" );
close( connfd );

流程:

  1. 先关闭标准输出文件描述符 STDOUT_FILENO (其值是1);
  2. 通过 dup 复制 socket 文件描述符 connfd
  3. 由于 dup 总是返回系统中最小的可用文件描述符,所以它的返回值实际上是 1 ,即之前关闭的标准输出文件描述符的值;
  4. 服务器输出到标准输出的内容(“hello”)就会直接发送到与客户端对应的 socket 上,因此 printf 调用的输出将被客户端获得,而不是显示在服务器程序的终端上。

读取或写入数据

readv函数、writev函数

  • readv函数: 将数据从文件描述符读到分散的内存块中,即分散读;
  • writev函数: 将多块分配的内存数据一并写入文件描述符中,即集中写。

相当于简化版的 recvmsgsendmsg

#include<sys/uio.h>
ssize_t readv( int fd, const struct iovec* vector, int count );
ssize_t weitev( int fd, const struct iovec* vector, int count );
// fd:被操作的目标文件描述符
// vector:iovec结构数组,iovec封装了一块内存的起始位置和长度
// count:vector数组的长度,即有多少块内存数据需要从fd读出或写入到fd
// 成功时返回读出/写入fd的字节数,失败则返回-1并试着errno。

零拷贝

sendfile函数

sendfile函数: 用于在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高。被称为 零拷贝

#include<sys/sendfile.h>
ssize_t sendfile( int out_fd, int in_fd, off_t* offset, size_t count );
// out_fd:待写入内容的文件描述符,必须是一个socket
// in_fd:待读出内容的文件描述符,必须是一个支持类似mmap函数的文件描述符,即必须指向真实的文件,不能是socket和管道
// offset:指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件流默认的起始位置
// count:传输的字节数
// 成功时返回传输的字节数,失败返回-1,并设置errno

sendfile 几乎是专门为在网络上传输文件而设计的。


splice函数

splice函数: 本质就是借助管道描述符在两个文件之间移动数据,也是零拷贝

#include<fcntl.h>
ssize_t splice( int fd_in, loff_t* off_in, int fd_out, loff_t* off_out, size_t len, unsigned int flags );
// fd_in:待输入数据的文件描述符
// off_in:如果fd_in是一个管道描述符,那么off_in必须被设置为NULL;反之,如果fd_in不是一个管道描述符(如socket),那么off_in表示从输入数据流开始读取数据的起始位置。总而言之,若off_in不为NULL,则它将指出具体的偏移位置。
// fd_out/off_out:与fd_in/off_in类似,不过用于输出数据流。
// len:移动数据的长度
// flags:控制数据的移动方式

使用 splic函数fd_infd_out 必须至少有一个是管道文件描述符splice函数 调用成功时返回移动字节的数量。可能返回 0,表示没有数据需要移动,这发生在从管道中读取数据(fd_in 是管道文件描述符)而管道没有被写入任何数据时(fd_out 不是管道文件描述符)。splice函数失败时返回 -1 并设置 errno

常见的 errno

errno含义
EBADF参数所指文件描述符有错
ENOMEM内存不够
EINVAL目标文件系统不支持splice,或者目标文件以追加方式打开,或者两个文件描述符都不是管道文件描述符,或者某个 offset 参数被用于不支持随机访问的设备(如字符设备)
ESPIPE参数 fd_in(或fd_out) 是管道文件描述符,而 off_in(或off_out) 不为NULL

tee函数

tee函数: 两个管道文件描述符之间的 零拷贝。它不消耗数据,因此源文件描述符上的数据仍可以用于后续的读操作

#include<fcntl.h>
ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);
// 参数含义同splice,但 fd_in 和 fd_out 必须同时是管道文件描述符
// 成功时返回两个文件之间复制的数据数量(字节数),返回0表示没有复制任何数据,tee失败返回-1并设置errno。

tee 常和 splice 一起用,splice非管道管道绑定,teesplice 操作后得到的管道绑定在一起。


进程间通信——共享内存

mmap函数 和 munmap函数

  • mmap函数: 申请一段内存空间作为进程间通信的共享内存,也可以将文件直接映射到其中。
  • munmap函数: 释放由 mmap 创建的内存空间。
#include<sys/mman.h>
void* mmap( void* start, size_t length, int prot, int flags, int fd, off_t offset );
int munmap( void* start, size_t length );
// start:起始地址,为NULL时由系统自动分配一个地址。
// length:指定内存段的长度。
// prot:设置内粗段的访问权限,可取以下值的按位或:
//  PROT_READ,内存段可读。
//  PROT_WRITE,内存段可写。
//  PROT_EXEC,内存段可执行。
//  PROT_NONE,内存段不能被访问。
// flags:控制内存段内容被修改后程序的行为。
// fd:被映射文件对应的文件描述符,一般通过open调用获得。
// offset:参数设置从文件的何处开始映射(对于不需要读入整个文件的情况)。
// 成功时返回0,失败返回-1并设置errno。

mmap 的 flags 参数的常用值及其含义:

常用值含义
MAP_SHARED进程间共享这段内存,对该内存段的修改将反映到被映射的文件中。提供了进程间共享内存的 POSIX 方法
MAP_PRIVATE内存段为调用进程所私有,所有修改不会反映到被映射的文件中
MAP_ANONYMOUS这段内存不是从文件映射而来的,其内容被初始化为全0。此时 mmap 最后两个参数将被忽略。
MAP_FIXED内存段必须位于 start 指定的地址处,start 必须是内存页面大小(4096字节=4KB)的整数倍
MAP_HUGETLB按照“大内存页面”来分配内存空间,“大内存页面”由 /proc/meminfo 文件来擦查看

控制文件描述符

fcntl函数

fcntl函数: 全名 file control ,提供了对文件描述符的各种控制操作(类似于系统调用 ioctl,但 ioctlfcntl 提供的控制更多。)但是 fcntlPOSIX 规定的首选方法。

#include<fcntl.h>
int fcntl(int fd, int cmd, ...);
// fd:被操作的文件描述符
// cmd:执行何种操作
// 有可能需要第三个可选参数 arg
// 成功时返回值根据操作不同有所不同,失败时返回-1并设置errno

将文件描述符设置为非阻塞的:

int setnonblocking( int fd ){// F_GETFL 获取 fd 的标志,成功时返回 fd 的标志int old_option = fcntl(fd, F_GETFL); // 获取文件描述符旧的状态标志int new_option = old_option | O_NONBLOCK; // 设置非阻塞标志fcntl(fd, F_SETFL, new_option); // F_SETFL 设置 fd 的标志return old_option; // 返回文件描述符旧的状态标志,以便日后恢复该状态标志
}

题外话:SIGIOSIGURG 这两个信号与其他 Linux 信号不同,他们必须与某个文件描述符相关联方可使用:

  • 被关联文件描述符可读可写时,系统将触发 SIGIO 信号。
  • 被关联文件描述符是一个 socket有带外数据可读时,系统将触发 SIGURG 信号。

这两个信号就是通过 fcntl函数 与文件描述符关联的,具体做法是:fcntl函数 为目标文件描述符指定宿主进程进程组,被指定的宿主进程进程组去捕获这两个信号。

特别的,使用 SIGIO 时,还需利用 fcntl 设置其 O_ASYNC标志(异步I/O标志,不过SIGIO信号模型并非真正意义上的异步I/O模型)。

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

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

相关文章

分布式理论:CAP、BASE | 分布式存储与一致性哈希

文章目录分布式理论CAP定理BASE理论分布式存储与一致性哈希简单哈希一致性哈希虚拟节点分布式理论 CAP定理 一致性&#xff08;Consistency&#xff09;&#xff1a; 在分布式系统中的所有数据副本&#xff0c;在同一时刻是否一致&#xff08;所有节点访问同一份最新的数据副…

Tomcat服务器性能优化

一、概述 本文档主要介绍了Tomcat的性能调优的原理和方法。可作为公司技术人员为客户Tomcat系统调优的技术指南&#xff0c;也可以提供给客户的技术人员作为他们性能调优的指导手册。 二、调优分类 由于Tomcat的运行依赖于JVM&#xff0c;从虚拟机的角度我们把Tomcat的调整分为…

分布式系统概念 | 分布式事务:2PC、3PC、本地消息表

文章目录分布式事务2PC&#xff08;二阶段提交协议&#xff09;执行流程优缺点3PC&#xff08;三阶段提交协议&#xff09;执行流程优缺点本地消息表&#xff08;异步确保&#xff09;分布式事务 分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分…

数据结构算法 | 单调栈

文章目录算法概述题目下一个更大的元素 I思路代码下一个更大元素 II思路代码132 模式思路代码接雨水思路算法概述 当题目出现 「找到最近一个比其大的元素」 的字眼时&#xff0c;自然会想到 「单调栈」 。——三叶姐 单调栈以严格递增or递减的规则将无序的数列进行选择性排序…

最长下降子序列

文章目录题目解法DP暴搜思路代码实现贪心二分思路代码实现题目 给出一组数据 nums&#xff0c;求出其最长下降子序列&#xff08;子序列允许不连续&#xff09;的长度。&#xff08;类似于lc的最长递增子序列&#xff09; 示例&#xff1a; 输入&#xff1a; 6 // 数组元素个…

Linux 服务器程序规范、服务器日志、用户、进程间的关系

文章目录服务器程序规范日志rsyslogd 守护进程syslog函数openlog函数setlogmask函数closelog函数用户进程间的关系进程组会话系统资源限制改变工作目录和根目录服务器程序后台化服务器程序规范 Linux 服务器程序一般以后台进程&#xff08;守护进程[daemon]&#xff09;形式运…

IO模型 :阻塞IO、非阻塞IO、信号驱动IO、异步IO、多路复用IO

文章目录IO模型阻塞IO非阻塞IO信号驱动IO多路复用IO异步IOIO模型 根据各自的特性不同&#xff0c;IO模型被分为阻塞IO、非阻塞IO、信号驱动IO、异步IO、多路复用IO五类。 最主要的两个区别就是阻塞与非阻塞&#xff0c;同步与异步。 阻塞与非阻塞 阻塞与非阻塞最主要的区别就…

Tomcat服务器集群与负载均衡实现

一、前言 在单一的服务器上执行WEB应用程序有一些重大的问题&#xff0c;当网站成功建成并开始接受大量请求时&#xff0c;单一服务器终究无法满足需要处理的负荷量&#xff0c;所以就有点显得有点力不从心了。另外一个常见的问题是会产生单点故障&#xff0c;如果该服务器坏掉…

Linux服务器 | 事件处理模式:Reactor模式、Proactor模式

文章目录Reactor模式Proactor模式同步I/O模型模拟Proactor模式两者的优缺点ReactorProactor同步I/O模型通常用于实现 Reactor 模式&#xff0c;异步I/O模型通常用于实现 Proactor 模式。&#xff08;不是绝对的&#xff0c;同步I/O也可模拟出 Proactor 模式&#xff09; React…

Linux服务器 | 服务器模型与三个模块、两种并发模式:半同步/半异步、领导者/追随者

文章目录两种服务器模型及三个模块C/S模型P2P模型I/O处理单元、逻辑单元、存储单元并发同步与异步半同步/半异步模式变体&#xff1a;半同步/半反应堆模式改进&#xff1a;高效的半同步/半异步模式领导者/追随者模式组件 &#xff1a;句柄集、线程集、事件处理器工作流程两种服…

香农信息熵之可怜的小猪

文章目录题目解析香农熵公式样例具体分析代码题目 有 n 桶液体&#xff0c;其其中 正好 有一桶含有毒药&#xff0c;其装的都是水。它们从外观看起来都一样。为了弄清楚哪只水桶含有毒药&#xff0c;你可以喂一些猪喝&#xff0c;通过观察猪是否会死进行判断&#xff0c;实验对…

字符串匹配之KMP(KnuthMorrisPratt)算法(图解)

文章目录最长相等前后缀next数组概念代码实现图解GetNext中的回溯改进代码实现代码复杂度分析最长相等前后缀 给出一个字符串 ababa 前缀集合&#xff1a;{a, ab, aba, abab} 后缀集合&#xff1a;{a, ba, aba, baba} 相等前后缀 即上面用同样颜色标识出来的集合元素&#…

linux下tomcat6.0与jdk安装详细步骤

安装Tomcat6.0和JDK1.6 在linux系统上安装tomcat和jdk应该说是我学习linux知识的第一课了&#xff0c;之前只 是听说过&#xff0c;从没接触过&#xff0c;但我们公司项目都是部署在linux系统上的&#xff0c;那天上司突 然给我发了几个文档&#xff0c;让我看一下&#xff…

Android入门(一) | Android Studio的配置与使用

文章目录安装配置Android Studio使用Android Studio模拟器更改Android SDK的路径Hello World&#xff01;安装配置Android Studio 从这一步开始&#xff1a; 一直点 next 即可&#xff0c;直到存储路径的选择上&#xff0c;可以放到非 C 盘&#xff0c;这里我放到 D 盘了&am…

Android 入门(四) | Intent 实现 Activity 切换

文章目录Intent显式 Intent定义两个 xml 文件android:orientationmatch_parent 和 wrap_contentIntent函数定义两个 Activity隐式 Intent更多隐式 Intent 的用法用隐式 Intent 打开系统浏览器自建 Activity 以响应打开网页的 Intent向下一个活动传递数据返回数据给上一个活动In…

Android入门(二) | 项目目录及主要文件作用分析

文章目录项目目录分析app目录分析AndroidManifest.xml 分析MainActivity.kt 分析build.gradle 分析最外层目录下的 build.gradleapp 目录下的 build.gradle项目目录分析 我们来看一下 src/main/res 下的一些文件&#xff1a; .gradle 和 .idea &#xff1a;这两个目录下放置…

Android入门(三) | Android 的日志工具 Logcat

文章目录日志工具类 android.util.LogLogcat 中的过滤器日志工具类 android.util.Log Log 从属日志工具类 android.util.Log &#xff0c;该类提供了五个方法供我们打印日志&#xff1a; Log.v() &#xff1a;用于打印那些最为琐碎的、意义最小的日志信息。对应级别 verbose&…

Android 客户端与服务器交互方式

突然想到一个问题就是Android客户端与服务器交互有几种方式&#xff0c;因为在脑袋里想当然的就是webservices和json。要在Android手机客户端与pc服务器交互&#xff0c;需要满足下面几种条件&#xff1a;跨平台、传输数据格式标准、交互方便...。 为了与服务器通讯其实无非就…

Android入门(五) | Activity 的生命周期

文章目录Activity 的状态及生命周期实现管理生命周期FirstActivitySecondActivityDialogActivity运行结果旧活动被回收了还能返回吗&#xff1f;Activity 的状态及生命周期 Android 的应用程序运用 栈&#xff08;Back Stack&#xff09; 的思想来管理 Activity&#xff1a; …

Android入门(六) | Activity 的启动模式 及 生产环境中关于 Activity 的小技巧

文章目录Activity 的启动模式standardsingleTopsingleTasksingleInstance技巧了解当前界面是哪个 Activity随时随地退出程序启动活动的最佳写法Activity 的启动模式 standard&#xff1a;默认的启动方式&#xff0c;每次启动一个活动都会重新创建singleTop&#xff1a;如果该活…