目录
一、前言
二、低级文件访问
1、write
2、read
3、open
4、Initial Permissions
(1)umask
(2)close
(3)ioctl
(4)第一个 copy_system.c 程序
(5)第二个 copy_block.c 程序
5、Other System Calls for Managing Files
(1)lseek
(2)fstat, stat, and lstat
(3)dup and dup2
一、前言
本文将简单介绍Linux文件和目录,以及如何操作它们(如何创建文件、打开、读、写和关闭,程序如何操作目录,如创建、扫描和删除目录等)。本文大部分内容将会介绍处理文件和目录的各种调用,涵盖了与文件相关的部分主题:(1)文件和设备;(2)系统调用;(3)库函数;(4)低级文件访问;(5)管理文件;(6)标准I/O库;(7)格式化的输入和输出;(8)文件和目录维护;(9)扫描目录;(10)错误;(11)/proc文件系统;(12)fcntl 和 mmap。这些内容本博客不会全部介绍,只是进行部分的基础介绍。
二、低级文件访问
每个正在运行的程序称为进程,它有许多与之相关联的文件描述符。当一个程序启动时,它通常已经打开了三个这样的描述符,可以使用它们访问打开的文件或设备。
0: Standard input
1: Standard output
2: Standard error
自动打开的文件描述符已经能够让我使用 write 创建一些简单的程序。
1、write
write 系统调用安排将 buf 中的第一个 nbytes 字节写入与文件描述符文件相关联的文件。
它返回实际写入的字节数。
如果文件描述符中有错误,或者如果底层设备驱动程序对块大小非常敏感,则此值可能小于 nbytes。
如果函数返回 0,则表示没有写入数据;
如果它返回 -1,则表示写调用中存在错误,并且错误将在 errno 全局变量中指定。
#include <unistd.h>
size_t write(int fildes, const void *buf, size_t nbytes);
写一个小demo,simple_write.c:
这个程序只是将一条消息打印到标准输出。
当程序退出时,所有打开的文件描述符都会自动关闭,因此不需要显式地关闭它们。但是,处理缓冲输出时,情况就不是这样了。
$ ./simple_write
Here is some data
$
值得再次注意的一点是,write 可能写入的字节比要求的少,这并不一定是一个错误。在程序中,可以去 check errno 来检测错误,并调用 write 来写入任何剩余的数据。
2、read
read 系统调用从与文件描述符文件相关联的文件中读取最多 nbytes 字节的数据,并将它们放在数据区 buf 中。
它返回实际读取的数据字节数,可能小于请求的数据字节数。
如果 read 调用返回 0,则它没有可读的内容,它到达了文件的末尾。
同样,调用上的错误将导致它返回 -1。
这个程序 simple_read.c 将标准输入的前 128 字节复制到标准输出。如果输入少于128字节,它将复制所有输入。
运行程序:
要有 “东西” 重定向到 ./simple_read 中。
3、open
要创建一个新的文件描述符,我们需要使用打开系统调用。
oflags 被指定为强制文件访问模式和其他可选模式的组合。open 调用必须指定下表中所示的文件访问模式之一:
当然 oflags 还包含其他 or 组合的可选形参,感兴趣的 uu 可以自行搜索了解~使用 man 2 查询手册页是个很好的查询方式
如果成功,open 返回新的文件描述符(总是一个非负整数),如果失败,则返回 -1,这时 open 还设置全局变量 errno,以指示失败的原因。
一个正在运行的程序一次可以打开的文件数量是有限的。这个限制通常由 limit .h 中的常量 OPEN_MAX 定义,它因系统而异,但 POSIX 要求它至少为 16 。这个限制本身可能受到本地系统范围的限制,因此程序可能不总是能够打开这么多文件。在 Linux 上,这个限制可能在运行时改变,因此 OPEN_MAX 不是一个常数,它通常从 256 开始。
4、Initial Permissions
(1)umask
umask 是一个系统变量,它为创建文件时使用的文件权限编码掩码。您可以通过执行 umask 命令来更改该变量,以提供一个新值。该值为三位八进制值。每个数字都是来自 1、2 或 4 的 ORing 值的结果,其含义如下表所示。分开的数字分别代表 “user”、“group” 和 “other” 权限。
例如,要阻止 “group” 的写和执行,以及 “other” 的写,umask 将是:
每个数字的值都是 or, 所以第二个数字应该是 2 | 1,得到3。故umask结果是032。
当通过 open 或 create 调用创建文件时,mode 参数将与当前的 umask 进行比较。在模式参数中同时在 umask 中设置的任何位都将被移除。最终的结果是,用户可以设置他们的环境说“不要为别人创建任何具有写权限的文件,即使创建文件的程序请求这种权限。” 这并不阻止程序或用户随后使用 chmod 命令(或程序中的 chmod 系统调用)添加其他写权限,但它确实有助于保护用户,使他们不必检查和设置所有新文件的权限。
(2)close
使用 close 终止文件描述符、fildes 及其文件之间的关联。文件描述符可以重用。如果成功则返回0,如果出错则返回 -1。
注意,检查 close 的返回结果可能很重要。一些文件系统,特别是网络文件系统,可能在文件关闭之前不会报告写入文件的错误,因为在执行写入操作时,数据可能没有被确认为已写入。
(3)ioctl
ioctl 是一些杂七杂八的东西。它为控制设备及其描述符的行为和配置底层服务提供了一个接口。终端、文件描述符、套接字,甚至磁带驱动器都可能有为它们定义的 ioctl 调用,用的时候需要参考特定设备的手册页了解详细信息。POSIX 仅为“流”定义 ioctl。语法如下:
ioctl 在描述符字段引用的对象上执行 CMD 指示的函数。它可以有第三个可选参数,这取决于特定设备支持的函数。
例如,在 Linux 上调用 ioctl 打开键盘 led:
现在我们已经了解了 open 、 read 和 write 系统调用,以便编写低级程序 copy_system.c,逐个字符地将一个文件复制到另一个文件。
为了简单起见,我们假设输入文件存在,而输出文件不存在,并且所有的读写操作都成功。当然,在实际的程序中,我们会检查这些假设是否有效。
(4)第一个 copy_system.c 程序
1、首先,先创建一个测试输入文件,大小为1Mb,并将其命名为 file.in
2、然后编译 copy_system.c:
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
char c;
int in,out;
in=open("file.in",O_RDONLY);
out=open("file.out",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
while(read(in,&c,1)==1)
write(out,&c,1);
exit(0);
}
注意,#include <unistd.h> 行必须放在前面,因为它定义了有关 POSIX 遵从性的标志,这些标志可能会影响其他包含文件。
可以用上图的命令查看耗时和CPU占用情况。
(5)第二个 copy_block.c 程序
你可以通过复制更大的块来改善这个问题。看看这个修改过的程序 copy_block.c,它同样使用系统调用将文件复制为1K块:
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
char block[1024];
int in,out;
int nread;
in=open("file.in",O_RDONLY);
out=open("file.out",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
while((nread=read(in,block,sizeof(block)))>0)
write(out,block,nread);
exit(0);
}
5、Other System Calls for Managing Files
还有许多其他的系统调用操作这些低级文件描述符。这些允许程序控制文件的使用方式和返回状态信息。
(1)lseek
lseek 系统调用设置文件描述符 fildes 的读/写指针。也就是说,可以使用它来设置下次读或写将发生在文件中的什么位置。可以将指针设置为文件中的绝对位置或相对于当前位置或文件末尾的位置。
offset 参数用于指定位置,下面的参数指定如何使用偏移量。
lseek 返回文件指针所指向的文件开头的偏移量(以字节为单位),失败时返回 -1。off_t 类型用于seek 操作中的偏移量,是在 sys/types.h 中定义的一个依赖于实现的整数类型。
(2)fstat, stat, and lstat
fstat 系统调用返回与打开的文件描述符相关联的文件的状态信息。信息被写入结构 buf,其地址作为参数传递。
注意,sys/types.h 是可选的,但我们建议在使用系统调用时使用它,因为它们的一些定义对标准类型使用别名,这些标准类型可能有一天会更改。
相关函数 stat 和 lstat 返回指定文件的状态信息。它们产生相同的结果,除非文件是符号链接 lstat返回关于链接本身的信息,stat 返回链接所指向的文件的信息。
结构的成员 stat 在类 unix 系统中可能有所不同,但将包括下表中的成员:
在统计结构中返回的 st_mode 标志也有一些在头文件 sys/stat.h 中定义的相关宏。这些宏包括权限和文件类型标志的名称和一些掩码,以帮助测试特定类型和权限。
权限标志与前面描述的开放系统调用相同。文件类型标志包括:
其他模式标志包括:
解释 st_mode 标志的掩码包括:
定义了一些宏来帮助确定文件类型。它们只是将适当的掩码模式标志与适当的设备类型标志进行比较。这些包括:
例如,要测试一个文件是否不代表目录,是否具有所有者的执行权限集,但没有其他权限,可以使用以下测试:
(3)dup and dup2
dup 系统调用提供了一种复制文件描述符的方法,提供了访问同一文件的两个或多个不同的描述符。它们可能用于对文件中的不同位置进行读写。dup 系统调用复制文件描述符 fildes,返回一个新的描述符。dup2 系统调用通过指定用于复制的描述符,有效地将一个文件描述符复制到另一个文件描述符。
当使用通过管道通信的多个进程时,这些调用也很有用。在以后的学习中再深入地研究一下dup系统。
以上,Linus 文件处理(一)
祝好