目录
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】