APUE读书笔记之四——文件和目录

第四章 文件和目录

4.1,引言

第四章主要讲的是文件的属性,以及操作这些属性的函数。文件的属性由一个结构体stat定义,其主要结构是:

struct stat {
mode_t  st_mode; //文件类型与权限;
ino_t st_ino; //i节点序号;
dev_t st_dev; //文件系统的设备号;
dev_t st_rdev; //特殊文件(字符特殊文件和块特殊文件)实际设备的设备号
nlink_t st_nlink; //链接数(硬链接)
uid_t st_uid; //文件所有者ID
gid_t st_gid; //组所有者ID
off_t st_size; //文件长度(仅对普通文件、目录文件和符号链接有意义)
time_t st_atime; //文件数据的最后访问时间
time_t st_mtime; //文件数据的最后修改时间
time_t st_ctime; //i节点状态的最后更改时间
blksize_t st_blksize; //对文件I/O较适合的块大小
blkcnt_t st_blocks; //所分配的实际512字节块数量
}


4.2stat(),fstat()和lstat()函数

int stat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int filedes, struct * buf);
int lstat(const char * restrict pathname. struct stat * restrict buf);

使用这三个函数可以读取一个文件的属性,并将读取到的属性值保存在stat类型的buf结构体中。其中fstat通过文件描述符获取一个已经打开的文件的属性,当碰到符号链接时,lstat会返回符号链接的属性信息,而不是像stat一样追踪符号链接指向的文件。使用stat函数最多的可能是ls -l命令,可以获取一个文件的所有信息。


4.3 文件类型

UNIX文件类型有如下几种:

(1)普通文件,最常见的文件,文本数据还是二进制数据对UNIX内核而言并无区别;

(2)目录文件,包含了其他文件的名字和指向与这些文件有关信息的指针,对目录文件拥有读权限的进程可以读取该目录的内容,但只有内核可以直接写目录文件,其他进程必须使用专门的函数才可以更改目录内容。

(3)块特殊文件,这种文件类型提供对设备(例如磁盘)带缓冲的访问,每次访问以固定长度为单位进行。

(4)字符特殊文件,这种文件类型提供对设备不带缓冲的访问,每次访问长度可变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。

(5)FIFO,这种文件类型用于进程间通信,也称为命名管道(named pipe)。

(6)套接字(socket),用于进程间网络间或非网络通信。

(7)符号链接(symbolic link),这种文件类型指向另一个文件。

可以使用文件类型宏判断文件类型,参数是stat结果的st_mode成员:

S_ISREG() 普通文件

S_ISDIR() 目录文件

S_ISCHR() 字符特殊文件

S_ISBLK() 块特殊文件

S_ISFIFO() 管道或FIFO

S_ISLNK() 符号链接

S_ISSOCK() 套接字


4.4 设置用户ID和设置组ID

每个进程关联的用户ID和组ID

实际用户ID

进程实际是谁

实际组ID

 

有效用户ID

用于文件访问权限检查

有效组ID

 

附加组ID

 

保存的设置用户ID

exec函数保存

保存的设置组ID

 

实际用户ID标识我们究竟是谁,有效用户ID,有效组ID以及附加组绝对了我们的文件访问权限,当执行一个程序文件时,进程的有效用户ID通常就是实际用户ID;保存的设置用户ID和保存的设置组ID在执行一个程序时包含了有效用户ID和有效组ID的副本,可以在st_mode文件模式字中设置一个特殊标志,可以将执行此文件的进程的有效组ID设置为文件的组所有者ID。例如若文件所有者是超级用户,而且设置了该文件的设置用户ID位,然后一个进程执行时,则该进程具有超级用户特权。


4.5 文件访问权限

文件的9个访问权限位(按照用户--其他顺序):

st_mode屏蔽

意义

S_IRUSR

用户-

S_IWUSR

用户-

S_IXUSR

用户-执行

S_IRGRP

-

S_IWGRP

-

S_IXGRP

-执行

S_IROTH

其他-

S_IWOTH

其他-

S_IXOTH

其他-执行

当用名字打开任一类型的文件时,对该名字包含的每一个目录,包括它可能隐含的当前工作目录都应该具有执行权限,仅仅有读权限允许我们读目录,获得在该目录中所有文件名的列表,当一个目录是我们要访问文件的路径名的一部分时,对该目录的可执行权限可通过该目录。

