理论教育 实践:Linux操作系统文件操作

实践:Linux操作系统文件操作

时间:2023-11-25 理论教育 版权反馈
【摘要】:系统调用提供的文件操作类系统调用有open、 close、 read、 write、 ioctl 等, 常用功能函数原型描述如下。如果打开文件成功, 将返回一个可被read、 write 等系统调用使用的文件描述符, 该描述符帮助进程唯一地定位到该文件。

实践:Linux操作系统文件操作

系统调用提供的文件操作类系统调用有open、 close、 read、 write、 ioctl 等, 常用功能函数原型描述如下。

1. 打开文件

注意对文件读写操作前, 一定要先执行打开文件操作, 打开操作的目的是找到文件位置并登记到系统, 以免每次读写操作时都要反复查找文件在哪里。 如果打开文件成功, 将返回一个可被read、 write 等系统调用使用的文件描述符, 该描述符帮助进程唯一地定位到该文件。 open 或creat 函数都可实现打开文件, 其头文件及函数原型说明如下。

常用参数说明如下。

(1)pathname: 要打开的文件名, 可以是相对路径也可以是绝对路径。

(2)flags: 标志位, 指定打开文件的操作方式及打开时的行为, 该参数有以下几个基本取值。

①O_RDONLY、 O_WRONLY、 O_RDWR: 必须项, 指定打开文件的操作方式(只读、只写、 读写), 这些标志位不能同时用, 可与后面标志用按位或的方式组合起来使用。

②O_CREAT: 如果被打开的文件不存在, 则自动创建新文件, 一般要给出mode 参数(mode 模式值可用按位或的方式组合使用), 新文件的所属用户和所属组是创建它的进程的所属用户和所属组。

③O_EXCL: 如果O_CREAT 标志已经使用, 则当pathname 参数指定的文件已经存在时open 函数返回失败。 如果pathname 给出的是一个符号链接, 无论它指向的文件是否存在, 对open 函数的调用都会返回失败。

④O_TRUNC: 如果文件存在并以可写方式打开, 则设置文件长度为0, 清空文件。

⑤O_APPEND: 写入的内容追加在文件末尾, 即打开后文件的读写位置被置于文件尾。

⑥O_NONBLOCK: 被打开的文件将以非阻塞的方式进行操作, 这关系到多个进程同时操作文件时的同步问题。

⑦O_NDELAY: 同O_NONBLOCK。

⑧O_SYNC: 被打开的文件将以同步I/O 的方式进行操作, 即任何写操作都会先被同步到硬件设备上。 同步完成后, 对写函数的调用才返回。

⑨O_NOFOLLOW: 如果pathname 是一个符号链接, 则对open 函数的调用将返回失败。

⑩O_DIRECTORY: 如果pathname 不是目录, 则对open 函数的调用将返回失败。

(3)mode: 用于指定新文件的权限标志位。 参数意义如下。

如果函数调用操作成功返回文件描述符, 否则返回-1, 并设置标量errno 的值标志错误情况。 文件描述符是一个取值从0 开始的整数。 内核默认一个进程同时打开的文件数有一个上限, 文件描述符取值的上限一般是1024。 每个进程在启动后就默认有三个打开的文件描述符, 如果启动程序时没有进行重定向, 则文件描述符0 关联到标准输入, 1 关联到标准输出, 2 关联到标准错误输出。 每新打开一个文件, 所获得的fd 为当前最大fd 加1。

鉴于在调用open 函数时, O_WRONLY、 O_CREAT、 O_TRUNC 三个标志位经常组合使用, 因此由一个专门的函数creat 来实现等价功能, 如:

2. 读文件

文件打开后就可以进行读写操作了。 读操作的接口头文件及函数原型如下:

参数说明如下。

(1)fd: 要读取的文件描述符。

(2)buf: 指向读取到的数据要放入的缓冲区。 (buf 指向的内存空间必须事先分配好。)

(3)count: 要读取缓冲区的字节数。

函数将从fd 代表的文件的当前读写位置读取不超过count 个字节到buf 指向的内存中,调用成功将返回读取到的字节数, 失败则返回-1, 并设置标量errno 的值。 如果文件本身提供的字节数就比count 小或者该调用被信号打断, read 函数的返回值可能会小于count 指定的值。

