一、Linux 中各种文件类型
1、普通文件(- regular file)
- (1)文本文件:内容由 ASCII 码字符构成,本质上是编码数字,即 ASCII 码字符对应的数字。 常见的有.c 文件、.h 文件、.txt 文件。其优点是可以被人轻易读懂和编写。
- (2)二进制文件:内容由二进制数字构成,但不是编码数字,而是真正的数字,当然本质 上也对一套应了编码数字。常见的有可执行文件(gcc 编译生成的 a.out,arm-linux-gcc 编译连接生成的.bin)。
- (3)使用文本编辑器打开二进制文件会得到乱码,使用二进制阅读工具去打开文件,文件会得到文本文字对应的二进制编码。
2、目录文件(d directory)
- (1)目录文件就是文件夹,文件夹在 linux 中也是一种文件,不过是特殊文件。用 vi 打开 一个文件夹就能看到,里面存的内容包括这个文件夹的路径,还有文件夹里面的文件 列表。
- (2)文件夹本身不适合用普通的方式来读写,Linux 中是使用一些特殊的 API 来读写文件夹 的。
3、管道文件(p pipe)
4、符号链接文件(l link)
5、字符设备文件(c character)
- 字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台、开关量设备etc。
6、块设备文件(b block)
- (1)块设备是指可以从设备的任意位置读取一定长度数据的设备,常见的有硬盘、磁盘、 U 盘、SD 卡。
- 块设备文件对应的硬件设备,也就是说这个文件虽然在文件系统中存在,但它并不是真正存储在硬盘的文件,而是由文件系统虚拟出来的(VFS),比如/dev,/sys,/proc等
- 虚拟文件系统中的大多数文件不能或者说不用直接读写,而是用一些特殊的API产生或者使用的,具体会在驱动学习阶段介绍。
7、套接字文件(s socket)
- 网络设备是特殊设备的驱动,它负责接收和发送数据帧,可能是物理帧,也可能是ip数据包,这些特性都有由网络驱动决定。
二、常用文件属性获取
1、stat、fstat、lstat函数简介
- (1)每个文件都附带了这个文件的一些属性,但只能被专用的 API 获取。
- (2)Linux 命令行下可以用 stat 去查看文件属性信息。
- (3)fstat (fd)和 stat(filename) 的区别是:stat 是从文件名出发得到文件属性信息结构体,而 fstat 是从一 个已经打开的文件 fd 出发得到该文件的属性信息。
- (4)lstat 和 stat/fstat 的区别是:对于符号链接文件,stat 和 fstat 查阅的是符号链接文件指向的文件的属性,而 lstat 查阅的是符号链接文件本身的属性。
int stat(const char* pathname,struct stat* statbuf); int fstat(int fd,struct stat* statbuf); int lstat(const char* pathname,struct stat* statbuf);
2、struct stat 结构体简介
- (1)struct stat 是内核定义的一个结构体,在<sys/stat.h>中声明,这个结构体中所有元素加起来就是文件属性信息。
#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=%ld.\n",buf.st_ino); pritnf("size=%ld bytes.\n",buf.st_size); printf("st_blksize=%ld.\n",buf.st_blksize); return 0; }
stat函数应用示例
1、用代码判断文件类型
- (1)文件类型:即-,d,l 等。
- (2)文件属性中的文件类型在 struct stat 结构体的 mode_t st_mode 元素中,这个元素是按位来定义的位标志,判断时按位与即可,但是由于这些位不容易记住,因此 Linux 系统事先定义了很多宏来进行相应操作。
- (3)譬如 S_ISREG 宏返回值是 1 表示这个文件是一个普通文件,如果返回 0 则表示不是普通文件。
2、用代码判断文件权限设置
- (1)st_mode 中除了记录文件类型之外,还记录了一个重要信息:文件权限。
- (2)Linux 并没有给文件权限测试提供宏操作,只是提供了位掩码,所以我们只能用位掩码 来判断是否具有相应的权限。
#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)); ret = stat(NAME, &buf); if (ret < 0) { perror("stat"); exit(-1); } #if 0// 判断这个文件属性 //int result = S_ISREG(buf.st_mode);//是不是普通文件 int result = S_ISDIR(buf.st_mode);//是不是目录文件 printf("result = %d\n", result); #endif //文件权限测试 //unsigned int result = (buf.st_mode & S_IRWXU) >> 8; unsigned int result = ((buf.st_mode & S_IRUSR)? 1: 0); return 0; }
四、文件权限管理
1、st_mode中记录的文件权限位
- (1)st_mode 本质上是一个32位的数(类型就是 unsigned int),每一位表示一个含义。
- (2)文件类型和文件权限都记录在st_mode 中,我们用的时候使用专门的掩码去取出相应的位即可得知相应的信息。
2、ls -l打印出的权限列表
- (1)权限列表共有 9 个位,3 个一组,第一组表示属主的权限,第二组表示属主所在的组的权限,第三组表示其他用户的权限。
- (2)一般来说创建文件的用户就是文件的属主,但属主也可以通过 chown 命令来修改,还 可以通过 chgrp 命令去修改属主所在的组。
3、文件操作时权限检查规则
- (1)假设 a.out 去操作 1.txt,那么首先应该搞清楚 a.out 是被谁执行,也就是当前程序(进 程)是哪个用户的进程,然后再去查看 1.txt 的文件权限列表,判断是否具有相应权限。
4、access 函数检查权限设置
- (1)文本权限的管控极其复杂,所以在操作某个文件之前应该先判断是否有相应的权限, 没有则提示错误信息。
- (2)access 函数可以测试得到当前执行程序的那个用户在当前环境下对目标文件是否具有某种操作权限。
5、chmod/fchmod 与权限修改
- (1)chmod 是一个 Linux 命令,用来修改文件的各种权限属性,但只有 root 用户才有权利 去执行。
- (2)chmod 其实是调用内部的 chmod 的 API 实现的。
6、chown/fchown/lchown 与属主修改
- (1)Linux 中有个 chown 命令来修改文件属主,它也是调用 chown API 来实现的。
7、umask 与文件权限掩码
- (1)文件权限掩码是 Linux 中维护的一个全局设置,umask 的作用是更改系统中新创建的文件的默认权限。它同样是调用 umask API 来实现的。
五、读取目录文件
1、opendir 与 readdir 函数
- (1)opendir 打开一个目录后得到一个 DIR 类型的指针给 readdir 使用。
- (2)readdir 调用一次就会返回一个 struct dirent 类型的指针,这个指针指向以一个结构体 变量,该结构体变量记录了一个目录项(目录中一个子文件)。
- (3)readdir 调用一次只能读出一个目录项,想要读出目录中所有的目录项必须多次调用 readdir 函数,当 readdir 函数返回 NULL 时就表示目录中所有的目录项已经读完了。
2、不可重入函数介绍==important
- (1)readdir 函数和我们前面接触的一些函数是不同的,首先 readdir 函数直接返回了一个 结构体变量指针,因为 readdir 内部申请了内存并且给我们返回了地址。多次调用 readdir 并不会重复申请内存而是使用第一次调用时申请的那个内存。这个设计方法是 readdir 不可重入的关键
- (2)readdir 在多次调用时是有关联的,这个关联也表明 readir 是不可重入。
- (3)库函数中有一些函数一开始是可重入的,后来意识到这个方式不安全,于是设计了相 应的可重入版本(一般是不可重入函数名_r)。