Initial Permissions
当你使用O_CREAT标志和open来创建文件时,必须使用open调用的三种模式参数格式。第三个参数mode是几个标志按位OR后得到的,这些标志在头文件sys/stat.h.中定义,它们是:
S_IRUSR | 读权限,文件属主 |
S_IWUSR | 写权限,文件属主 |
S_IXUSR | 执行权限,文件属主 |
S_IRGRP | 读权限,文件所属组 |
S_IWGRP | 写权限,文件所属组 |
S_IXGRP | 执行权限,文件所属组 |
S_IROTH | 读权限,其他用户 |
S_IWORH | 写权限,其他用户 |
S_IXORH | 执行权限,其他用户 |
创建了一个名叫myfile的文件,同时对文件文件属主拥有读的权限,其他用户则是执行的权限,并且只有这些权限而已。
$ ls -ls myfile0 -r-------x 1 neil software 0 Sep 22 08:11 myfile*
有两个因素在影响着文件权限。首先,只有在文件在创建时才能指定文件访问权限。其次,用户掩码(由shell命令umask指定)会影响被创建文件的权限。open调用里给出的模式值会与当时的用户掩码的反值做AND操作。举例说明,假设用户掩码被设置为001,并且指定了S_IXOTH模式标志,那么其他用户对创建的文件不会拥有执行权限,因为用户掩码中指定了不允许向其他用户提供执行权限。因此,open和creat调用中的标志实际上是设置文件访问权限的请求,所请求的权限是否会被设置取决于当时umask的值。
umask
umask是一个系统变量,它的作用是:当文件被创建后为文件访问权限设置一个掩码。当我们登录系统之后创建一个文件会有一个默认权限,这个权限是如何来的呢?这就是umask造成的结果。我们可以通过执行umask命令可以对这个变量进行修改,为其提供一个新值。umask设置了用户创建文件的默认权限,它与chmod的效果刚好相反,umask设置的是权限“补码”,而chmod设置的是文件权限码。一般在/etc/profile、$ [HOME]/.bash_profile或$[HOME]/.profile中设置umask值。
umask命令允许你设定文件创建时的缺省模式,对应每一类用户(文件属主、同组用户、其他用户)存在一个相应的umask值中的数字。对于文件来说,这一数字的最大值分别是6。系统不允许你在创建一个文本文件时就赋予它执行权限,必须在创建后用chmod命令增加这一权限。目录则允许设置执行权限,这样针对目录来说,umask中各个数字最大可以到7。
该命令的一般形式为:
umask nnn
其中nnn为umask置000 - 777。
我们只要记住u m a s k是从权限中“拿走”相应的位即可。下表是umask值与权限的对照表:
umask | 文件 | 目录 |
0 | 6 | 7 |
1 | 6 | 6 |
2 | 4 | 5 |
3 | 4 | 4 |
4 | 2 | 3 |
5 | 2 | 2 |
6 | 0 | 1 |
7 | 0 | 0 |
如:umask值为022,则默认目录权限为755,默认文件权限为644。
当我们通过open或creat调用创建文件时,mode参数将与umask进行比较。在mode参数中被设置的位如果在umask中也被设置了,就会从文件的访问权限中删除。因此,最终用户可以设置自己的环境,比如“不准创建允许其他用户有写权限的文件,即使创建该文件的程序要求该权限也不行。”这样做并不能阻止一个程序或用户在今后使用chmod命令(或者在程序中使用chmod系统调用)来添加其他用户的写权限,但它确实能够帮助用户,使他们不必对每个新文件都去检查和设置其访问权限。
close
我们使用close来终止文件描述符fildes同它所对应的文件之间的关联,这样文件描述符就被释放,从而可以被重新使用。close执行成功返回0,出错就返回-1。
close系统调用的语法结构:
int close(int fildes);
注意:查看close返回的结果十分重要。有些文件系统,尤其是网络文件系统,可能不会在关闭文件之前报告文件写操作中出现的错误,因为在执行写操作时,数据可能并未确认被写入。
ioctl
ioctl有点像个装东西的袋子。它提供了一个接口,用于控制设备及其文件描述符和配置底层服务。终端、文件描述符、sockets、甚至磁带设备都可以为它们定义ioctl调用,具体细节请参考特定设备的使用手册。POSIX只为流(stream)指定了ioctl,在此不做相关讨论。
ioctl系统调用的语法结构:
int ioctl(int fildes, int cmd, ...);
ioctl对描述符fildes指定的对象执行cmd参数中给出的操作。根据特定设备所支持函数的不同,还有可能会有一个可选的第三参数。
举例说明,下面所示的ioctl调用在Linux上的功能是打开键盘上LED灯:
实例:
(一个文件复制程序)我们已经了解了open、read以及write等系统调用的相关内容,下面让我们来写一个简单的程序copy_system.c,逐个字符地复制一个文件到另外一个文件中。
为简单起见,我们假定输入文件已经存在,输出文件不存在,且用户所有的读写操作都成功。当然,在现实程序中,我们要对此进行确认其有效性。
1.首先,我们创建测试的输入文件file.in。
2.接下来,书写程序copy_system.c的代码:
#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);
}
3.像下面所给出的那样运行程序:
$ TIMEFORMAT="" time ./copy_system4.67user 146.90system 2:32.57elapsed 99%CPU
...
$ ls -ls file.in file.out
1029 -rw-r---r- 1 neil users 1048576 Sep 17 10:46 file.in
1029 -rw------- 1 neil users 1048576 Sep 17 10:46 file.out
工作原理:
我们在此使用time工具对该程序运行的时间进行了测算。在Linux上使用TIMEFORMAT变量来重置默认的POSIX时间输出格式,POSIX时间格式不包括CPU使用率。我们可以看到在这台相当老的系统上,1MB的输入文件file.in被成功拷贝到file.out,file.out以只允许属主进行读写的权限被创建出来。但这次拷贝花费了大约两分半钟,并且几乎消耗了所有的CPU时间。之所以这么慢,是因为它必须完成超过两百万次的系统调用。
(另一个文件复制程序)
我们通过拷贝大一些的数据块来改善效率较低的问题,请看下面这个改进后的程序copy_block.c,它每次拷贝长度为IK的数据块,用的还是系统调用:
#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_WRDONLY|O_CREAT, S_IRUSR|S_IWUSR);
while((nread = read(in, block, sizeof(block))) > 0)
write(out, block, nread);
exit(0);
}
现在运行程序:
$ TIMEFORMAT="" time ./copy_system0.00user 0.02system 0:00.04elapsed 78%CPU
...
工作原理:
改进后的程序只花费了0.01秒的时间,因为它只需做大约2000次系统调用。当然,这些时间与系统本身的性能有很大的关系,但它们确实显示了系统调用需要巨大的开支,因此值得对其使用进行优化。