news 2026/5/5 8:52:26

Linux系统编程-文件描述符

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux系统编程-文件描述符

一. 文件描述符

1.1 文件描述符的概念

本质:文件描述符本质是一个整型数,是一个数组的下标。

其中:默认打开数组的前三个 0:STDIN_FILENO:标准输入

1:STDOUT_FILENO:标准输出

2:STDERR_FILENO:标准出错

注意:文件描述符在使用时优先使用当前可用位置最小的。

二. 阻塞与非阻塞

2.1 概念

读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端设备网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定。

现在明确一下阻塞(Block)这个概念。当进程调用一个阻塞的系统函数时,该进程被置于睡眠(Sleep)状态,这时内核调度其它进程运行,直到该进程等待的事件发生了(比如网络上接收到数据包,或者调用sleep指定的睡眠时间到了)它才有可能继续运行。与睡眠状态相对的是运行(Running)状态,在Linux内核中,处于运行状态的进程分为两种情况:

(1)、正在被调度执行。CPU处于该进程的上下文环境中,程序计数器(eip)里保存着该进程的指令地址,通用寄存器里保存着该进程运算过程的中间结果,正在执行该进程的指令,正在读写该进程的地址空间。

(2)、就绪状态。该进程不需要等待什么事件发生,随时都可以执行,但CPU暂时还在执行另一个进程,所以该进程在一个就绪队列中等待被内核调度。系统中可能同时有多个就绪的进程,那么该调度谁执行呢?内核的调度算法是基于优先级和时间片的,而且会根据每个进程的运行情况动态调整它的优先级和时间片,让每个进程都能比较公平地得到机会执行,同时要兼顾用户体验,不能让和用户交互的进程响应太慢。

综上:(1) 阻塞(Block)产生的场景:读设备文件,读网络文件。(读常规文件无阻塞特性)

(2) 针对红色字体"如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞"做以下解释:缓冲区有以下三种模式:

模式备注设备
行缓冲换行/满了刷新,也可强制刷新终端设备
全缓冲满了刷新,或者强制刷新默认,只要不是终端设备。(比如文件)
无缓冲需要立即输出的内容如stderr

由于终端是行缓冲模式,这就决定了输入的数据没有换行符就会一直阻塞。

(3)阻塞是文件的属性(针对设备文件与网络文件),不是read,write函数的属性。

eg:终端文件/dev/tty----默认是阻塞属性。

2.2 代码示例

2.2.1 阻塞

2.2.2 修改文件阻塞属性

若要修改终端文件的阻塞属性,则要使用open函数以非阻塞(O_NONBLOCK)的形式打开终端文件。

这里有一个重点:在read函数那一块说到函数读取失败时返回值为-1并且设置errno。但是当read

读取一个非阻塞形式的文件时并且没有数据可读时也返回-1,errno被设置为EA

GAIN或者EWOULDBLOCK(二者相等),这种情况并不是读取失败情况,故需 要对返回值以及errno的值加以判断!

三. 重定向

3.1 重定向命令

cat 12_dup.c > test

cat 12_dup.c >> test //以追加方式重定向

eg:

以下是顺序执行这三条指令后test3中的内容:

3.2 重定向函数

3.2.1 dup函数

作用:dup() 系统调用会创建一个与文件描述符 oldfd 相同的副本,同时会使用编号最小且尚未被使用的文件描述符来作为新描述符。

返回值:成功:返回一个新的文件描述符

失败:返回-1并且设置errno

原理:

eg:

执行之前的test:

执行之后的test:

3.2.2 dup2函数

作用:dup2() 系统调用的功能与 dup() 相同,但其不同之处在于,它并非使用未被占用的最小文件描述符编号,而是使用 newfd 中指定的文件描述符编号。如果文件描述符 newfd 之前已处于打开状态,那么在重新使用之前,它会被自动关闭如果oldfd=newfd,则dup2函数什么也不做,返回newfd本身。

返回值:成功:返回一个新的文件描述符newfd

失败:返回-1并且设置errno

eg:

3.2.3 dup函数的缺点

缺点:操作不原子。

原子操作:不可被分割的操作。 作用:解决竞争和冲突。(要么没做,要么一步做完)

上面这个例子是在out文件里面打印hehe!,但是有两条语句操作不原子,就是:

close(1); dup(fd);

如果在dup(fd);之前有其他进程使用了dup()这个函数,那么1这个位置将会被占用,hehe!将会打印到其他文件里面。

还有一个问题:如果fd本身就是1,close(1) 之后fd已经失效,又dup(fd),则会出现错误!

解决方法:使用dup2函数----原子操作:关闭与重定向一起操作。

dup2(fd,1) 这条语句首先会关闭1,然后将1作为fd的副本。----保证了原子操作。

如果fd=1,那么这条语句什么也不做,并且返回newfd。

宏观编程思想:不要内存泄漏。不要越界。不要认为自己在写一个main函数,永远认为自己在写一个模块。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/5 8:51:27

如何免费解锁WeMod高级功能:5步快速配置完整指南

如何免费解锁WeMod高级功能:5步快速配置完整指南 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/gh_mirrors/we/Wand-Enhancer 想要免费享受WeMod专业版的所有高级功能吗&…

作者头像 李华
网站建设 2026/5/5 8:47:26

基于节点持续性的脑功能网络多尺度拓扑分析:揭示自闭症谱系障碍的环路角色重组(世毫九实验室原创研究)

基于节点持续性的脑功能网络多尺度拓扑分析:揭示自闭症谱系障碍的环路角色重组 作者:方见华 单位:世毫九实验室 摘要 自闭症谱系障碍(ASD)作为一种复杂的神经发育障碍,其核心特征表现为社交沟通缺陷、重复刻…

作者头像 李华
网站建设 2026/5/5 8:37:27

从芯片内部看世界:手把手教你用Verilog在FPGA上点灯(入门必备的5个基础门电路实验)

从芯片内部看世界:手把手教你用Verilog在FPGA上点灯 第一次接触FPGA开发时,我被这个神奇的世界深深吸引——几行代码就能在硬件上创造出各种逻辑功能。还记得当初点亮第一个LED时的兴奋,仿佛打开了数字世界的大门。本文将带你从最基础的门电路…

作者头像 李华
网站建设 2026/5/5 8:31:30

Python 爬虫反爬突破:访问轨迹随机化模拟真人操作

前言 随着 Web 风控体系持续迭代升级,单纯的接口参数伪造、IP 切换、请求头伪装等基础反爬手段已无法满足高防护站点的数据采集需求。现代化互联网平台不再仅依赖 IP 封禁与参数签名校验,而是深度依托用户访问行为轨迹、页面浏览逻辑、操作时序特征、交互行为习惯等多维行为…

作者头像 李华