0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux系统编程中的文件描述符调用

C语言编程学习基地 来源:博客园 作者:melonstreet 2021-09-02 09:50 次阅读

文件描述符

进程每打开一个文件的时候,会获得该文件的文件描述符,而后续的读写操作都把文件描述符作为参数。在用户空间或者内核空间,都是通过文件描述符来唯一地索引一个打开的文件。文件描述符使用int类型表示,文件描述符的范围从0开始,到上限值-1,默认情况下,上限值为1024,也就是说,进程默认情况下最多可以打开1024个文件。负数是不合法的文件描述符,当函数调用出错时,返回的文件描述符为-1。

每个进程都至少包含三个文件描述符:

8c65cdc2-0b7a-11ec-8fb8-12bb97331649.png

遵循Linux一切皆文件的概念,文件描述符除了访问普通文件外,几乎能够访问任何能够读写的东西。包括设备文件、管道、先进先出缓冲区、套接字等。

open()系统调用

对文件进行读写之前,必须先打开文件。Linux提供了系统调用open()。open()有两个函数原型:

8c6f5fc2-0b7a-11ec-8fb8-12bb97331649.png

两个函数均可用来打开文件,第二个函数比第一个多了参数mode,mode指定文件的权限---当创建新文件的时候才需要。如果文件打开成功,则返回文件描述符,指向pathname所指定的文件。flags参数用于指定打开的方式,它支持三种访问模式:

8c7b926a-0b7a-11ec-8fb8-12bb97331649.png

flags参数还可以与下面的值进行按位或运算,修改打开文件的行为:

8c8c4a4c-0b7a-11ec-8fb8-12bb97331649.png

举个例子,下面的句子表示:以写的方式打开文件,如果文件不存在,则创建新的文件,并且文件的内容为空:

int fd ;

fd = open("file.txt",O_WRONLY|O_CREAT|O_TRUNC,0644);

这里的0644指定了新创建的文件访问权限,参数mode的取值如下:

8c97b33c-0b7a-11ec-8fb8-12bb97331649.png

实际上最终写入磁盘的文件访问权限是由mode参数和用户的文件创建掩码(umask)执行按位与操作得到的。举个例子:

8ca9a132-0b7a-11ec-8fb8-12bb97331649.png

按理来说,创建出来的文件的访问权限应该是-rwxrwxrwx,而查看后发现其实不是:

ls -l TEST.txt

-rwxrwxr-x 1 huanzhewu huanzhewu 0 8月 30 21:29 TEST.txt【权限为0775】

查看当前的掩码:

$ umask

0002

可以发现 0775 = 0777 ^ (~0002) ,所以0775才是最后的文件访问权限。umask是进程级属性,通过调用umask()函数来修改,支持用户修改新创建的文件和目录的权限。

总结起来可以得到这样一条公式:

newmode = mode ^ (~ umask)

总结一下:至此,我们了解了文件打开所提供的两个系统调用函数open(),了解了打开文件的方式、新建文件的访问权限设置。如果文件打开成功,那么将返回一个文件描述符,这是一个非零整数(因为0,1,2是进行已经拥有的文件描述符),否则函数将返回-1

creat()系统调用

顾名思义,creat函数用来创建一个文件,不过我们可能产生疑问:前面的open函数使用一些选项后,不是也可以创建新文件吗?没错,creat函数完全等价与下面的open语句:

8cb24800-0b7a-11ec-8fb8-12bb97331649.png

由于选项O_WRONLY|O_CREAT|O_TRUNC组合经常使用,因而系统调用专门使用creat函数来提供这个功能。creat函数的原型如下:

8cbdfdb2-0b7a-11ec-8fb8-12bb97331649.png

其中参数的描述与open的参数一致,这里不再赘述。

read()系统调用

文件打开后,就能够读文件了。read()是最基础、最常见的读取文件的机制。read的函数原型为:

8cd2fe10-0b7a-11ec-8fb8-12bb97331649.png

