linux中文件属性
一. linux系统如何管理文件
1.1. 硬盘中的静态文件和inode
a. 文件平时都在存放在硬盘中的,硬盘中存储的文件以一种固定的形式存放的,我们叫静态文件。
b. 一块硬盘中可以分为两大区域:一个是硬盘内容管理表项,另一个是真正存储内容的区域。操作系统访问硬盘时是先去读取硬盘内容管理表,从中找到我们要访问的那个文件的扇区级别的信息,然后再用这个信息去查询真正存储内容的区域,最后得到我们要的文件。
c. 操作系统最初拿到的信息是文件名,最终得到的是文件内容。第一步就是去查询硬盘内容管理表,这个管理表中以文件为单位记录了各个文件的各种信息,每一个文件有一个信息列表(我们叫inode,i节点,其实质是一个结构体,这个结构体有很多元素,每个元素记录了这个文件的一些信息,其中就包括文件名、文件在硬盘上对应的扇区号、块号那些东西·····)
PS:硬盘管理的时候是以文件为单位的,每个文件一个inode,每个inode有一个数字编号,对应一个结构体,结构体中记录了各种信息。
PS:联系平时实践,大家格式化硬盘(U盘)时发现有:快速格式化和底层格式化。快速格式化非常快,格式化一个32GB的U盘只要1秒钟,普通格式化格式化速度慢。这两个的差异?其实快速格式化就是只删除了U盘中的硬盘内容管理表(其实就是inode),真正存储的内容没有动。这种格式化的内容是有可能被找回的。
1.2. 内存中被打开的文件和vnode
1.2.1. 一个程序的运行就是一个进程,我们在程序中打开的文件就属于某个进程。每个进程都有一个数据结构用来记录这个进程的所有信息(叫进程信息表),表中有一个指针会指向一个文件管理表,文件管理表中记录了当前进程打开的所有文件及其相关信息。文件管理表中用来索引各个打开的文件的index就是文件描述符fd,我们最终找到的就是一个已经被打开的文件的管理结构体vnode
1.2.2. 一个vnode中就记录了一个被打开的文件的各种信息,而且我们只要知道这个文件的fd,就可以很容易的找到这个文件的vnode进而对这个文件进行各种操作。
二. linux中各种文件类型
2.1. 普通文件(- regular file)
2.1.1. 文本文件。文件中的内容是由文本构成的。常见的.c文件, .h文件 .txt文件等都是文本文件。
2.1.2. 二进制文件。二进制文件中存储的本质上也是数字,常见的可执行程序文件(gcc编译生成的a.out,arm-linux-gcc编译连接生成的.bin)都是二进制文件。
2.2. 目录文件(d directory)
2.2.1. 目录就是文件夹,文件夹在linux中也是一种文件,不过是特殊文件。用vi打开一个文件夹就能看到,文件夹其实也是一种特殊文件,里面存的内容包括这个文件的路径,还有文件夹里面的文件列表。
2.2.2. 但是文件夹这种文件比较特殊,本身并不适合用普通的方式来读写。linux中是使用特殊的一些API来专门读写文件夹的。
2.3. 字符设备文件(c character)
2.4. 块设备文件(b block)
2.4.1. 设备文件对应的是硬件设备,也就是说这个文件虽然在文件系统中存在,但是并不是真正存在于硬盘上的一个文件,而是文件系统虚拟制造出来的(叫虚拟文件系统,如/dev /sys /proc等)
2.4.2. 虚拟文件系统中的文件大多数不能或者说不用直接读写的,而是用一些特殊的API产生或者使用的,具体在驱动阶段会详解。
2.5. 管道文件(p pipe)
2.6. 套接字文件(s socket)
2.7. 符号链接文件(l link)
三. 常用文件属性获取
3.1. stat、fstat、lstat函数简介
root@ubuntu:/mnt/hgfs/windows_share/baseC/APPNet# stat test.txt File: ‘test.txt’ Size: 0 Blocks: 0 IO Block: 1024 regular empty file Device: 1ah/26d Inode: 116117 Links: 1 Access: (0777/-rwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2018-11-15 06:14:47.795103500 -0800 Modify: 2018-11-15 06:14:47.795103500 -0800 Change: 2018-11-15 06:14:47.795103500 -0800 Birth: - root@ubuntu:/mnt/hgfs/windows_share/baseC/APPNet#
3.1.1. linux命令行下还可以去用stat命令去查看文件属性信息,实际上stat命令内部就是使用stat系统调用来实现的。
3.1.2. fstat和stat的区别是:stat是从文件名出发得到文件属性信息结构体,而fstat是从一个已经打开的文件fd出发得到一个文件的属性信息。所以用的时候如果文件没有打开(我们并不想打开文件操作而只是希望得到文件属性)那就用stat,如果文件已经被打开了然后要属性那就用fstat效率会更高(stat是从磁盘去读取文件的,而fstat是从内存读取动态文件的)。
3.1.3. lstat和stat/fstat的差别在于:对于符号链接文件,stat和fstat查阅的是符号链接文件指向的文件的属性,而lstat查阅的是符号链接文件本身的属性。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #define NAME "1.txt" int main(void) { int ret = -1; struct stat buf; memset(&buf, 0, sizeof(buf)); // memset后buf中全是0 ret = stat(NAME, &buf); // stat后buf中有内容了 if (ret < 0) { perror("stat"); exit(-1); } // 成功获取了stat结构体,从中可以得到各种属性信息了 printf("inode = %d.\n", buf.st_ino); printf("size = %d bytes.\n", buf.st_size); printf("st_blksize = %d.\n", buf.st_blksize); return 0; }
3.2. 文件权限
root@ubuntu:/mnt/hgfs/windows_share/baseC/APPNet# ls -l total 137 -rwxrwxrwx 1 root root 7673 Nov 18 07:59 advancedIO -rwxrwxrwx 1 root root 2187 Nov 18 07:59 advancedIO.c
3.2.1. ls -l打印出的权限列表
3.2.1.1. 第一位为文件类型。剩余9位为文件权限,3个一组。第一组三个表示文件的属主(owner、user)对该文件的可读、可写、可执行权限;第2组3个位表示文件的属主所在的组(group)对该文件的权限;第3组3个位表示其他用户(others)对该文件的权限。
3.2.1. 文件权限管理
3.2.1.1. access函数检查权限设置
a. access函数可以测试得到当前执行程序的那个用户在当前那个环境下对目标文件是否具有某种操作权限。
#include <stdio.h> #include <unistd.h> #define NAME "3.txt" int main(void) { int ret = -1; ret = access(NAME, F_OK); if (ret < 0) { printf("文件不存在 \n"); return -1; } else { printf("文件存在 "); } ret = access(NAME, R_OK); if (ret < 0) { printf("不可读 "); } else { printf("可读 "); } ret = access(NAME, W_OK); if (ret < 0) { printf("不可写 "); } else { printf("可写 "); } ret = access(NAME, X_OK); if (ret < 0) { printf("不可执行 \n"); } else { printf("可执行 \n"); } return 0; }
3.2.3. chmod/fchmod与权限修改
a. chmod是一个linux命令,用来修改文件的各种权限属性。chmod命令只有root用户才有权利去执行修改。
root@ubuntu:/home/yaofeng# touch tmp.txt root@ubuntu:/home/yaofeng# chmod 777 tmp.txt root@ubuntu:/home/yaofeng# chmod a-r tmp.txt root@ubuntu:/home/yaofeng# chmod u-w tmp.txt root@ubuntu:/home/yaofeng# chmod g-x tmp.txt root@ubuntu:/home/yaofeng# chmod o+r tmp.txt
b. chmod/fchmod其实内部是用linux的一个叫chmod的API实现的。
#include <sys/stat.h> int chmod(const char *path, mode_t mode); int fchmod(int fd, mode_t mode);
#include <stdio.h> #include <sys/stat.h> int main(int argc, char **argv) { int ret = -1; if (argc != 2) { printf("usage: %s filename\n", argv[0]); return -1; } ret = chmod(argv[1], S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWOTH); if (ret < 0) { perror("chmod"); return -1; } return 0; }
3.2.4. umask与文件权限掩码
a. 文件掩码是linux系统中维护的一个全局设置,umask的作用是用来设定我们系统中新创建的文件的默认权限的。
root@ubuntu:/home/yaofeng# umask 0022 root@ubuntu:/home/yaofeng#
3.3. 文件属主修改
3.3.1. chown/fchown/lchown
a. linux中有个chown命令来修改文件属主
b. chown/fchown/lchown之间的差别和上述3.1相似。
3.3.2. chgrp
a. 转变文件所属用户组
3.4. 读取目录文件
a. opendir打开一个目录后得到一个DIR类型的指针给readdir使用
b. readdir函数调用一次就会返回一个struct dirent类型的指针,这个指针指向一个结构体变量,这个结构体变量里面记录了一个目录项(所谓目录项就是目录中的一个子文件)。
c. readdir调用一次只能读出一个目录项,要想读出目录中所有的目录项必须多次调用readdir函数。readdir函数内部户记住哪个目录项已经被读过了哪个还没读,所以多次调用后不会重复返回已经返回过的目录项。当readdir函数返回NULL时就表示目录中所有的目录项已经读完了。
PS: readdir函数和我们前面接触的一些函数是不同的,首先readdir函数直接返回了一个结构体变量指针,因为readdir内部申请了内存并且给我们返回了地址。多次调用readdir其实readir内部并不会重复申请内存而是使用第一次调用readdir时分配的那个内存。这个设计方法是readdir不可重入的关键。库函数中有一些函数当年刚开始提供时都是不可重入的,后来意识到这种方式不安全,所以重新封装了C库,提供了对应的可重复版本(一般是不可重入版本函数名_r)
#include <stdio.h> #include <sys/types.h> #include <dirent.h> int main(int argc, char **argv) { DIR *pDir = NULL; struct dirent * pEnt = NULL; unsigned int cnt = 0; if (argc != 2) { printf("usage: %s dirname\n", argv[0]); return -1; } pDir = opendir(argv[1]); if (NULL == pDir) { perror("opendir"); return -1; } while (1) { pEnt = readdir(pDir); if(pEnt != NULL) { // 还有子文件,在此处理子文件 printf("name:[%s] ,", pEnt->d_name); cnt++; if (pEnt->d_type == DT_REG) { printf("是普通文件\n"); } else { printf("不是普通文件\n"); } } else { break; } }; printf("总文件数为:%d\n", cnt); return 0; }