apue学习第八天——文件和目录(第四章)

时间:2022-04-12 21:54:19

接下来几章,打算不追究函数细节,快速通过。

第四章,讲的主要是文件系统,着重讨论文件和目录的性质,当然啦,我们围绕着一个贯穿全局的stat structure,纵观一个文件的all attributes并详细说明。当然啦,还是那句话,不过细的追究具体函数,用到再查嘛!


好啦,要想知道一个file的信息结构,UNIX在#include <sys/stat.h>中提供了一个很好的system call — stat。stat成功返回int 0,否则返回-1,用法是这样的:

#include <sys/stat.h>  
#include <unistd.h>
#include <stdio.h>

int main() {
struct stat buf;
stat("/etc/hosts", &buf);
printf("/etc/hosts file size = %d\n", buf.st_size);
}
类似的fstat,fstatat,lstat不多说,用到再查。但代码中有个重中之重,就是struct stat,它的各个属性就是我们今天需要一个一个阐明的,来看定义:

struct stat {
mode_t st_mode; /* file type & mode (permissions) */
ino_t st_ino; /* i-node number (serial number) */
dev_t st_dev; /* device number (file system) */
dev_t st_rdev; /* device number for special files */
nlink_t st_nlink; /* number of links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
off_t st_size; /* size in bytes, for regular files */
struct timespec st_atim; /* time of last access */
struct timespec st_mtim; /* time of last modification */
struct timespec st_ctim; /* time of last file status change */
blksize_t st_blksize; /* best I/O block size */
blkcnt_t st_blocks; /* number of disk blocks allocated */
};
仔细看后面的解释,发现这个structure保存了一个文件所需要的几乎所有信息,nice,现在开始~


1.st_mode: file type & mode (permissions)
首先啊,这个文件类型包括很多:regular file普通文件、directory file目录文件、block special file(block当然是提供对设备带缓冲的访问)、character special file(character一个一个的当然是不带缓冲的访问)、FIFO(named pipe命名管道,进程间通信用的,以后会讲到的)、socket、symbolic link。乱七八槽的什么都有,还真符合那句话:everything is a file!


2.st_uid st_gid——real user ID, effective user ID 和 saved set-user-ID
第一个大头来啦,UNIX系统中这三个ID可谓开始的时候可谓让我头疼至极,当然,现已被攻破,来看:

书上说,real user ID: who we really are; effective user ID: used for file permission checks; saved set-user-ID: saved by exec functions。宏观上解释了这三个ID是用来干什么的,但具体用法我们来做进一步阐述。这里要注意,这些ID都是与进程相关的,和文件本身都没有关系;结构体stat中的st_uid和st_gid才和文件有关系。

首先做一个大环境假设:你的电脑是一个server,以superuser(也就是root啦,root ID=0)权限运行着。

好啦,接下来我们在几个假设的场景示例下看这三个ID是如何工作的:

(1)有一个用户叫blitz(或者叫进程blitz),他已经登录系统,他想修改自己的登录密码,来看看具体细节:

用户blitz希望修改自己的账户密码,比如通过passwd类似的进程来达到目的(先不管其它的,暂定这个effective user ID=500)。但是我们知道安全起见,password file只有superuser可以对它进行写操作,而blitz又不是superuser,这可怎么办呢?UNIX系统是这样解决的,在st_mode中有一位叫set-user-ID bit,它允许进程执行此文件时以该文件所有者的ID(也就是st_uid)执行此文件。所以,password file的st_uid是superuser,但它的中t_mode设置了set-user-ID bit,所以当blitz想要修改密码时,这个进程的effective ID被设置成了superuser,那么就获得了对该文件写的权限,修改完成后,它的effective ID又被还原回去了。

(3)可能有人看了第一个场景示例会问,系统怎么知道blitz要修改哪个账户呢?

这就需要real user ID了,登录的时候,blitz输入了自己的用户名和密码,那么此时real user ID就是blitz(我们暂且用500来表示这个ID),那么修改密码的时候只要简单验证一下,只提供uid=500账户的修改权限就可以了。因为修改密码的进程是由blitz启动的,这就是为什么说通常情况下real user ID和effective user ID相等了。

(3)用户blitz想要打开server上的一个文件:

这就涉及到了kernel的file access permission test(文件访问权限测试),它用来测试一个进程是不是有权限打开或者删除一个文件。首先,blitz进程是一个普通用户(ID非0),如果是superuser的话当然可以直接访问,不是的话只好继续看它的effective ID是否等于文件的st_uid;幸运的话,blitz进程的effective ID 正好和文件的st_uid相等,那么就继续看具体的访问权限了:是读、写还是执行?(这三种访问权限分别由<sys/stat.h>中的access permission bits S_IRUSR, S_IWUSR, S_IXUSR控制,对于group和other还各有三个,一共9个,用法和user的相同)。这样,blitz的访问测试就完成了。

还有几个函数的功能说一下:

对一个给定文件,如果一个进程只是想验证一下有没有权限访问它,access来解决;

创建文件时想设置文件权限,调用umask(它返回previous file mode creation mask),是少数几个没有出错返回的函数之一;

chmod可以改变一个文件的访问权限(只有effective ID=st_uid或者用户是superuser时候可以改变);

我在开始的时候,chown和chmod搞不清,现在很清楚了,chown改变的文件的st_uid或st_gid,而chmod改变的是st_mode中的访问权限嘛!

OK,上面说的就是关于用户ID和用户访问的问题了,设计得很精妙吧!


3.sticky bit

看一个比较有意思的东西,粘着位:

首先,因为虚拟存储系统和快速文件系统的存在,sticky bit已经不用了,但SUS允许针对目录设置sticky bit。stick bit是早期的时候,由于文件的各数据块是散乱存放的,而交换区是连续的,所以可以通过设置sticky bit来把程序的正文部分的副本保存在交换区以更快的载入内存。时过境迁呀!


4.st_size

文件长度,通常是一个数的整数倍,如16或者512,普通文件的长度可以是0.

du命令(disk usage)可以查看一个文件占用的磁盘空间。

关于文件截断,打开文件时使用O_TRUNC,如果截断为length,那么length之后的位将不可访问。当然截断为0的话意味着清空文件啦。


5.st_atime, st_mtime, st_ctime

这三个代表着access,modification,change的时间。SUS 2008版将stat中时间的精度提高到nanosecond(纳秒)。

下面几个函数可以更改一个文件的访问时间和修改时间:futimens, utimensat, utimes(这里u是update的意思,其他自己拆开看就知道了),采用70年以来的秒数,不足一秒用纳秒表示,具体的就不说啦。


晚上十一点啦,今天的就到这里,明天说一下文件系统,那么第四章也就结束啦!