3.2.文件属性

版权声明:转载请声明 https://blog.csdn.net/qq_40732350/article/details/81988921

请移步到这:

http://note.youdao.com/noteshare?id=6292675da6e0a33a412944bcd7925308&sub=2BC58A4DD5294D238BF94F20C6920B67

3.2.1.linux中各种文件类型

3.2.1.1、普通文件(— regular file)

(1)文本文件。文件中的内容是由文本构成的,文本指的是ASCII码字符。文件里的内容本质上都是数字(不管什么文件内容本质上都是数字,因为计算机中本身就只有1和0),而文本文件中的数字本身应该被理解为这个数字对应的ASCII码。常见的.c文件, .h文件 .txt文件等都是文本文件。文本文件的好处就是可以被人轻松读懂和编写。所以说文本文件天生就是为人类发明的。

(2)二进制文件。二进制文件中存储的本质上也是数字,只不过这些数字并不是文字的编码数字,而是就是真正的数字。常见的可执行程序文件(gcc编译生成的a.out,arm-linux-gcc编译连接生成的.bin)都是二进制文件。

(3)对比:从本质上来看(就是刨除文件属性和内容的理解)文本文件和二进制文件并没有任何区别。都是一个文件里面存放了数字。区别是理解方式不同,如果把这些数字就当作数字处理则就是二进制文件,如果把这些数字按照某种编码格式去解码成文本字符,则就是文本文件。

(4)我们如何知道一个文件是文件文件还是二进制文件?在linux系统层面是不区分这两个的(譬如之前学过的open、read、write等方法操作文件文件和二进制文件时一点区别都没有),所以我们无法从文件本身准确知道文件属于哪种,我们只能本来就知道这个文件的类型然后用这种类型的用法去用他。有时候会用一些后缀名来人为的标记文件的类型。

(5)使用文本文件时,常规用法就是用文本文件编辑器去打开它、编辑它。常见的文本文件编辑器如vim、gedit、notepad++、SourceInsight等,我们用这些文本文件编辑器去打开文件的时候,编辑器会read读出文件二进制数字内容,然后按照编码格式去解码将其还原成文字展现给我们。如果用文本文件编辑器去打开一个二进制文件会如何?这时候编辑器就以为这个二进制文件还是文本文件然后试图去将其解码成文字,但是解码过程很多数字并不对应有意义的文字所以成了乱码。

(6)反过来用二进制阅读工具去读取文本文件会怎么样?得出的就是文本文字所对应的二进制的编码。

3.2.1.2、目录文件(d directory) 相当于一个门

(1)目录就是文件夹,文件夹在linux中也是一种文件,不过是特殊文件。用vi打开一个文件夹就能看到,文件夹其实也是一种特殊文件,里面存的内容包括这个文件的路径,还有文件夹里面的文件列表。

(2)但是文件夹这种文件比较特殊,本身并不适合用普通的方式来读写。linux中是使用特殊的一些API来专门读写文件夹的。

vi 3.1.11

3.2.1.3、字符设备文件(c character)

3.2.1.4、块设备文件(b block)

(1)设备文件对应的是硬件设备,也就是说这个文件虽然在文件系统中存在,但是并不是真正存在于硬盘上的一个文件,而是文件系统虚拟制造出来的(叫虚拟文件系统,如/dev /sys /proc等)

(2)虚拟文件系统中的文件大多数不能或者说不用直接读写的,而是用一些特殊的API产生或者使用的,具体在驱动阶段会详解。

3.2.1.5、管道文件(p pipe)

3.2.1.6、套接字文件(s socket)

3.2.1.7、符号链接文件(l link)

3.2.2.常用文件属性获取

3.2.2.1、stat、fstat、lstat函数简介

(1)每个文件中都附带了这个文件的一些属性(属性信息是存在于文件本身中的,但是它不像文件的内容一样可以被vi打开看到,属性信息只能被专用的API打开看到)

命令stat a.out