读文件需要读权限,写文件需要写权限,open函数的O_TRUNC标志需要写权限,在一个目录中创建新文件,需要对该目录写权限和执行权限,删除一个现有的文件,必须对包含该文件的目录具有写权限和执行权限,对文件本身不需要读、写权限,如果使用6exec函数中任何一个执行一个文件,都必须对文件具有执行权限,而且文件必须是普通文件类型。

文件权限检测顺序是1,若文件的有效用户ID0(超级用户),则允许访问;2,若进程的有效用户ID等于文件的所有者ID(即该进程拥有该文件),那么按照适当的权限进行访问;3,若进程的有效组ID或进程的附加组ID之一等于文件的组ID,按照适当的权限进行访问;4,若其他用户适当的权限位被设置,按照权限访问。步骤2,3互斥。


4.6 新文件和目录的所有权

新文件的用户ID设置为进程的有效用户ID,新文件的组ID1,可以是进程的有效组ID,2,也可以是它所在目录的组ID


4.7 access()函数

int access(const char *pathname, int mode);

access()函数按照实际用户ID和实际组ID进行文件的访问权限测试。若成功则返回0,失败返回-1


4.8 umask()函数

mode_t umask(mode_t cmask);

umask()函数为进程设置文件模式创建屏蔽字,并放回以前的值。这是少数几个没有出错返回的函数之一。

umask文件访问权限位:

屏蔽位

意义

0400

用户读

0200

用户写

0100

用户执行

0040

组读

0020

组写

0010

组执行

0004

其他读

0002

其他写

0001

其他执行

设置了相应的权限位后,对应的权限就会被拒绝。


4.9 chmod()和fchmod()函数

int chmod(const char *pathname, mode_t mode);
int fchmod(int filedes, mode_t mode);

这两个函数可以修改现有文件的访问权限;chmod()函数在指定的文件上操作,fchmod()在已打开的文件上操作。为了改变一个文件的权限位,进程的有效用户ID必须等于文件的所有者ID

chmod()函数的mode常量:

mode

说明

S_ISUID

执行时设置用户ID

S_ISGID

执行时设置组ID

S_ISVTX

保存正文(粘住位)

S_IRWXU

用户(所有者)读、写和执行

S_IRUSR

用户(所有者)读

S_IWUSR

用户(所有者)写

S_IXUSR

用户(所有者)执行

S_IRWXG

组读、写和执行

S_IRGRP

组读

S_IWGRP

组写

S_IXGRP

组执行

S_IRWXO

其他读、写和执行

S_IROTH

其他读

S_IWOTH

其他写

S_IXOTH

其他执行

先调用stat()函数获得当前权限stat.st_mode,然后利用chmod()函数修改它。


4.11 chown()、fchown()和lchown()函数

int chown(const char *pathname, uid_t ower, gid_t group);
int fchown(int filedes, uid_t ower, gid_t group);
int lchown(const char *pathname, uid_t ower, gid_t group);

这几个chown函数可以更改文件的用户ID和组IDlchown函数更改符号链接本身的所有者,而不是该符号链接所指向的文件。


4.12 文件长度

stat结构成员st_size表示以字节为单位的文件长度,此字段只对普通文件,目录文件和符号链接有意义。对于普通文件,文件长度可为0,读取时将得到文件结束指示;目录文件是一个数(16512)的倍数;对于符号链接,文件长度是文件名中的实际字节数。


4.13 文件截短

int truncate(const char *pathname, off_t length);
int ftruncate(int filedes, off_t length);

将文件截短为length字节,而不是截短多少字节。若成功返回0,失败返回-1


4.14 文件系统

把一个磁盘分成一个或者多个分区,每个分区可以包含一个文件系统。磁盘,分区和文件系统的关系可以从下图看出:

 

文件系统是一个比较复杂的部分,一个文件系统包括自举块,超级块,一个若干柱面组。其中每个柱面组又由超级块副本,配置信息,i节点图,块位图,i节点,数据块组成,ext2文件系统的组成要比这个复杂一点。自举块,也称为引导块,总是作为文件系统的首块,不为文件系统所用,只是包含用来引导文件系统的信息,绝大多数文件系统都不使用它。超级块包含与文件系统有关的参数信息,包括i节点数组的容量,文件系统逻辑块的大小,以逻辑块来计算,文件系统的大小。i节点数组(i节点表)与文件或目录一一对应。数据块,文件系统的大部分空间都用来存放数据,以构成驻留在文件系统之上的文件和目录。

 