fd 为文件描述符。每次调用read函数时,会从fd指向的文件的当前偏移(或称文件位置)开始读取count字节到buf所指的的内存中。随着文件的读取,fd的文件位置指针会向前移动。关于read的读取,会出现很多需要思考的问题:

问题一:如果文件长度为0

问题二:如果文件长度不够count长度

问题三:如果读取时,read被信号中断了

我们一一来看:

问题1属于“没有数据可读”,此时read调用会阻塞,直到有数据可读;

问题2属于到达数据结尾(EOF),此时read调用返回0;

问题三,read调用返回大于0小于count的值;如果在读取任何数据之前被信号中断,则返回-1,同时把errno设置为EINTR。

8fdd37ba-0b7a-11ec-8fb8-12bb97331649.png

再来看看问题1,当文件没有数据可以读时(一开始就没有),read调用会被阻塞,直到文件有数据可以读,这是一种阻塞I/O。如果文件以O_NONBLOCK模式打开,则文件为非阻塞模式,当文件没有数据可以读时,read系统调用返回-1,并把errno设置为EAGAIN。

8fe9f2f2-0b7a-11ec-8fb8-12bb97331649.png

除了errno被设置为EINTR与EAGAIN,其他情况下都是出现严重的文件读取错误,重新执行读操作不会成功。

write() 系统调用

write的函数原型为:

9000f858-0b7a-11ec-8fb8-12bb97331649.png

write的返回值比较简单:

写入失败返回-1 ,同时设置errno的值

写入成功返回成功写入的字节数。

返回0时没有特殊含义,仅表示写入了0个字节的内容。

对于普通文件,write基本能保证每次执行调用能够写入全部的内容。对于其他文件如socket,需要进行循环写,保证所有的字节都写入了文件中:

9014a24a-0b7a-11ec-8fb8-12bb97331649.png

同样的,当以非阻塞的模式打开文件时(O_NONBLOCK),系统调用write()会返回-1,并把errno设置为EAGAIN。

系统调用write()时,数据从用户空间的缓冲区中拷贝到了内核空间的缓冲区,但并没有立即把数据写入磁盘中,这称为延迟写。延迟写的问题在于,如果在数据真正写入磁盘之前系统崩溃了,则数据可能丢失。内核设置了一个时间,在该时间内将内核空间缓冲区上的数据写入磁盘,该时间称为"最大存放时效"。Linux系统也支持强制文件立即写入磁盘上,这在后面介绍。

close()系统调用

程序完成文件的读写后,调用close函数关闭文件描述符与文件之间的连接,使得文件描述符可以被重用。close的函数原型为:

#incldue

int close(int fd);

文件关闭成功返回0,出错返回-1,并设置相应的errno。文件成功关闭并不以为着该文件的数据已经被写入磁盘。

总结:本文简单介绍了文件的打开、创建、读写、关闭操作,介绍了一些常用的open参数选项,creat与open的等价性,循环读、循环写的必要性,也关注了各个系统调用的返回值含义,了解如何设置非阻塞读写,并简单提到了延迟写的问题,在后续的文件中将介绍同步I/O的内容。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11342

    浏览量

    210298
  • 编程
    +关注

    关注

    88

    文章

    3637

    浏览量

    93944

原文标题:Linux系统编程:基本 I/O 系统调用