(2)文件属性信息查看的API有三个:stat、fstat、lstat,三个作用一样,参数不同,细节略有不同。

(3)linux命令行下还可以去用stat命令去查看文件属性信息,实际上stat命令内部就是使用stat系统调用来实现的。

(4)stat这个API的作用就是让内核将我们要查找属性的文件的属性信息结构体的值放入我们传递给stat函数的buf中,当stat这个API调用从内核返回的时候buf中就被填充了文件的正确的属性信息,然后我们通过查看buf这种结构体变量的元素就可以得知这个文件的各种属性了。

(5)fstat和stat的区别是:

stat是从文件名出发得到文件属性信息结构体,

fstat是从一个已经打开的文件fd出发得到一个文件的属性信息。

所以用的时候如果文件没有打开(我们并不想打开文件操作而只是希望得到文件属性)那就用stat,如果文件已经被打开了然后要属性那就用fstat效率会更高(stat是从磁盘去读取文件的,而fstat是从内存读取动态文件的)。

(6)lstat和stat/fstat的差别在于:对于符号链接文件,stat和fstat查阅的是符号链接文件指向的文件的属性,而lstat查阅的是符号链接文件本身的属性。

3.2.2.2、struct stat结构体简介

(1)struct stat是内核定义的一个结构体,在<sys/stat.h>中声明,所以我们可以用。这个结构体中的所有元素加起来就是我们的文件属性信息。

struct stat { dev_t st_dev; /* ID of device containing file -文件所在设备(硬盘,或者其他)的ID*/ ino_t st_ino; /* inode number -inode节点号*/ mode_t st_mode; /* protection -文件对应的模式,文件,目录等*/ nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/ uid_t st_uid; /* user ID of owner -user id-文件所有者*/ gid_t st_gid; /* group ID of owner - group id-文件所有者的组号*/ 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 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 - 最近修改属性时间*/ };

3.2.2.3、写个程序来查看一些常见属性信息

用法实例:

ret = stat(NAME, &buf);

printf("file owner: %u.\n", buf.st_mode);

3.2.3.解析 st_mode

3.2.3.1、用代码判断文件类型

(1)文件类型就是-、d、l····

(2)文件属性中的文件类型标志在struct stat结构体的mode_t st_mode元素中,这个元素其实是一个按位来定义的一个位标志(有点类似于ARM CPU的CPSR寄存器的模式位定义)。这个东西有很多个标志位共同构成,记录了很多信息,如果要查找时按位&操作就知道结果了,但是因为这些位定义不容易记住,因此linux系统给大家事先定义好了很多宏来进行相应操作。

(3)譬如S_ISREG宏返回值是

1表示这个文件是一个普通文件,

0表示不是普通文件则返回值是.

S_ISREG(st_mode) 是否为一般文件?

S_ISDIR(st_mode) 是否为目录?

S_ISCHR(st_mode) 是否为字符设备文件?

S_ISBLK(st_mode) 是否为块设备文件?

S_ISFIFO(st_mode) FIFO文件 (named pipe)?

S_ISLNK(st_mode) 链接文件? (Not in POSIX.1-1996.)

S_ISSOCK(st_mode) socket文件? (Not in POSIX.1-1996.)

用这样的方式来使用

结果:

3.2.3.2、用代码判断文件权限设置

(1)st_mode中除了记录了文件类型之外,还记录了一个重要信息:文件权限。

(2)linux并没有给文件权限测试提供宏操作,而只是提供了位掩码,所以我们只能用位掩码来自己判断是否具有相应权限。

用这样的方式来使用

例如:unsigned int result = (buf.st_mode & S_IRUSR) >> 8

S_IFMT 0170000 文件类型的位遮罩 S_IFSOCK 0140000 scoket S_IFLNK 0120000 符号连接 S_IFREG 0100000 一般文件 S_IFBLK 0060000 区块装置 S_IFDIR 0040000 目录 S_IFCHR 0020000 字符装置 S_IFIFO 0010000 先进先出 S_ISUID 04000 文件的 (set user-id on execution)位 S_ISGID 02000 文件的 (set group-id on execution)位 S_ISVTX 01000 文件的sticky 位