每一个柱面组,都由i节点数组和若干个目录块以及数据块组成。其中i节点是固定长度的记录项,它包含有关文件的大部分信息。i节点的标识采用i节点的顺序位置,以数字表示,即i节点号,命令ls -li第一列即是文件的i节点号。i节点维护的信息(stat结构大多数信息来自i节点维护的信息,除了文件名和i节点号来自于目录项)包括文件类型,文件所有者(用户ID),文件所有组,所有者、所有组和其他用户的访问权限,时间戳(文件的最后访问时间,文件的最后修改时间,文件状态的最后改变时间,但没有文件的创建时间),指向文件的硬链接数量,文件大小,文件实际分配的块数量,指向文件数据块的指针。

在目录块中保存着目录项,记录着i节点号与文件名的对应关系。一个新的目录块至少有三个目录项,包括命名自己的目录项、...。当更改一个文件的文件名时,如/usr/local/foo改为/usr/foo,只需在/usr目录块创建一个新的目录项,记录原有文件的节点号和文件名(/usr/foo),并删除/usr/local目录块原有的目录项。如果两个目录同属一个文件系统,则不需移动文件,只需执行上面的操作,这就是mv命令的通常操作方式。


4.15 linkunlinkremoverename函数

int link(const char *existentpath, const char *newpath);

若成功返回0,失败返回-1。此函数创建一个新目录项newpath,它引用现有的文件existentpath,若newpath已经存在,返回出错。创建目录项和增加硬链接计数应当是个原子操作。

unlink()函数删除目录项,将硬链接计数减一,只有当硬链接计数为0时,该文件的内容才可被删除(并且没有其他进程打开该文件)。如果unlink()的是符号链接文件,则删除该符号链接,不会删除由该链接所引用的文件。

rename()用于文件或目录更名。


4.16 符号链接

符号链接是一个指向文件的间接指针,而硬链接直接指向文件的i节点。符号链接可以跨文件系统,硬链接不可以。只有超级用户才可以创建指向目录的符号链接。而符号链接对指向的文件类型没有什么限制,任何用户都可以创建指向目录的符号链接。符号链接一般用于将一个文件或整个目录结构移到系统中的另一个位置。

系统调用面对符号链接文件时应该要注意的一个问题是当前系统调用会不会跟随符号链接所指向的文件,下表给出了各个函数对符号链接的处理,说明会不会跟随符号链接:

函数

不跟随符号链接

跟随符号链接

access

 

chdir

 

chmod

 

chown

•(取决于系统实现)

•(取决于系统实现)

creat

 

exec

 

lchown

 

link

 

lstat

 

open

 

opendir

 

pathconf

 

readlink

 

remove

 

rename

 

stat

 

truncate

 

unlink

 

 

4.17 symlinkreadlink函数

symlink函数创建一个符号链接。因为open函数会跟随符号链接,所以读取符号链接需要一个专门的函数,即readlink函数。


4.18 文件的时间

字段

说明

例子

ls(l)选项

st_atime

文件数据的最后访问时间

read

-u

st_mtime

文件数据的最后修改时间

write

默认

st_ctime

i节点状态的最后更改时间

chmod, chown

-c

文件数据的最后修改时间是指write等对文件数据块的修改时间,而i节点状态的最后更改时间是指chmod等对文件对应i节点修改的时间。


4.19 utime函数

一个文件的访问和修改时间(st_atime,st_mtime)可以使用utime函数修改。


4.20 mkdirrmdir函数

使用mkdir函数创建目录文件,使用rmdir函数删除目录文件。使用rmdir函数删除目录时,将会使目录的链接计数为0,当没有进程打开此目录时,此目录占用的空间会被释放,如果有进程打开此目录,那么rmdir函数会删除最后一个链接和...项,打开目录的进程在目录中不能执行其他操作。


4.21 读目录

对目录拥有读权限的任一用户都可以读取目录,但是只有内核才能写目录。一个目录的写权限位表示能否在此目录建立新文件或者删除文件(更改目录项),而不是能否写目录本身。


4.22 chdir,fchdirgetcwd函数

使用chdirfchdir函数可以更改当前工作目录(fchdir接受某目录的文件描述符来改变工作目录),getcwd函数可以返回当前工作目录。当前工作目录是进程搜索相对路径的起点,因为当前工作目录只是进程的一个属性,所以更改工作目录不会影响其他进程。

4.23 不得不吐槽一下:CSDN的编辑器真的很垃圾。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值