注:前言、目录见 https://blog.csdn.net/qq_44220418/article/details/108428971

文章目录

  • 一、文件系统简介
    • 1、索引节点
    • 2、文件系统组成
    • 3、文件
      • (1).类型
      • (2).文件权限
        • [1].文件权限
        • [2].表示方法
      • (3).访问文件
  • 二、文件基本I/O操作
    • 1、文件描述符
    • 2、常用系统调用
      • (1).`open`
      • (2).`close`
      • (3).`write`
      • (4).`read`
      • (5).`lseek`
      • (6).使用示例
      • (7).探索与练习
  • 三、目录操作
    • 1、目录文件
    • 2、常用系统调用
      • (1).`opendir`
      • (2).`readdir`
      • (3).`closedir`
      • (4).`rewinddir`
      • (5).`seekdir`
      • (6).`telldir`
      • (7).`mkdir`
      • (8).`rmdir`
      • (9).`getcwd`
      • (10).`chdir`
      • (11).`rename`
      • (12).使用示例
  • 四、文件/目录的属性
    • 1、常用函数
      • (1).`stat`
      • (2).`lstat`
      • (3).`fstat`
    • 2、常用宏
      • (1).文件类型宏
      • (2).文件权限宏
    • 3、使用示例

一、文件系统简介

1、索引节点

Linux系统采用按名存取的方式访问文件

除了文件名以外,文件的主要属性信息都存放在inode节点

目录文件中保存着文件名和索引节点的对应关系【可用命令ls -i查看文件/目录的inode号】