S_IRWXU 00700 文件所有者权限的 掩码

(用这个掩码会得到一个8进制的数字,用来表示权限) S_IRUSR 00400 文件所有者具可读取权限 这些不能当作掩码 S_IWUSR 00200 文件所有者具可写入权限 S_IXUSR 00100 文件所有者具可执行权限

S_IRWXG 00070 文件组权限的 掩码

(用这个掩码会得到一个8进制的数字,用来表示权限) S_IRGRP 00040 用户组具可读取权限 S_IWGRP 00020 用户组具可写入权限 S_IXGRP 00010 用户组具可执行权限

S_IRWXO 00007 其他用户权限的 掩码

(用这个掩码会得到一个8进制的数字,用来表示权限)

S_IROTH 00004 其他用户具可写读权限

S_IWOTH 00002 其他用户具可写入权限 S_IXOTH 00001 其他用户具可执行权限

返回的错误代码:

1、ENOENT 参数file_name 指定的文件不存在

2、ENOTDIR 路径中的目录存在但却非真正的目录

3、ELOOP 欲打开的文件有过多符号连接问题, 上限为16 符号连接

4、EFAULT 参数buf 为无效指针, 指向无法存在的内存空间

5、EACCESS 存取文件时被拒绝

6、ENOMEM 核心内存不足

7、ENAMETOOLONG 参数file_name 的路径名称太长

3.2.4.文件权限管理1

我们在操作文件时要判断文件对当前用户的权限,

3.2.4.1、st_mode中记录的文件权限位

(1)st_mode本质上是一个32位的数(类型就是unsinged int),这个数里的每一个位表示一个含义。

(2)文件类型和文件的权限都记录在st_mode中。我们用的时候使用专门的掩码去取出相应的位即可得知相应的信息。

3.2.4.2、ls -l打印出的权限列表

(1)123456789一共9位,3个一组。第一组三个表示文件的属主(owner、user)对该文件的可读、可写、可执行权限;第2组3个位表示文件的属主所在的组(group)对该文件的权限;第3组3个位表示其他用户(others)对该文件的权限。

(2)属主就是这个文件属于谁,一般来说文件创建时属主就是创建这个文件的那个用户。但是我们一个文件创建之后还可以用chown命令去修改一个文件的属主,还可以用chgrp命令去修改一个文件所在的组。

3.2.4.3、文件操作时的权限检查规则

(1)一个程序a.out被执行,a.out中试图去操作一个文件1.txt,这时候如何判定a.out是否具有对1.txt的某种操作权限呢?

(2)判定方法是:首先1.txt具有9个权限位,规定了3种人(user、group、others)对该文件的操作权限。所以我们判定1.txt是否能被a.out来操作,关键先搞清楚a.out对1.txt到底算哪种人。准确的说是看a.out被谁执行,也就是当前程序(进程)是哪个用户的进程。

(3)刚才上面说的是我的分析,到底对不对还得验证。

3.2.5.文件权限管理2

3.2.5.1、access函数检查权限设置

(1)文本权限管控其实蛮复杂,一般很难很容易的确定对一个文件是否具有某种权限。设计优秀的软件应该是:在操作某个文件之前先判断当前是否有权限做这个操作,如果有再做如果没有则提供错误信息给用户。

(2)access函数可以测试得到当前执行程序的那个用户在当前那个环境下对目标文件是否具有某种操作权限。

int access( const char *pathname, int mode);

mode:

R_OK(读) W_OK(可写) X_OK(可执行) F_OK(文件是否存在)

3.2.5.2、chmod/fchmod与权限修改

(1)chmod是一个linux命令,用来修改文件的各种权限属性。chmod命令只有root用户才有权利去执行修改。

(2)chmod命令其实内部是用linux的一个叫chmod的API实现的。

