目录
1文件存储
1.1 inode
1.2 dentry
2.文件系统
2.1 stat函数
2.2 lstat函数
2.3特殊权限位、黏住位
2.4 access函数
2.5 chmod函数
2.6 truncate函数
2.7 link函数
2.8 unlink函数、
2.9 隐式回收
2.10 symlink函数
2.11 readlink函数
2.12 rename函数
2.13 目录操作
2.14 getcwd函数
2.15 chdir函数
3.文件、目录权限
3.1 opendir函数
3.2 closedir函数
3.3 readdir函数
3.4 rewinddir函数
3.6 telldir/seekdir函数
3.7 递归遍历目录
3.8 重定向
1文件存储
首先了解如下文件存储相关概念:inode、 dentry、 数据存储、文件系统。
1.1 inode
其本质为结构体,存储文件的属性信息。如:权限、类型、大小、时间、用户、盘块位置……也叫作文件属性管理结构,大多数的inode都存储在磁盘上。少量常用、近期使用的inode会被缓存到内存中。
1.2 dentry
目录项,其本质依然是结构体,重要成员变量有两个 {文件名,inode,...},而文件内容(data)保存在磁盘盘块中。一个文件有两部分一部分是dentry 找到inode 号,之后再找到inode(结构体),进一步找到其在文件中的位置。给一个文件创建一个硬链接,他们有相同的inode 节点,有不同的目录项dentry.删除就是将连接数减1 ,实际就是 去掉一个dentry而已。当inode 变成0 ,原来指向会被覆盖而已。数据不能被擦除,只能被覆盖而已。只要恢复到原来的inode 即可。
数据的恢复。
2.文件系统
文件系统是,一组规则,规定对文件的存储及读取的一般方法。文件系统在磁盘格式化过程中指定。常见的文件系统有:fat32 ntfs exfat ext2 、ext3 、ext4.
2.1 stat函数
获取文件属性,(从inode结构体中获取)
int stat(const char *path, struct stat *buf); 成返回0;失败返回-1 设置errno为恰当值。
参数1:文件名
参数2:inode结构体指针 (传出参数)
文件属性将通过传出参数返回给调用者。
练习:使用stat函数查看文件属性
2.2 lstat函数
int lstat(const char *path, struct stat *buf); 成返回0;失败返回-1 设置errno为恰当值。
练习:给定文件名,判断文件类型。 【get_file_type.c】
文件类型判断方法:st_mode 取高4位。 但应使用宏函数:
S_ISREG(m) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
穿透符号链接:stat:会;lstat:不会.
2.3特殊权限位
包含三个二进制位。依次是:设置组ID位setGID;设置用户ID位setID;黏住位sticky。
黏住位
早起计算机内存紧,只有精要的常用的程序可以常驻物理内存,剩下的要暂存磁盘中。当内存不够用的时候会将该部分程序存回磁盘,腾出内存空间。若文件设置了黏住位,那么即使在内存比较吃紧的情况下,也不会将该文件回存到磁盘上。由于现阶段操作系统的虚拟内存管理分页算法完善。该功能已经被废弃。
但我们仍然可以对目录设置黏住位。被设置了该位的目录,其内部文件只有:
①超级管理员
②该目录所有者
③该文件的所有者
以上三种用户有权限做删除、修改操作。其他用户可以读、创建但不能随意删除。
setUID位
进程有两个ID:EID(有效用户ID),表示进程履行哪个用户的权限。
UID(实际用户ID),表示进程实际属于哪个用户。
多数情况下,EID和UID相同。但是,当文件的setID被设置后两个ID则有可能不一样。
例如:当进程执行一个root用户的文件,若该文件的setID位被设置为1, 那么执行该文件时,进程的UID不变。EID变为root,表示进程开始履行root用户权限。
setGID位于setID相类似。
2.4 access函数
测试指定文件是否存在/拥有某种权限。
int access(const char *pathname, int mode); 成功/具备该权限:0;失败/不具备 -1 设置errno为相应值。
参数2:R_OK、W_OK、X_OK
通常使用access函数来测试某个文件是否存在。F_OK。
2.5 chmod函数
修改文件的访问权限
int chmod(const char *path, mode_t mode); 成功:0;失败:-1设置errno为相应值int fchmod(int fd, mode_t mode);
2.6 truncate函数
截断文件长度成指定长度。常用来拓展文件大小,代替lseek。
int truncate(const char *path, off_t length); 成功:0;失败:-1设置errno为相应值
int ftruncate(int fd, off_t length);
2.7 link函数
思考,为什么目录项要游离于inode之外,画蛇添足般的将文件名单独存储呢??这样
的存储方式有什么样的好处呢?其目的是为了实现文件共享。Linux允许多个目录项共享一个inode,即共享盘块(data)。不同文件名,在人类眼中将它理解成两个文件,但是在内核眼里是同一个文件。link函数,可以为已经存在的文件创建目录项(硬链接)。int link(const char *oldpath, const char *newpath); 成功:0;失败:-1设置errno为相应值注意:由于两个参数可以使用“相对/绝对路径+文件名”的方式来指定,所以易出错。
如:link("../abc/a.c", "../ioc/b.c")若a.c,b.c都对, 但abc,ioc目录不存在也会失败。mv命令既是修改了目录项,而并不修改文件本身。
2.8 unlink函数、
删除一个文件的目录项;int unlink(const char *pathname); 成功:0;失败:-1设置errno为相应值
练习:编程实现mv命令的改名操作 【imp_mv.c】
注意Linux下删除文件的机制:不断将st_nlink -1,直至减到0为止。无目录项对应的文件,将会被操作系统择机释放。(具体时间由系统内部调度算法决定)因此,我们删除文件,从某种意义上说,只是让文件具备了被释放的条件。unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放。要等到所有打开该文件的进程关闭该文件,系统才会挑时间将该文件释放掉【unlink_exe.c】
2.9 隐式回收
当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放。系统的这一特性称之为隐式回收系统资源。
2.10 symlink函数
创建一个符号链接int symlink(const char *oldpath, const char *newpath); 成功:0;失败:-1设置errno为相应值。
2.11 readlink函数
读取符号链接文件本身内容,得到链接所指向的文件名。ssize_t readlink(const char *path, char *buf, size_t bufsiz); 成功:返回实际读到的字节数;失败:-1设置errno为相应值。
2.12 rename函数
重命名一个文件。int rename(const char *oldpath, const char *newpath); 成功:0;失败:-1设置errno为相应值。
2.13 目录操作
工作目录:“./”代表当前目录,指的是进程当前的工作目录,默认是进程所执行的程序所在的目录位置。
2.14 getcwd函数
获取进程当前工作目录 (卷3,标库函数)
char *getcwd(char *buf, size_t size); 成功:buf中保存当前进程工作目录位置。失败返回NULL。
2.15 chdir函数
改变当前进程的工作目录。int chdir(const char *path); 成功:0;失败:-1设置errno为相应值
【imp_cd.c】
练习:获取及修改当前进程的工作目录,并打印至屏幕。
3.文件、目录权限
注意:目录文件也是“文件”。其文件内容是该目录下所有子文件的目录项dentry。 可以尝试用vim打开一个目录。
r | w | x | |
文件 | 文件的内容可以被查看 | 内容可以被修改 | 可以运行产生一个进程 |
cat、more、less… | vi、> … | ./文件名 | |
目录 | 目录可以被浏览 | 创建、删除、修改文件 | 可以被打开、进入 |
ls、tree… | mv、touch、mkdir… | cd |
目录设置黏住位:若有w权限,创建不变,删除、修改只能由root、目录所有者、文件所有者操作。
3.1 opendir函数
根据传入的目录名打开一个目录 (库函数) DIR * 类似于 FILE *DIR *opendir(const char *name); 成功返回指向该目录结构体指针,失败返回NULL 参数支持相对路径、绝对路径两种方式:例如:打开当前目录:① getcwd() , opendir() ② opendir(".");
3.2 closedir函数
关闭打开的目录 int closedir(DIR *dirp); 成功:0;失败:-1设置errno为相应值
3.3 readdir函数
读取目录 (库函数)struct dirent *readdir(DIR *dirp); 成功返回目录项结构体指针;失败返回NULL设置errno为相应值需注意返回值,读取数据结束时也返回NULL值,所以应借助errno进一步加以区分。
struct 结构体:
struct dirent {
ino_t d_ino; inode编号
off_t d_off;
unsigned short d_reclen; 文件名有效长度
unsigned char d_type; 类型(vim打开看到的类似@*/等)
char d_name[256];文件名 };
其成员变量重点记忆两个:d_ino、d_name。实际应用中只使用到d_name。
练习1:实现简单的ls功能。 【imp_ls.c】
练习2:实现ls不打印隐藏文件。每5个文件换一个行显示。 【imp_ls2.c】
拓展1:实现ls -a -l 功能。
拓展2:统计目录及其子目录中的普通文件的个数
3.4 rewinddir函数
回卷目录读写位置至起始。
void rewinddir(DIR *dirp); 返回值:无。
3.6 telldir/seekdir函数
获取目录读写位置
long telldir(DIR *dirp); 成功:与dirp相关的目录当前读写位置。失败-1,设置errno修改目录读写位置
void seekdir(DIR *dirp, long loc); 返回值:无参数loc一般由telldir函数的返回值来决定。
3.7 递归遍历目录
查询指定目录,递归列出目录中文件,同时显示文件大小。 【ls_R.c】
3.8 重定向
dup 和 dup2函数
int dup(int oldfd); 成功:返回一个新文件描述符;失败:-1设置errno为相应值
int dup2(int oldfd, int newfd);
记忆方法两种:1. 文件描述符的本质角度理解记忆。2. 从函数原型及使用角度,反向记忆。练习:借助dup函数编写mycat程序,实现cat file1 > file2 命令相似功能。 【mycat.c】