文章出处:【微信号:cyuyanxuexi,微信公众号:C语言编程学习基地】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    服务器数据恢复——Ext4文件系统umount失败的数据恢复案例

    块组:Ext4文件系统的全部空间被划分为若干个块组,每个块组结构基本上相同。 块组描述符表:每个块组都对应一个块组描述符,这些块组描述符统一放在
    的头像 发表于 11-13 13:25 437次阅读
    服务器数据恢复——Ext4<b class='flag-5'>文件系统</b>umount失败的数据恢复案例

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-网络编程示例之网络socket程序编程

    ,通过调用 getsockopt 函数获取与该文件描述符相关的 TCP 连接信息,并判断连接状态是否为已建立(TCP_ESTABLISHED)。如果连接状态为已建立,则返回 0;否则返回-1
    发表于 11-13 09:12

    飞凌嵌入式ElfBoard ELF 1板卡-网络编程示例之网络socket程序编程

    。下面我们看下详细解释:func_detect_tcp_server_link(int fd): 检测 TCP 服务端连接状态的函数。它接受一个文件描述符 fd,通过调用 getsockopt 函数获取
    发表于 11-12 10:53

    Linux--IO多路复用(select,poll,epoll)

    导致的资源浪费和低效率问题。它通过将多个IO操作合并到一个系统调用,允许程序同时等待多个文件描述符(如sockets、
    的头像 发表于 11-06 16:13 427次阅读

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-Linux C接口编程入门之文件I/O

    程之间的通信。管道和套接字也被视为文件,并通过文件描述符进行访问。虚拟文件系统(Virtual File System):虚拟文件系统
    发表于 10-10 09:11

    飞凌嵌入式ElfBoard ELF 1板卡-Linux C接口编程入门之文件I/O

    程之间的通信。管道和套接字也被视为文件,并通过文件描述符进行访问。虚拟文件系统(Virtual File System):虚拟文件系统
    发表于 10-09 15:38

    esp32-s2-soala-v1.2如何获取摄像头描述符

    descriptors from PC side,eg. run `lsusb -v` in linux, 请问我该如何获得所需usb描述符
    发表于 06-27 06:48

    二维PDMA可以使用描述符链吗?

    我正在尝试使用二维描述符连锁。 编写了一些二维描述符链的代码。 但我有一些疑问,比如 1.二维 PDMA 可以使用描述符链吗? 2.如果 1 是,请附上一些代码 我们是否可以使用 2 个结构或只使用 1 个结构即可。
    发表于 05-31 08:16

    FX3无法设置最小/最大比特率描述符值,为什么?

    我正试图通过 FX3 以 5200*3900 分辨率、15fps 的速度从我的 fpga 传输视频数据流,但无法设置最小/最大比特率描述符值,因为描述符大小只有 32 位。 描述符的预期值应该是
    发表于 05-21 06:36

    STM32 USB的字符串描述符不能显示是哪里的问题?

    最近用STM32做了一个USB 的HID类设备,设备描述符中有指定厂商字符串索引、产品字符串索引、序列号索引。在获取字符串的代码部分也发现确实执行了,但是插上电脑后却没有我想要的字符串,有哪位高手能指点一下。
    发表于 04-30 08:13

    STM32F103在官方USB的MSC程序基础上修改为CDC+MSC的组合设备,出现设备描述符请求失败的原因?

    STM32F103在官方USB的MSC程序基础上修改为CDC+MSC的组合设备,出现设备描述符请求失败问题;抓包发现PC主机配置描述符数据和设置的数据不一致
    发表于 04-07 06:34

    CDC+MSC设备连接不了PC,显示设备描述符请求失败的原因?

    USB组合设备:CDC+MSC,设备连接不了PC,显示设备描述符请求失败;用网上其它成功的例程测试,不是该情况就是只能使用U盘无法用虚拟串口,请问这是什么问题呢
    发表于 04-03 06:44

    USB OTG发送设备描述符失败是什么原因呢?

    能收到SETUP包,并解析数据跑到发送设备描述符,给FIFO写数据也正确,但波形上就发了一个数据出去,这会是什么原因呢?
    发表于 03-11 06:29

    为FX3应用程序实现可变USB配置描述符,开始编译程序时报错怎么解决?

    __heap_start” 和 “extern char __heap_end” 的错误。 我该如何解决这个 _sbrk 错误? 有没有更好的方法来处理可变大小的 USB 描述符? 使用固定大小的数组,我 CAN 创建一个正确的描述符,但在设备管理器
    发表于 02-26 06:58

    CYUSB3KIT-003开发板cyfxusbuart例程,配置描述符无效枚举失败的原因?

    系统:win11 驱动已正常安装,出厂默认例程在I2C启动及USB启动下均可正常运行。 选择USB启动,下载cyfxusbuart固件后,设备管理器显示配置描述符无效。 请问是否为驱动问题?
    发表于 02-23 06:02