int chmod( const char *path, mode_t mode);

int fchmod( int fd, mode_t mode);

用法实例:

ret = chmod("1.txt", S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWOTH);

3.2.5.3、chown/fchown/lchown与属主修改

(1)linux中有个chown命令来修改文件属主

(2)chown命令是用chown API实现的

int chown( const char *path, uid_t owner, gid_t group);

int fchown( int fd, uid_t owner, gid_t group);

int lchown( const char *path, uid_t owner, gid_t group);

用stat可以查找ID号

3.2.5.4、umask与文件权限掩码 umask时权限的的取反的结果

(1)文件掩码是linux系统中维护的一个全局设置,umask的作用是用来设定我们系统中新创建的文件的默认权限的。

(2)umask命令就是用umask API实现的

umask默认为0022

改变umask后,创建6.txt

umask()函数的用法:

https://blog.csdn.net/zhuyi2654715/article/details/7540759

用法实例:

umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

3.2.5.5创建文件函数creat()

int creat(const char *pathname, mode_t mode);

S_IRWXU 00700 user (file owner) has read, write and execute permission

S_IRUSR 00400 user has read permission

S_IWUSR 00200 user has write permission

S_IXUSR 00100 user has execute permission

S_IRWXG 00070 group has read, write and execute permission

S_IRGRP 00040 group has read permission

S_IWGRP 00020 group has write permission

S_IXGRP 00010 group has execute permission

S_IRWXO 00007 others have read, write and execute permission

S_IROTH 00004 others have read permission

S_IWOTH 00002 others have write permission

S_IXOTH 00001 others have execute permission

用法实例:

creat("foo", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH );

3.2.6.读取目录文件

3.2.6.1、opendir与readdir函数

(1)opendir打开一个目录后得到一个DIR类型的指针给readdir使用

(2)readdir函数调用一次就会返回一个struct dirent类型的指针,这个指针指向一个结构体变量,这个结构体变量里面记录了一个目录项(所谓目录项就是目录中的一个子文件)。

(3)readdir调用一次只能读出一个目录项,要想读出目录中所有的目录项必须多次调用readdir函数。readdir函数内部户记住哪个目录项已经被读过了哪个还没读,所以多次调用后不会重复返回已经返回过的目录项。当readdir函数返回NULL时就表示目录中所有的目录项已经读完了。

3.2.6.2、dirent结构体

3.2.6.3、读取目录实战演练

DIR *opendir( const char *name);

DIR *fdopendir( int fd);

参考我的博客:

https://blog.csdn.net/qq_40732350/article/details/81986548

struct dirent *readdir( DIR *dirp);

int readdir_r( DIR *dirp, struct dirent *entry, struct dirent **result);

文件目录结构体:

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 supported by all filesystem types 文件类型*/

char d_name[256]; /* filename 文件名,最长255字符*/

};

d_type的值为:

DT_BLK This is a block device.

DT_CHR This is a character device.

DT_DIR This is a directory.

DT_FIFO This is a named pipe (FIFO).

DT_LNK This is a symbolic link.

DT_REG This is a regular file.

DT_SOCK This is a UNIX domain socket.

DT_UNKNOWN The file type is unknown.

3.2.6.4、可重入函数介绍

(1)有些函数是可重入的有些是不可重入的,具体概念可以去百度。

(2)readdir函数和我们前面接触的一些函数是不同的,首先readdir函数直接返回了一个结构体变量指针,因为readdir内部申请了内存并且给我们返回了地址。多次调用readdir其实readir内部并不会重复申请内存而是使用第一次调用readdir时分配的那个内存。这个设计方法是readdir不可重入的关键。

(3)readdir在多次调用时是有关联的,这个关联也标明readdir函数是不可重入的。

(4)库函数中有一些函数当年刚开始提供时都是不可重入的,后来意识到这种方式不安全,所以重新封装了C库,提供了对应的可重复版本(一般是不可重入版本函数名_r)

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/81988921
3.2
今日推荐