以下内容源于朱有鹏嵌入式课程的学习整理与整理,如有侵权请告知删除。
前言
文件属性,包括文件的名字、创建时间、文件类型、文件权限等等内容。
本文讲述了以下内容:
(1)文件类型有哪些;
(2)文件权限管理的内容;
(3)利用stat函数获取某个文件的属性信息;
(4)操作目录文件。
一、文件类型
文件类型 | 符号标识 |
普通文件 (regular file) | - |
目录文件(directory) | d |
字符设备文件(character) | c |
块设备文件(block) | b |
管道文件(pipe) | p |
套接字文件(socket) | s |
符号链接文件(link) | l |
1、普通文件
1)普通文件包括:文本文件、二进制文件。
文本文件,是由ASCII码字符构成的文件。常见的.c文件、.h文件、.txt文件等都是文本文件,它可以被人轻松读懂和编写,是为人类而发明的。其实文本文件本质上还是由数字01构成的文件,不过编辑器读出这些数字后,按照编码格式转化成ASCII字符。
二进制文件,是有01数字构成的文件,不过01数字不是某些字符的编码,而是真正的数字。常见的可执行程序文件,比如由gcc编译生成的a.out、arm-linux-gcc编译连接生成的.bin文件,都是二进制文件。
2)如何区别二进制文件和文本文件?
从本质上来看,文本文件和二进制文件并没有任何区别,都是一个文件里面存放了数字。如果把这些数字就当作数字处理则就是二进制文件,如果把这些数字按照某种编码格式去解码成文本字符,则就是文本文件。
Linux系统不区分这两种文件,比如open、read、write等方法操作文本文件和二进制文件时,没有一点区分。为了明确文件类型,有时候人为地添加后缀来表征文件类型。
3)如何打开与编辑?
常见的文本文件编辑器如vim、gedit、notepad++、SourceInsight等;使用这些文本文件编辑器去打开文件的时候,编辑器读出文件二进制数字内容,然后按照编码格式去解码将其还原成文字。
如果用文本文件编辑器去打开一个二进制文件,编辑器以为这个二进制文件还是文本文件,然后试图去将其解码成文字,但是解码过程很多数字并不对应有意义的文字,所以成了乱码。
如果用二进制阅读工具去读取文本文件,得到的就是文本文字所对应的二进制的编码。
2、目录文件
目录文件即文件夹,文件夹在linux中也是一种文件,不过是特殊文件。
用vi打开一个文件夹,可知文件夹里的内容包括这个文件夹的路径、文件夹里面的文件列表。
linux中使用特殊的一些API读写文件夹。
3、块设备文件
设备文件对应的是硬件设备。
它不是真正存在于硬盘上的一个文件,而是由文件系统虚拟制造出来的。
虚拟文件系统中的文件,需要用一些特殊的API产生或者使用。
二、文件权限
文件权限,指不同用户的读写与执行权限。
1、“ls -l”命令
使用“ls -l”命令打印出来的信息,比如“- rwx rwx rwx”,其中“-”表示文件类型(这里表示普通文件);从左到右,第一组“rwx”表示文件属主对该文件的操作权限,第二组表示文件属主所在组队该文件的操作权限,第三组表示其他用户对该文件的操作权限。
2、access函数
函数原型
int access(const char *pathname, int mode);
函数作用
该函数用来检测当前用户对文件是否具有某种操作权限。
补充说明
mode的可取值分别为:R_OK、W_OK、X_OK、F_OK。它们分别用来测试是否可读、是否可写、是否可执行、文件是否存在。
3、chmod函数
只有root用户才能使用chmod命令,该命令实际调用的是Linux内部一个叫做chmod的API。
函数原型
int chmod(const char *path, mode_t mode);
函数作用
该函数用来修改文件的权限
补充说明
(1)chmod命令的细节:chmod命令:修改文件(夹)权限_天糊土的博客-CSDN博客
(2)mode的可选值及其含义
S_IRUSR (00400) read by owner
S_IWUSR (00200) write by owner
S_IXUSR (00100) execute/search by owner ("search" applies for directories, and means that entries within the directory can be accessed)
S_IRGRP (00040) read by group
S_IWGRP (00020) write by group
S_IXGRP (00010) execute/search by group
S_IROTH (00004) read by others
S_IWOTH (00002) write by others
S_IXOTH (00001) execute/search by others
代码示例
ret=chmod(argv[1] ,S_IRUSR | S_IWUSR | S_IXUSR)
三、获取文件属性
1、stat函数的介绍
每个文件都具有一些属性(比如文件类型、文件权限等等),这些属性存在于文件本身,只能被一些专用的API打开看到,比如stat、fstat、lstat,它们作用一样,参数不同,细节略有不同。这里重点简述stat这个API。
在命令行中,使用命令“man 1 stat”以及“man 2 stat”,都可以得到stat的使用手册。这说明stat是linux下的一个命令,同时也是linux下的API(或者说函数),或者说stat命令就是通过stat这个API来实现的。
xjh@ubuntu:~/iot/tmp$ stat sample.c File: ‘sample.c’Size: 169 Blocks: 8 IO Block: 4096 regular file Device: 801h/2049d Inode: 917861 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ xjh) Gid: ( 1000/ xjh) Access: 2022-06-17 23:47:02.105965691 +0800 Modify: 2022-06-17 23:29:15.993972099 +0800 Change: 2022-06-17 23:29:15.997972099 +0800Birth: - xjh@ubuntu:~/iot/tmp$ //上面的IO BLOCK 表示达到多少内容时,缓冲区的内容才会写入硬盘
stat函数的模型
int stat(const char *path, struct stat *buf);
stat函数的作用
内核将某个文件的属性信息结构体填充到stat函数的参数buf所指向的结构体中(这个参数是不加const修饰的指针,说明它是输出型参数),当stat这个API调用从内核返回时,buf所指向的结构体就被文件的属性信息填充好了。
我们后续通过查看buff结构体变量的元素,就可以得知该文件的各种属性。比如:
#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;//struct stat buf={0};初始化方法1,或者使用下面的memset(&buf, 0, sizeof(buf)); // memset后buf中全是0ret = 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; }
参数说明
struct stat是内核定义的一个结构体,在<sys/stat.h>中声明。 这个结构体中的所有元素加起来就是文件属性信息。
struct stat {dev_t st_dev; /* ID of device containing file */ino_t st_ino; /* inode number */mode_t st_mode; /* protection */nlink_t st_nlink; /* number of hard links */uid_t st_uid; /* user ID of owner */gid_t st_gid; /* group ID of owner */dev_t st_rdev; /* device ID (if special file) */off_t st_size; /* total size, in bytes */blksize_t st_blksize; /* blocksize for filesystem I/O */blkcnt_t st_blocks; /* number of 512B blocks allocated */time_t st_atime; /* time of last access */time_t st_mtime; /* time of last modification */time_t st_ctime; /* time of last status change */};
补充说明
stat和stat的区别
- stat是从文件名出发得到文件属性信息结构体,而fstat是从一个已经打开的文件fd出发,得到一个文件的属性信息;
- 如果文件没有打开就用stat,如果文件已经被打开用fstat效率会更高。因为stat是从磁盘去读取文件的,而fstat是从内存读取动态文件的。
lstat和stat/fstat的差别
- 对于符号链接文件,stat和fstat查阅的是符号链接文件指向的文件的属性,而lstat查阅的是符号链接文件本身的属性。
2、stat函数的应用案例
(1)获知文件类型
文件类型标志在struct stat结构体的 st_mode 元素中。
- st_mode本质上是一个32位的数(类型就是unsinged int),这个数里的每一个位表示一个含义,按位&操作可以知道某些信息。
- 但是这些位定义不容易记住,因此linux系统事先定义很多宏来进行相应操作。
- 比如S_ISREG宏返回值是1表示这个文件是一个普通文件,如果文件不是普通文件则返回值是0;
The following POSIX macros are defined to check \the file type using the st_mode field: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.)
2)获知文件权限
st_mode中除了记录文件类型,还记录着一个重要信息:文件权限。
linux并没有给文件权限测试提供宏操作,只提供了位掩码,所以我们只能用位掩码来判断是否具有相应权限。
The following flags are defined for the st_mode field:S_IFMT 0170000 bit mask for the file type bit fieldsS_IFSOCK 0140000 socketS_IFLNK 0120000 symbolic linkS_IFREG 0100000 regular fileS_IFBLK 0060000 block deviceS_IFDIR 0040000 directoryS_IFCHR 0020000 character deviceS_IFIFO 0010000 FIFOS_ISUID 0004000 set-user-ID bitS_ISGID 0002000 set-group-ID bit (see below)S_ISVTX 0001000 sticky bit (see below)S_IRWXU 00700 mask for file owner permissionsS_IRUSR 00400 owner has read permissionS_IWUSR 00200 owner has write permissionS_IXUSR 00100 owner has execute permissionS_IRWXG 00070 mask for group permissionsS_IRGRP 00040 group has read permissionS_IWGRP 00020 group has write permissionS_IXGRP 00010 group has execute permissionS_IRWXO 00007 mask for permissions for others (not in group)S_IROTH 00004 others have read permissionS_IWOTH 00002 others have write permissionS_IXOTH 00001 others have execute permission
3)代码示例
#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中全是0ret = stat(NAME, &buf); // stat后buf中有内容了if (ret < 0){perror("stat");exit(-1);}#if 0 // 判断这个文件属性int result = S_ISDIR(buf.st_mode);printf("result = %d\n", result); #endif// 文件权限测试unsigned int result = ((buf.st_mode & S_IRUSR)? 1: 0);printf("file owner: %u.\n", result);return 0; }
三、对目录文件的操作
(1)opendir函数与readdir函数
对于目录文件,我们一般先使用opendir函数打开目录文件,然后再使用readdir函数读取目录文件。
在命令行中输入“man 3 opendir”、“man 3readdir”,可以知道这两个函数的模型:
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dirp);
使用opendir函数打开一个目录后,得到一个DIR类型的指针给readdir使用。
每调用一次readdir函数,就会返回一个struct dirent类型的指针,它记录着目录中的一个文件。要想读出目录中所有的文件,必须多次调用readdir函数。当readdir函数返回NULL时就表示目录中所有的文件都已经被读完了。另外,readdir函数内部户会对已经读过的文件进行记录,因此不会重复返回已经返回过的文件。
(2)struct dirent 结构体
struct dirent {ino_t d_ino; /* inode number */off_t d_off; /* not an offset; see NOTES */unsigned short d_reclen; /* length of this record */unsigned char d_type; /* type of file; not supportedby all filesystem types */char d_name[256]; /* filename */ };
(3)代码示例
将下面的代码用gcc编译生成a.out文件,然后在命令行输入“./a.out xxx”,其中xxx表示某个目录。
#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; }