Linux系统中文件包括两部分

  • 索引节点inode
    \qquad记录文件属性信息(除了文件名)【可用命令ls -l查看】
    \;
    \qquad文件属性包括{文件类型访问权限文件主人组长度访问日期……\begin{cases} 文件类型 \\ 访问权限 \\ 文件主人 \\ 组 \\ 长度 \\ 访问日期 \\ …… \end{cases}⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧​文件类型访问权限文件主人组长度访问日期……​
    \;
    \qquad索引节点的结构如下图所示:
    \qquad\qquad
  • 数据块
    \qquad存放文件具体内容

注:硬链接文件的inode与源文件的inode相同

更多关于inode节点、软链接与硬链接的区别相关的知识可以参考博文 Linux文件索引节点inode

2、文件系统组成

一个简单的Unix文件系统组成
\qquad
\qquad引导块:用于引导该分区内操作系统的引导程序
\qquad超级块:UFS的重要参数,如文件系统的{块索引节点总数空闲块空闲索引节点数\begin{cases} 块 \\ 索引节点总数 \\ 空闲块 \\ 空闲索引节点数 \\ \end{cases}⎩⎪⎪⎪⎨⎪⎪⎪⎧​块索引节点总数空闲块空闲索引节点数​
\qquad索引节点表(i节点):文件系统中索引节点的集合
\qquad数据块:存储文件数据

Linux文件系统——Ext2
\qquad
\qquad第一个是引导块,其他空间分成各个块组
\qquad每个块组中{超级块块组描述符,数据块位图,索引节点位图,索引节点,数据块\begin{cases} 超级块 \\ 块组描述符,数据块位图,索引节点位图,索引节点,数据块 \end{cases}{超级块块组描述符,数据块位图,索引节点位图,索引节点,数据块​

3、文件

(1).类型

文件类型{普通文件(rugular file)目录文件(directory)字符设备文件(character device)块设备文件(block device)FIFO文件(fifo)符号链接文件(symbolic link)socket套接字文件(socket)\begin{cases} 普通文件 & (\text{rugular file})\\ 目录文件 & (\text{directory})\\ 字符设备文件 & (\text{character device})\\ 块设备文件 & (\text{block device})\\ \text{FIFO}文件 & (\text{fifo})\\ 符号链接文件 & (\text{symbolic link})\\ \text{socket}套接字文件 & (\text{socket})\\ \end{cases}⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧​普通文件目录文件字符设备文件块设备文件FIFO文件符号链接文件socket套接字文件​(rugular file)(directory)(character device)(block device)(fifo)(symbolic link)(socket)​

(2).文件权限

[1].文件权限

Linux中的用户分为三类{user/owner文件拥有者group用户组other其他用户\begin{cases} \text{user/owner} & 文件拥有者 \\ \text{group} & 用户组 \\ \text{other} & 其他用户 \\ \end{cases}⎩⎪⎨⎪⎧​user/ownergroupother​文件拥有者用户组其他用户​

Linux文件对三类用户都有三种访问权限{r可读w可写x可执行\begin{cases} \text{r} & 可读 \\ \text{w} & 可写 \\ \text{x} & 可执行 \\ \end{cases}⎩⎪⎨⎪⎧​rwx​可读可写可执行​

另外,Linux文件还有三种特殊的权限{setuidsetgidsticky\begin{cases} \text{setuid} \\ \text{setgid} \\ \text{sticky} \\ \end{cases}⎩⎪⎨⎪⎧​setuidsetgidsticky​
这里就不对这三种特殊权限做具体介绍了,想知道更详细的可以去看博文 Linux文件特殊权限——SetUID、SetGID、Sticky BIT


Linux文件的索引节点里面有一个st_mode字段,记录文件类型及权限
该字段共 161616 位{高4位文件类型低12位文件权限\begin{cases} 高4位 & 文件类型 \\ 低12位 & 文件权限 \\ \end{cases}{高4位低12位​文件类型文件权限​

12位文件权限{特殊权限{setuidsetgidsticky文件拥有者{r可读权限w可写权限x可执行权限用户组{r可读权限w可写权限x可执行权限其他用户{r可读权限w可写权限x可执行权限\begin{cases} 特殊权限 & \begin{cases} \text{setuid} \\ \text{setgid} \\ \text{sticky} \\ \end{cases} \\ \\ 文件拥有者 & \begin{cases} \text{r} & 可读权限 \\ \text{w} & 可写权限 \\ \text{x} & 可执行权限 \\ \end{cases} \\ \\ 用户组 & \begin{cases} \text{r} & 可读权限 \\ \text{w} & 可写权限 \\ \text{x} & 可执行权限 \\ \end{cases} \\ \\ 其他用户 & \begin{cases} \text{r} & 可读权限 \\ \text{w} & 可写权限 \\ \text{x} & 可执行权限 \\ \end{cases} \\ \end{cases}⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧​特殊权限文件拥有者用户组其他用户​⎩⎪⎨⎪⎧​setuidsetgidsticky​⎩⎪⎨⎪⎧​rwx​可读权限可写权限可执行权限​⎩⎪⎨⎪⎧​rwx​可读权限可写权限可执行权限​⎩⎪⎨⎪⎧​rwx​可读权限可写权限可执行权限​​

如下图所示:

[2].表示方法

文件权限的表示方法

  • 字母

    字母 权限
    r 可读
    w 可写
    x 可执行
    -

    如:rwxr-xr-x就表示了{拥有者对文件可读、可写、可执行用户组对文件可读、可执行其他用户对文件可读、可执行\begin{cases} 拥有者对文件可读、可写、可执行 \\ 用户组对文件可读、可执行 \\ 其他用户对文件可读、可执行 \\ \end{cases}⎩⎪⎨⎪⎧​拥有者对文件可读、可写、可执行用户组对文件可读、可执行其他用户对文件可读、可执行​

  • 数字(9位二进制 / 3位八进制)
    和上面的字母类似,对应位的二进制{1有权限0无权限\begin{cases} 1 & 有权限 \\ 0 & 无权限 \\ \end{cases}{10​有权限无权限​
    如:111 101 101就相当于rwxr-xr-x755就相当于rwxr-xr-x
  • 宏(S_IPwww模式)
    S_I固定,P可为RWXwww可为USRGRPOTH,多种权限可用或运算连接
    如:S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH就相当于rwxr-xr-x

注:编程中作为参数时,只能使用数字/宏

(3).访问文件

每打开一次文件

  • 系统增加一个file表项
  • 进程增加一个fd表项(以文件描述符为下标),通过指针指向file表项
  • 用户通过文件描述符系统调用访问该文件

二、文件基本I/O操作

1、文件描述符

用户空间中任何打开的文件都被分配一个唯一的非负整数,标识该打开文件,即文件描述符

进程默认打开的三个文件描述符

  • 标准输入 0
  • 标准输出 1
  • 标准错误输出 2

会选用文件描述符中未使用的最小值

2、常用系统调用

常用的系统调用如下

系统调用 说明
open 打开/新建文件
close 关闭文件
creat 新建文件
read 读文件
write 写文件
lseek 定位文件偏移量

(1).open

定义在<fcntl.h>

函数原型如下:

int open( const char *path, int flags);
int open( const char *path, int flags, mode_t perms);
功能
打开一个已经存在的文件时,参数perms可以省略
创建并打开一个不存在的文件时,参数perms必填
参数
参数 说明
path 文件路径
flags 文件打开方式(可用或运算|连接多种打开方式)
关于flags可选的宏参数可参考博客 https://blog.csdn.net/ArchyLi/article/details/78937937
perms 文件权限(4位八进制,仅创建时需要)
返回值
成功:返回最小可用的文件描述符(当前文件所用的文件描述符)
失败:返回-1(置errno

(2).close

定义在<unistd.h>

函数原型如下:

int close(int fd );
功能
关闭文件,释放文件描述符,使之可再利用
参数
参数 说明
fd 文件描述符
返回值
成功:返回0
失败:返回-1(置errno

注:当一个进程终止时,内核会自动检查并回收该进程所有的文件描述符

(3).write

定义在<unistd.h>

函数原型如下:

ssize_t write(int fd, const void *buf, size_t nbytes);
功能
buf所指缓冲区中的nbytes个字节写入文件描述符fd所指示的已打开文件中
注意点
写操作从当前文件偏移量处开始写数据,写完后,文件偏移量也将后移成功写入的字节数(因此不移动读写指针读不到刚刚写入的内容)
若调用open打开文件时,读写指针初始指向文件开头
\qquad若flags包含O_APPEND,则每次写入前,读写指针都将移动到文件末尾
由于物理介质空间不足等原因将会使得write的返回值小于计划要写入文件的字节个数
参数
参数 说明
fd 文件描述符
buf 要写入的数据
nbytes 要写入数据的字节数
返回值
成功:返回已写入的字节数
失败:返回-1(置errno

(4).read

定义在<unistd.h>

函数原型如下:

ssize_t read(int fd, void *buf, size_t nbytes);
功能
从一文件描述符fd对应的已打开文件中,读取nbytes字节的数据放入buf
注意点
读操作从当前文件偏移量处开始读数据,读完后,当前文件偏移也将改变成功读出的字节个数
若调用open打开文件时,读写指针初始指向文件开头
有时读到的数据可能比希望读的字节少,但系统不会置errno,因为这不是错误(如文件已经到结尾),需要用户自己去推测问题所在
参数
参数 说明
fd 文件描述符
buf 保存读取数据的缓冲区
nbytes 要读取数据的字节数
返回值
成功:返回已读取的字节数
失败:返回-1(置errno

(5).lseek

定义在<unistd.h>

函数原型如下:

off_t lseek(int fd, off_t pos, int whence);
文件偏移量
文件偏移量是一个非负整数,它用于标识下一次读或写文件的位置
除了O_APPEND模式打开文件外,读/写操作都从当前文件偏移量处开始
open一次文件,就能得到一个新的打开文件表项,因此每次open都可以得到一个独立的文件偏移量
功能
移动文件读写指针(文件偏移量)
参数
参数 说明
fd 文件描述符
pos 相对于whence的位置的偏移量
whence SEEK_SET:代表文件开始,即设文件偏移量为pos
SEEK_CUR:代表当前位置,即设文件偏移量为当前值+pos
SEEK_END:代表文件结尾,即设文件偏移量为文件长度+pos
返回值
成功:返回当前文件偏移量
失败:返回-1(置errno

(6).使用示例

示例1:创建文件权限为rw-r--r--的文件test.txt(初始时不存在),在其末尾写入内容

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main()
{// 创建并打开文件test.txtint fd = open("test.txt", O_RDWR | O_APPEND | O_CREAT, 0644);if (fd == -1){perror("open");exit(1);}// 写入文件char str1[] = "123456789012345678901234567890\n";if (write(fd, str1, strlen(str1)) != -1){printf("写入成功\n");}// 关闭文件close(fd);return 0;
}

程序输出的内容如下

写入成功

生成的test.txt文件内容如下

123456789012345678901234567890

示例2:从刚刚 示例1 生成的test.txt中读取至多 999999 字节的数据

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main()
{// 打开文件int fd = open("test.txt", O_RDWR);if (fd == -1){perror("open");exit(1);}// 读取文件char buf[100];read(fd, buf, sizeof(buf) - 1);printf("读取到的字符串是\n%s\n", buf);// 关闭文件close(fd);return 0;
}

示例3:创建文件权限为rw-r--r--的文件test3.txt(初始时不存在),利用lseek函数定位,写入、读取文件内容

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main()
{// 创建int fd3 = open("test3.txt", O_RDWR | O_CREAT, 0644);char str2[] = "newnewnewnewnewnewnewnew\n";char str3[] = "test\n";char buf[100];// 向文件末尾写入数据lseek(fd3, 0, SEEK_END);write(fd3, str2, strlen(str2));// 从文件开头读取数据lseek(fd3, 0, SEEK_SET);read(fd3, buf, sizeof(buf) - 1);printf("读取到的字符串3是\n%s\n", buf);// 向文件开头写入数据lseek(fd3, 0, SEEK_SET);write(fd3, str3, strlen(str3));// 从文件开头读取数据lseek(fd3, 0, SEEK_SET);read(fd3, buf, sizeof(buf) - 1);printf("读取到的字符串4是\n%s\n", buf);close(fd3);return 0;
}

程序输出的内容如下

读取到的字符串3是
newnewnewnewnewnewnewnew读取到的字符串4是
test
wnewnewnewnewnewnew

生成的test3.txt文件内容如下

test
wnewnewnewnewnewnew

显然这个最终结果是将test3.txt之前的前5个字符"newne"修改成了"test\n"


(7).探索与练习

探索1:对同一文件的不同fd的操作是否会造成相互影响?

已存在test2.txt,内容如下:

1234321222222

编写了如下C源程序对其进行探究:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main()
{char str1[] = "123456789012345678901234567890\n";char buf[100];int fd1 = open("test2.txt", O_RDWR | O_APPEND);int fd2 = open("test2.txt", O_RDONLY);write(fd1, str1, strlen(str1));write(fd1, str1, strlen(str1));write(fd1, str1, strlen(str1));read(fd2, buf, sizeof(buf) - 1);printf("读取到的字符串2是\n%s\n", buf);close(fd1);close(fd2);return 0;
}

程序输出的内容如下

读取到的字符串2是
1234321222222123456789012345678901234567890
123456789012345678901234567890
1234567890123456789012

修改后的test2.txt文件内容如下

1234321222222123456789012345678901234567890
123456789012345678901234567890
123456789012345678901234567890

显然,fd1fd2都是已打开文件test2.txt的文件描述符

fd1的写操作改变了fd1的文件偏移量,而没有改变fd2的文件偏移量(从读操作正常可见)

这也就验证了 每次open都可以得到一个独立的文件偏移量


练习1:将键盘输入的一批字符串(以quit结束)写入到文件test.txt中,然后再读出并显示到屏幕上

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>int main()
{char str[999];char buf[100];// 打开文件int fd = open("test.txt", O_CREAT | O_RDWR, 0644);// 循环读入并写入scanf("%s", &str);while (strcmp(str, "quit")){// 写入write(fd, str, strlen(str));scanf("%s", &str);}// 将文件偏移量定位到文件开头lseek(fd, 0, SEEK_SET);// 循环读取文件内容printf("读取到的写入内容如下:\n");while (read(fd, buf, sizeof(buf) - 1)){printf("%s", buf);}printf("\n");// 关闭文件close(fd);return 0;
}

程序输入的内容如下

1234
abc
quit

程序输出的内容如下

读取到的写入内容如下:
1234abc

创建的test.txt文件内容如下

1234abc

三、目录操作

1、目录文件

Linux系统中,目录也是以文件的方式存储,在目录文件中存储了该目录下的文件的信息,包括{文件名文件inode编号文件类型⋯\begin{cases} 文件名 \\ 文件inode编号 \\ 文件类型 \\ \cdots \\ \end{cases}⎩⎪⎪⎪⎨⎪⎪⎪⎧​文件名文件inode编号文件类型⋯​,姑且把这些信息称为一个文件的索引信息

如果想要找到某个文件,首先需要找到这个文件所在目录的目录文件

2、常用系统调用

常用的系统调用如下

系统调用 说明
opendir 打开目录
readdir 读取目录
telldir 返回当前打开的目录的下次读取位置
seekdir 设置目录流的读取位置为指定位置
rewinddir 重置目录流的读取位置为起始位置
closedir 关闭目录

(1).opendir

定义在<dirent.h>

函数原型如下:

DIR * opendir(const char *dir_path);
功能
打开一个目录,返回一个DIR指针,从而创建一个到目录的连接
参数
参数 说明
dir_path 目录路径
返回值
成功:返回指向参数目录的指针
失败:返回NULL(置errno

(2).readdir

定义在<dirent.h>

函数原型如下:

struct dirent * readdir(DIR *dir);
功能
DIR指针中读取一个目录项(文件/目录)的信息,并后移指针对应目录的读取位置
参数
参数 说明
dir 已打开目录对应的DIR指针
返回值
成功:返回相应目录项的结构体指针
失败:返回NULL(目录项读取完成)

(3).closedir

定义在<dirent.h>

函数原型如下:

int closedir(DIR * dir);
功能
关闭目录
参数
参数 说明
dir 已打开目录对应的DIR指针
返回值
成功:返回0
失败:返回-1(置errno

(4).rewinddir

定义在<dirent.h>

函数原型如下:

void rewinddir(DIR * dir);
功能
重置目录指针读取位置到起始位置
参数
参数 说明
dir 已打开目录对应的DIR指针

(5).seekdir

定义在<dirent.h>

函数原型如下:

void seekdir(DIR * dir, off_t offset);
功能
设置目录指针读取位置到指定位置
参数
参数 说明
dir 已打开目录对应的DIR指针

(6).telldir

定义在<dirent.h>

函数原型如下:

off_t telldir(DIR * dir);
功能
返回目录指针下次读取的位置(可以理解为一个从0开始的索引号)
参数
参数 说明
dir 已打开目录对应的DIR指针
返回值
成功:返回当前目录指针下次读取的位置
失败:返回-1(置errno

(7).mkdir

定义在<sys/stat.h>

函数原型如下:

int mkdir(char * pathname, mode_t mode);
功能
创建目录,并指定其权限模式
参数
参数 说明
pathname 创建目录的路径
mode 创建目录的权限模式
Tips:关于mode的选项,可以参考博客 mkdir()函数、mode_t参数
返回值
成功:返回0
失败:返回-1(置errno

(8).rmdir

定义在<unistd.h>

函数原型如下:

int rmdir(char * pathname);
功能
删除目录
参数
参数 说明
pathname 要删除目录的路径
返回值
成功:返回0
失败:返回-1(置errno

(9).getcwd

定义在<unistd.h>

函数原型如下:

char* getcwd(char *buf, size_t size);
功能
获取进程当前所处的工作目录的绝对路径,存入参数字符数组中
参数
参数 说明
buf 存储路径的字符数组
size buf所指向字符数组的字节数
返回值
成功:返回存储空间的首地址
失败:返回NULL(置errno

(10).chdir

定义在<unistd.h>

函数原型如下:

int chdir(const char *pathname);
功能
切换当前进程所处的工作目录
注意点
对其他进程、调用该进程的进程的当前工作目录均没有影响
参数
参数 说明
pathname 要切换到的工作目录路径
返回值
成功:返回0
失败:返回-1(置errno

(11).rename

定义在<unistd.h>

函数原型如下:

int rename(const char *from, const char *to);
功能
移动并重命名文件
注意点
rename并不真正复制文件中的数据,它只是将文件的链接从一个目录移动到另一个目录中,这个过程数据本身并没有移动
参数
参数 说明
from 源文件路径
to 新文件路径
返回值
成功:返回0
失败:返回-1(置errno

(12).使用示例

示例4:尝试使用上述若干系统调用读取、创建、删除、更改、查看目录

#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>int main()
{// 打开目录testdirDIR * dir_ptr = opendir("testdir");struct dirent * dir;// 获取下次读取位置if (dir_ptr != NULL)printf("下次读取的位置是:%ld\n\n", telldir(dir_ptr));// 获取目录下所有文件的名称printf("第一次打印:\n");if (dir_ptr != NULL)while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);printf("\n");// 重定位,重新读取printf("第二次打印:\n");if (dir_ptr != NULL){rewinddir(dir_ptr);while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);}printf("\n");// 重定位,重新读取printf("第三次打印:\n");if (dir_ptr != NULL){seekdir(dir_ptr, 3);while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);}printf("\n");// 关闭目录closedir(dir_ptr);// 创建目录int res = mkdir("./testmk", 0777);if (!res)printf("创建目录成功\n\n");// 删除目录res = rmdir("./testmk");if (!res)printf("删除目录成功\n\n");// 获取当前工作目录char str[100];getcwd(str, sizeof(str) - 1);printf("当前工作目录:\n%s\n\n", str);// 改变并重新获取当前工作目录res = chdir("./testdir/");if (!res){getcwd(str, sizeof(str) - 1);printf("改变后的当前工作目录:\n%s\n\n", str);}// 移动并重命名目录/文件// (注:因为已经改变了当前工作目录,所以现在已经跳转到了testdir目录下,再用"./testdir"就不管用了)int res2 = rename("./subdir/", "./newname/");int res3 = rename("./a.txt", "./b.txt");if (!res2 && !res3){printf("重命名成功\n");dir_ptr = opendir("./");printf("重命名后的打印:\n");if (dir_ptr != NULL)while ((dir = readdir(dir_ptr)) != NULL)printf("%s\n", dir->d_name);printf("\n");}return 0;
}

程序输出的内容如下

下次读取的位置是:0第一次打印:
.
..
12.txt
subdir
a.txt第二次打印:
.
..
12.txt
subdir
a.txt第三次打印:
12.txt
subdir
a.txt创建目录成功删除目录成功当前工作目录:
/home/excious/工程/目录操作改变后的当前工作目录:
/home/excious/工程/目录操作/testdir重命名成功
重命名后的打印:
.
..
12.txt
newname
b.txt

四、文件/目录的属性

文件与目录的属性存储与inode节点中,可通过系统调用stat获取文件或者目录的属性,存入struct stat结构中

结构体stat至少包含了以下文件信息:

struct stat
{dev_t st_dev,          // 包含该文件的设备ID号ino_t st_ino,         // 文件的inode号mode_t st_mode,         // 文件类型及权限模式nlink_t st_nlink,       // 文件的链接数uid_t st_uid,          // 文件所有者的用户IDgid_t st_gid,          // 文件的组IDdev_t st_rdev,         // 如果文件为字符或块设备时的设备IDoff_t st_size,          // 如果文件为普通文件时的文件字节数time_t st_atime,     // 最近的访问时间time_t st_mtime,      // 最近的数据修改时间time_t st_ctime,        // 最近的文件状态改变时间blksize_t st_blksize, // 该对象文件系统相关的最佳I/O块大小blkcnt_t st_blocks     // 系统为此文件所分配的数据块数
}

1、常用函数

(1).stat

定义在<sys/stat.h>

函数原型如下:

int stat(char *pathname, struct stat *buf);
功能
获取文件的详细信息
注意点
路径中的文件若是符号链接文件,则会获取其所链接到的文件的详细信息
参数
参数 说明
pathname 要切换到的工作目录路径
buf 要存入文件详细信息的结构指针
返回值
成功:返回0
失败:返回-1(置errno

(2).lstat

定义在<sys/stat.h>

函数原型如下:

int lstat(char *pathname, struct stat *buf);
功能
获取文件的详细信息
注意点
路径中的文件若是符号链接文件,则会获取该文件本身的详细信息
参数
参数 说明
pathname 要切换到的工作目录路径
buf 要存入文件详细信息的结构指针
返回值
成功:返回0
失败:返回-1(置errno

(3).fstat

定义在<sys/stat.h>

函数原型如下:

int fstat(int fd, struct stat *buf);
功能
获取已打开文件的详细信息
参数
参数 说明
fd 已打开文件的文件描述符
buf 要存入文件详细信息的结构指针
返回值
成功:返回0
失败:返回-1(置errno

2、常用宏

(1).文件类型宏

定义在<usr/include/bits/stat.h><sys/stat.h>

文件信息中的模式st_mode是一个161616位的二进制数,由高位(第151515位)到低位(第000位)分别是444为文件类型、333位特殊权限、333位用户权限、333位组权限、333位其他用户权限

C语言中已经定义好的文件类型宏如下表所示

最高四位二进制数 文件类型常量(八进制) 文件类型
0100 S_IFDIR 0040000 目录文件
0010 S_IFCHR 0020000 字符设备文件
0110 S_IFBLK 0060000 块设备文件
1000 S_IFREG 0100000 普通文件
1010 S_IFLNK 0120000 符号链接文件
1100 S_IFSOCK 0140000 Socket文件
0001 S_IFIFO 0010000 命名管道文件

可以把st_mode和相应的mask进行按位与,与上述相应的宏进行比较,判断文件类型

关于掩码mask
要判断文件类型,因为161616位文件模式中高444位是文件类型,因此应取mask1111000000000000,即mask = 0170000,该常量

C语言中也同样预定义好了用于判断文件类型的函数宏如下,直接将st_mode传入即可判断:

#define  __S_ISTYPE(mode, mask)  (((mode) & __S_IFMT) == (mask))#define    S_ISDIR(mode)    __S_ISTYPE((mode), __S_IFDIR)
#define S_ISCHR(mode)    __S_ISTYPE((mode), __S_IFCHR)
#define S_ISBLK(mode)    __S_ISTYPE((mode), __S_IFBLK)
#define S_ISREG(mode)    __S_ISTYPE((mode), __S_IFREG)#ifdef __S_IFIFO
# define S_ISFIFO(mode)  __S_ISTYPE((mode), __S_IFIFO)
#endif#ifdef __S_IFLNK
# define S_ISLNK(mode)   __S_ISTYPE((mode), __S_IFLNK)
#endif

(2).文件权限宏

定义在<usr/include/bits/stat.h><sys/stat.h>

文件信息中的模式st_mode低999位表示文件权限

C语言中已经定义好的文件权限宏如下

#define S_IRUSR 0000400                                  // 文件所有者读权限
#define S_IWUSR 0000200                                 // 文件所有者写权限
#define S_IXUSR 0000100                                 // 文件所有者执行权限
#define S_IRWXU (__S_IREAD | __S_IWRITE | __S_IEXEC)    // 文件所有者读写执行权限#define S_IRGRP (S_IRUSR >> 3)                          // 组用户读权限
#define S_IWGRP (S_IWUSR >> 3)                            // 组用户写权限
#define S_IXGRP (S_IXUSR >> 3)                            // 组用户执行权限
#define S_IRWXG (S_IRWXI >> 3)                            // 组用户读写执行权限#define S_IROTH (S_IRGRP >> 3)                            // 其他用户读权限
#define S_IWOTH (S_IWGRP >> 3)                            // 其他用户写权限
#define S_IXOTH (S_IXGRP >> 3)                            // 其他用户执行权限
#define S_IRWXO (S_IRWXO >> 3)                            // 其他用户读写执行权限

3、使用示例

示例5:尝试使用上述若干系统调用获取文件信息、判断文件类型和文件权限

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>int main(int argc, char *argv[])
{struct stat buf;int res = stat("./test.txt", &buf);if (res != -1){printf("文件的\t【设备ID号】\t\t\t为\t【%ld】\n", buf.st_dev);printf("文件的\t【inode号】\t\t\t为\t【%ld】\n", buf.st_ino);printf("文件的\t【类型及权限模式】\t\t为\t【%d】\n", buf.st_mode);printf("文件的\t【链接数】\t\t\t为\t【%ld】\n", buf.st_nlink);printf("文件的\t【所有者用户ID】\t\t为\t【%d】\n", buf.st_uid);printf("文件的\t【组ID】\t\t\t为\t【%d】\n", buf.st_gid);printf("文件的\t【字节数】\t\t\t为\t【%ld】\n", buf.st_size);printf("文件的\t【最近访问时间】\t\t为\t【%ld】\n", buf.st_atime);printf("文件的\t【最近数据修改时间】\t\t为\t【%ld】\n", buf.st_mtime);printf("文件的\t【最近状态改变时间】\t\t为\t【%ld】\n", buf.st_ctime);printf("文件的\t【系统相关最佳I/O块大小】\t为\t【%ld】\n", buf.st_blksize);printf("文件的\t【被系统分配到的数据块数】\t为\t【%ld】\n", buf.st_blocks);}printf("==============================================================\n");int fd = open("test.txt", O_RDONLY);struct stat buf2;int res2 = fstat(fd, &buf2);if (res2 != -1){printf("文件的\t【设备ID号】\t\t\t为\t【%ld】\n", buf2.st_dev);printf("文件的\t【inode号】\t\t\t为\t【%ld】\n", buf2.st_ino);printf("文件的\t【类型及权限模式】\t\t为\t【%d】\n", buf2.st_mode);printf("文件的\t【链接数】\t\t\t为\t【%ld】\n", buf2.st_nlink);printf("文件的\t【所有者用户ID】\t\t为\t【%d】\n", buf2.st_uid);printf("文件的\t【组ID】\t\t\t为\t【%d】\n", buf2.st_gid);printf("文件的\t【字节数】\t\t\t为\t【%ld】\n", buf2.st_size);printf("文件的\t【最近访问时间】\t\t为\t【%ld】\n", buf2.st_atime);printf("文件的\t【最近数据修改时间】\t\t为\t【%ld】\n", buf2.st_mtime);printf("文件的\t【最近状态改变时间】\t\t为\t【%ld】\n", buf2.st_ctime);printf("文件的\t【系统相关最佳I/O块大小】\t为\t【%ld】\n", buf2.st_blksize);printf("文件的\t【被系统分配到的数据块数】\t为\t【%ld】\n", buf2.st_blocks);}close(fd);printf("==============================================================\n");struct stat buf3;int res3 = stat("./test.txt", &buf3);if (res3 != -1){if ((buf3.st_mode & S_IFMT) == S_IFREG)printf("文件test.txt是一个普通文件\n");if (S_ISREG(buf3.st_mode))printf("文件test.txt是一个普通文件\n");}printf("==============================================================\n");struct stat buf4;int res4 = stat("./test.txt", &buf4);if (res4 != -1){int mode = buf4.st_mode;char str_res[] = "----------";// 判断文件类型if (S_ISDIR(mode)) str_res[0] = 'd';if (S_ISCHR(mode)) str_res[0] = 'c';if (S_ISBLK(mode)) str_res[0] = 'b';// 判断用户权限if (mode & S_IRUSR) str_res[1] = 'r';if (mode & S_IWUSR) str_res[2] = 'w';if (mode & S_IXUSR) str_res[3] = 'x';// 判断组权限if (mode & S_IRGRP) str_res[4] = 'r';if (mode & S_IWGRP) str_res[5] = 'w';if (mode & S_IXGRP) str_res[6] = 'x';// 判断其他用户权限if (mode & S_IROTH) str_res[7] = 'r';if (mode & S_IWOTH) str_res[8] = 'w';if (mode & S_IXOTH) str_res[9] = 'x';// 输出文件类型和权限printf("文件类型和权限为%s\n", str_res);}return 0;
}

程序输出的内容如下

文件的  【设备ID号】             为   【33】
文件的 【inode号】                为   【33875】
文件的 【类型及权限模式】           为   【33188】
文件的 【链接数】               为   【1】
文件的 【所有者用户ID】           为   【1000】
文件的 【组ID】                   为   【1000】
文件的 【字节数】               为   【7】
文件的 【最近访问时间】            为   【1618807263】
文件的 【最近数据修改时间】      为   【1618807248】
文件的 【最近状态改变时间】      为   【1618807260】
文件的 【系统相关最佳I/O块大小】  为   【4096】
文件的 【被系统分配到的数据块数】   为   【8】
==============================================================
文件的 【设备ID号】             为   【33】
文件的 【inode号】                为   【33875】
文件的 【类型及权限模式】           为   【33188】
文件的 【链接数】               为   【1】
文件的 【所有者用户ID】           为   【1000】
文件的 【组ID】                   为   【1000】
文件的 【字节数】               为   【7】
文件的 【最近访问时间】            为   【1618807263】
文件的 【最近数据修改时间】      为   【1618807248】
文件的 【最近状态改变时间】      为   【1618807260】
文件的 【系统相关最佳I/O块大小】  为   【4096】
文件的 【被系统分配到的数据块数】   为   【8】
==============================================================
文件test.txt是一个普通文件
文件test.txt是一个普通文件
==============================================================
文件类型和权限为-rw-r--r--

《Linux编程》学习笔记 ·004【文件I/O操作】相关推荐

  1. Linux系统学习笔记:文件描述符标志

    文件描述符标志的概念 文件描述符标志(目前就只有一个close-on-exec): 它仅仅是一个标志,当进程fork一个子进程的时候,在子进程中调用了exec函数时就用到了这个标志.意义是执行exec ...

  2. Linux centOS学习笔记(文件处理命令)

    touch: [文件名] 在当前目录创建一个文件(文件名中有空格需要加双引号,不然会创建两个文件) [/路径/文件名] 在指定路径创建一个文件 cat: [/路径/文件名] 查看文件内容(不适合浏览长 ...

  3. Linux命令学习笔记(一)目录操作

    目录操作 在桌面建立一个名为test的文件夹 clear 清除屏幕 pwd 查看当前工作目录 cd /home 进入 '/ home' 目录' cd ~ 打开当前用户目录 cd / 根目录 cd - ...

  4. shell脚本编程学习笔记(四)shell操作数据库

    一.数据库基本操作 1)登录mysql服务器:mysql -u root -p 密码 2)查看数据库:show databases 3)查看表:show tales from db; 4)查看表结构: ...

  5. Linux基本命令学习笔记--文件查看、下载、查找

    1.Linux基本命令学习笔记–(文件查看) cat -整个查看 -n:行号 >(创建.覆盖) >>(追加)a.txt<<EOF:编写内容 内容 EOF (定格写:结束) ...

  6. Linux与C++11多线程编程(学习笔记)

    多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...

  7. [Linux网络编程学习笔记]索引

    一.Linux基本知识 [学习笔记]Linux平台的文件I/O操作 [学习笔记]Linux平台的文件,目录及操作 [Linux学习笔记]标准输入输出 [Linux学习笔记]进程概念及控制 [Linux ...

  8. 《Linux Shell编程学习笔记之一》

    <Linux Shell编程学习笔记之一> 前言 由于自己一直在Windows上面编程,用linux用的比较少,学习linux还是本科大二学的一点点知识.因此自己就准备花点时间来熟悉下li ...

  9. Linux Shell编程学习笔记(2)

    Linux Shell编程学习笔记(2015-7-12) 分类:linux shell Shell变量 一:关于Shell变量   Shell是一种弱类型的语言,变量存储的一切值都是字符串.Shell ...

  10. linux系统管理学习笔记之八---linux文件与目录的管理及权限

    linux系统管理学习笔记之八---linux文件与目录的管理及权限 2010-01-05 09:00:49 标签:权限 管理 文件目录 linx [推送到技术圈] 版权声明:原创作品,允许转载,转载 ...

最新文章

  1. 标题和描述需注意什么才更有利于SEO首页优化?
  2. python多个for的执行顺序-python_装饰器篇(多个装饰器下的执行顺序)
  3. mysql清理连接数缓存,MySQL连接池、线程缓存、线程池的区别
  4. Sentinel 规则持久化到 apollo 配置中心
  5. python rest 框架_python-更新用户REST框架Django
  6. mysql数据库的注释语句是_coding++ :MySQL 使用 SQL 语句查询数据库所有表注释已经表字段注释...
  7. winform flash
  8. metasploit连接postgresql的问题(U盘启动kali linux)
  9. Cannot use v-for on stateful component root element because it renders multiple elements.
  10. Linux meset
  11. 分享两个好玩好看的特效
  12. pfamscan 的使用_48个在线分析使用工具
  13. 小程序分享图片给好友,到朋友圈,保存到本地
  14. android 测光模式,安卓手机里的专业模式究竟该怎么拍?
  15. 汉堡包菜单_神圣的汉堡包!
  16. Vue无缝滚动轮播插件vue-seamless-scroll
  17. 王和勇计算机软件,图像空间中的鉴别型局部线性嵌入方法
  18. python合并word表格单元格_Python-Excel转word表格并合并流程
  19. android MediaPlayer 完成播放后无法再次播放
  20. vue 如何实现多个路由共用同一个页面组件

热门文章

  1. 数据分析学习笔记—文件处理与pdf处理
  2. centos屏幕视频录制,转换
  3. orika java_使用orika进行对象间Mapping
  4. python布尔类型运算_Python对象类型及其运算方法(详解)
  5. 【机器学习-西瓜书】七、贝叶斯分类器
  6. AI人工智能 / ML机器学习专业词汇集
  7. 第八讲 拍卖的博弈分析练习题
  8. STL数组处理常用函数
  9. JS记坑 ----- children返回的类数组
  10. 剑指 Offer 58 - I. 翻转单词顺序 (双指针)