APUE第三章学习扎记之3.14节fcntl函数实例3-4程序详解

本文详细解析fcntl函数如何获取文件状态标志,包括只读、只写、可读可写模式,通过代码实例展示了如何使用atoi和argv参数,以及对标准输入输出重定向的影响。此外,文章还探讨了不同文件描述符的使用方式与标准输入输出的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文用意如题。对应的程序在APEU书本中的64页,对应的代码为fig3.10。

F_GETFL这个cmd作用是将对应的filedes的文件状态标志作为函数值返回。其中文件状态标志可以理解为文件的状态说明。比如打开文件以只读不写的方式打开则其状态为:O_RDONLY;又如文件以可读可写方式打开则其状态为:O_RDWR。特别地,如果filedes对应的文件不存在(也即:filedes未使用),函数fcntl会返回错误。

接下来说明下程序涉及到的文件状态标志的预定义:

笔者使用的系统是:Linux version 2.6.32-358.23.2.el6.x86_64(CentOS 6.4)

文件状态标志定义在:/usr/include/asm-generic/fcntl.h,打开可见:

#define O_ACCMODE    00000003
#define O_RDONLY    00000000
#define O_WRONLY    00000001
#define O_RDWR        00000002
#ifndef O_CREAT
#define O_CREAT        00000100    /* not fcntl */
#endif
#ifndef O_EXCL
#define O_EXCL        00000200    /* not fcntl */
#endif
#ifndef O_NOCTTY
#define O_NOCTTY    00000400    /* not fcntl */
#endif
#ifndef O_TRUNC
#define O_TRUNC        00001000    /* not fcntl */
#endif
#ifndef O_APPEND
#define O_APPEND    00002000
#endif
#ifndef O_NONBLOCK
#define O_NONBLOCK    00004000


我们可以特别关注上面定义中的黄色部分,就是我们需要用的内容。

好的,代码说明正式开始:

val = fcntl(atoi(argv[1]), F_GETFL, 0)

对于atoi函数谷歌下可知是用来把字符串转换成整数返回的。而argv[1]又是什么呢?这个代表的是main函数的参数,不懂可参考APUE7.4的内容,特别说明:如果对文件描述符进行重定向,那么重定向部分是不会作用输入参数的,如对于7.4处的程序编译后运行:./echoarg 1 1>temp.foo,由于重定向了标准输出,所以查看temp.foo可以看到:

argv[0]:./echoarg

argv[1]:1

所以重定向部分1>temp.foo是没有作为main的输入参数的。

接下来比较麻烦的就是(val & O_ACCMODE)了。我们知道val是fcntl返回的文件标志,由于访问方式标志(O_RDONLY、O_WRONLY、O_RDWR)并非各占1位,所以需要用屏蔽字O_ACCMODE取得访问模式位之后再与之进行比较。特别说明:&代表的是按位相与。理解这一点后面的就能够理解了。

接下来就是这篇博客的精彩部分了,笔者分别对这个程序进行了8个测试:

1)./a.out 0(或者1、2)

结果:打印read write

理解:默认0/1/2都是在使用的,并且使用的方式为:可读可写。

2)./a.out 0 0<temp.foo

结果:打印read only

理解:对标准输入进行重定向,对其进行重定向输入,也即将默认的可读可写改变为只读(read)。

3)./a.out 0 0>temp.foo

结果:打印write only

理解:对标准输入进行输出重定向,将其默认的可读可写改变为只写(write)。此时由于标准输出没有进行重定向,所以仍然将结果打印到终端,而查看temp.foo中是没有内容的,因为我们程序中并没有对fd=0写任何内容。

这个地方很有意思,我们一般理解fd=0为标准输入,此处对其进行输出重定向也是可以的。从这点我们可以看到在内核里面,对于fd=0,1,2只是默认使用并且默认为可读可写,除了这两点外其实fd=0,1,2和普通的文件描述符是没有区别的。

4)./a.out 1 1>temp.foo

结果:终端无打印,打开temp.foo中有内容:write only

理解:对标准输出进行输出重定向到temp.foo中,改变了标准输出默认的可读可写为只写(write)。

5)./a.out 3 3>temp.foo

结果:打印write only,查看temp.foo无内容

理解:标准输出没有改变,所以仍然将结果打印到终端;而由于对fd=3进行了重定向,这就相当与对fd=3进行了使用,此处是输出重定向,所以是只写(write),程序对fd=3没有写任何东西所以temp.foo无内容。

6)./a.out 3

结果:返回错误: fcutl error for fd 3:Bad filedescriptor

理解:在笔者的操作系统环境下,fd=3没有被使用,此时val返回值是37777777777(16进制数)。

为了证明这个问题,笔者修改了代码,将fd=3作为一个文件只写打开的文件描述符(利用总是分配可用的最小文件描述符的特点,将fd=3先关闭后再打开文件,验证返回文件描述符为3),这时val=0010 0001,用屏蔽字取值后证明是O_WRONLY,只读。

7)./a.out 3 3<>temp.foo

结果:打印read write,temp.foo无内容

理解:对fd=3进行了输入输出重定向,可读可写,但没有对temp.foo进行写内容所以temp.foo无内容。

其他的文件状态标志(O_APPEND、O_SYNC等)与上面所述类似,不再赘述。

转载于:https://my.oschina.net/DanielLee/blog/177321

### APUE 第三章 学习 #### 文件 I/O 基础 APUE第三章主要讨论了 Unix 系统中的文件 I/O 操作基础。这一章涵盖了多个重要的概念和技术细,对于理解如何高效地操作文件至关重要。 #### 打开和关闭文件 为了打开一个文件,程序通常会使用 `open` 或者 `creat` 函数[^1]。这两个函数都返回一个小于零的整数作为错误指示,而成功的调用则返回一个非负整数表示新创建的文件描述符。当不再需要访问某个特定文件时,应该通过调用 `close` 来关闭它。这不仅释放了与该文件关联的操作系统资源,而且也使得这个文件描述符能够被重新利用。 ```c #include <fcntl.h> /* For O_* constants */ #include <unistd.h> /* For open(), close() */ int fd; fd = open("example.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd >= 0) { // File opened successfully. } // Later... close(fd); ``` #### 文件读写 一旦有了有效的文件描述符,就可以对其进行读取 (`read`) 和写入 (`write`) 操作。这些基本的 I/O 操作允许应用程序直接处理底层的数据流而不必关心具体的设备特性[^2]。 ```c char buffer[BUFSIZ]; ssize_t n; n = read(fd, buffer, BUFSIZ - 1); if (n > 0) { buffer[n] = &#39;\0&#39;; // Null terminate the string printf("%s\n", buffer); } const char *msg = "Hello world!"; write(fd, msg, strlen(msg)); ``` #### 文件定位 除了简单的顺序读写外,还可以改变当前文件偏移量来实现随机访问。这是通过 `lseek` 实现的功能之一,它可以向前或向后移动文件指针的位置以便从不同的位置开始读写数据[^3]。 ```c off_t offset; offset = lseek(fd, SEEK_SET, 0); // Move to beginning of file if (offset != -1L) { // Seek succeeded. } ``` #### 特殊文件类型的支持 Unix 系统支持多种特殊类型的文件对象,比如管道、套接字以及终端设备等。本章还介绍了针对这些不同类型文件的具体 API 接口和支持机制[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值