但是, 实际上文件读取数据的过程并不像写得这么简单。 由于系统中并发执行的多个进程都有可能对文件做操作, 往往由于各种情况, 从文件中读取的数据可能读不动, 可能读到的量为0, 可能小于指定量, 可能等于指定量。 实际上读不到数的情况是重点要分析的。

比如: 打开文件时如果不设置O_NONBLOCK, 默认要照顾与当前读/写文件操作配合的写/读的另一方, 如果在对方没写入数据前就读, 读操作的执行将阻塞, 也就出现读不动的情况。 而当数据写入后, 系统底层会有触发机制, 唤醒读端运作重新读。(www.daowen.com)

而如果设置O_NONBLOCK, 意味着无论情况如何, 读写操作都不阻塞, 文件操作不再是交给系统进行底层的同步管理, 而是调用会立即返回结果, 将主动权交给用户, 由用户根据返回情况, 决定如何写代码处理不同情况, 如没有可读的数据时进程也不会阻塞,而是返回-1, 并且errno 变量被设置。 用户可以在代码中判断errno 标记的值, 根据不同取值情况, 给不同的处理代码。

(1)erron 为EINTR 表示在读入有效字节前收到一个信号, 这种情况可以重新进行读操作。

(2)errno 为EAGAIN 说明在非阻塞方式下, 读文件时并没有可读的数据, 这种情况下, 往往用户可以将程序转向其他功能代码执行, 过后再在合适的时机, 在程序的某处再尝试执行读调用。

errno 取值的其他情况对应了不同类型的错误发生, 必须根据具体情况进行处理。 此处不再赘述。

3. 写文件

write 函数原型为:

参数说明如下。

(1)fd: 要写入的文件的描述符。

(2)buf: 指向要写入的数据所存放的缓冲区。

(3)count: 要写入的字节数。

要写文件, 必须先以可写权限用open 系统调用打开一个文件, 获得所打开文件的fd。写文件操作执行时, 从fd 所代表的文件当前读写位置开始, 把buf 指向的内存中最多count 个字节写入文件。 写入成功则返回写入的字节数, 并更新文件的读写位置。 对fd 执行写调用, 返回值为实际写入的字节数, 调用失败则返回-1, 并设置变量errno 的值。

同样, 由于系统中并发执行的多个进程都有可能对文件做操作, 往往由于各种情况,向文件中写入数据也会出现各种不同的情况。

(1)调用返回值等于count, 说明数据全部写入成功。

(2)调用返回一个大于0 小于count 的值, 说明仅部分数据写入。 这可能是因为写入过程被信号打断, 或者底层的设备暂时没有足够的空间存放写入的数据。

(3)调用阻塞, 说明暂时不能写入数据, 这种情况下如果以非阻塞方式操作文件, 那么会立即返回错误。

(4)调用返回-1, errno 被置为EINTR, 表示收到一个信号, 应用程序可以再次进行写操作。

(5)调用返回-1, errno 被置为EAGAIN, 说明是在非阻塞方式下写文件但文件暂时不能写入数据。

(6)调用返回-1, errno 被置为EBADF, 表示给定的文件描述符非法, 或者文件不是以写方式打开。

(7)调用返回-1, errno 被置为EFAULT, 表示buf 是无效的指针

(8)调用返回-1, errno 被置为EFBIG, 表示写入的数据超过了最大文件尺寸, 或超过了允许的文件读写位置。

(9)调用返回-1, errno 被置为EPIPE, 说明写入时发生了数据通道断层的错误, 这种情况只在文件是管道或者是socket 的情况下发生。 在这种情况下, 进程还将收到一个SIGPIPE 信号, 信号的默认处理程序是使进程退出。

(10)调用返回-1, errno 被置为ENOSPC, 说明底层设备没有足够的空间。

对于设备文件、 管道或socket 通信, 读/写系统调用的同步处理很重要, 读写关系问题我们在后面进程通信部分再以管道通信为例体验。

4. 关闭文件

程序完成对文件的操作后, 要使用close 系统调用将文件关闭, 操作成功时返回0, 否则返回-1, 其接口头文件与函数原型如下:

【例5-4】 利用Write 系统调用写文件举例, testOpenW.c 源代码如下。

其编译执行结果如下。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