133 Linux 系统编程10 ,文件系统,inode,dentry,stat函数,lstat函数,access函数,chmod函数,truncate 函数,link函数,unlink函数,隐式回收

一 文件存储相关概念,这和后面函数学习息息相关。

inode

前面我们学习过 使用 stat a.txt 去查看一个文件的属性,可以看到每个文件都一个Inode,从内容来看,像是一个 int,实际其本质为结构体

hunandede@hunandede-virtual-machine:~/projects/linuxcpp$ stat main.cpp
  File: 'main.cpp'
  Size: 6428      	Blocks: 16         IO Block: 4096   regular file
Device: 801h/2049d	Inode: 665076      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/hunandede)   Gid: ( 1000/hunandede)
Access: 2024-02-24 12:44:27.250263847 +0800
Modify: 2024-02-24 12:44:26.662256231 +0800
Change: 2024-02-24 12:44:26.662256231 +0800
 Birth: -

其本质为结构体,存储文件的属性信息。如:权限、类型、大小、时间、用户、盘块位置……也叫作文件属性管理结构,大多数的inode都存储在磁盘上。

少量常用、近期使用的inode会被缓存到内存中。

dentry

目录项,其本质依然是结构体,重要成员变量有两个 {文件名,inode,...},而文件内容(data)保存在磁盘盘块中。

当我们增加 或者减少一个 "硬链接" 的时候,实际上就是 增加一个 dentry 或者减少一个 dentry

 注意 :当我们的dentry 被删除完毕后,磁盘上的区域并不会擦除。

即使是单纯的初始化也没有用。

那应该怎么擦除这些信息呢?把磁盘的这块区域重新写一遍就OK了,怎么知道是这块区域呢?

当然肯定有确定的办法, 但是还有笨办法,将你的磁盘重新写满,拷贝300集的 圣斗士星矢。

这就是 艳照门 事件 的原理,冠希哥看来读计算机的时候内有认真听讲。

二 文件操作 stat函数 

函数原型:

获取文件属性,(从inode结构体中获取)

int stat(const char *path, struct stat *buf); 成返回0;失败返回-1 设置errno为恰当值。

参数:

参数1:文件名,文件路径

参数2:inode结构体指针 (传出参数),函数调用完毕后,会将file stat信息存在这个struct中

文件属性将通过传出参数返回给调用者。

返回值:

成功,返回0

失败,返回-1,errno被设置

stat 结构体的样子

  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 */

               /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* time of last access */
               struct timespec st_mtim;  /* time of last modification */
               struct timespec st_ctim;  /* time of last status change */

           #define st_atime st_atim.tv_sec      /* Backward compatibility */
           #define st_mtime st_mtim.tv_sec
           #define st_ctime st_ctim.tv_sec
           };

st_mode表示文件的类型和存取的权限,为16位整数

//0-2bit--其他人权限
S_IROTH     00004   读权限
S_IWOTH     00002   写权限
S_IXOTH     00001   执行权限
S_IRWXO     00007   掩码,过滤st_mode中除其他人权限以外的信息
 
//3-5bit--所属组权限
S_IRGRP     00040   读权限
S_IWGRP     00020   写权限
S_IXGRP     00010   执行权限
S_IRWXG     00070   掩码,过滤st_mode中除所属组权限以外的信息
 


获取权限   //6-8bit--文件所有者权限


S_IRUSR     00400   读权限
S_IWUSR     00200   写权限
S_IXUSR     00100   执行权限
S_IRWXU     00700   掩码,过滤st_mode中除文件所有者权限以外的信息
if(st_mode & S_IRUSR)//-----为真,表示可读
if(st_mode & S_IWUSR)//-----为真,表示可写
if(st_mode & S_IXUSR)//-----为真,表示可执行

获取类型(普通文件,文件夹,软链接,socket套接字,fifo管道,block块设备文件,character 字符设备文件)


//12-15bit--文件类型
S_IFSOCK    0140000     socket(套接字)
S_IFLNK     0120000     symbolic link(软连接)
S_IFREG     0100000     regular file(普通文件)
S_IFBLK     0060000     block device(块设备文件)
S_IFDIR     0040000     directory(目录)
S_IFCHR     0020000     character device(字符设备文件)
S_IFIFO     0010000     FIFO(管道)
S_IFMT      0170000     掩码,过滤st_mode中除文件类型以外的信息
 if(( st_mode & S_IFMT) == S_IFREG)//-----为真,表示普通文件
 if(S_ISREG (st_mode))                       //-----为真,表示普通文件
 if(S_ISDIR (st_mode))                       //-----为真,表示目录文件

代码例子:练习:使用stat函数查看文件属性

int main() {
	const char * filepath = "/home/hunandede/projects/linuxcpp/main.cpp";
	struct stat buf ;
	int ret = stat(filepath, &buf);
	if (ret == -1) {
		perror("stat /home/hunandede/projects/linuxcpp/main.cpp has been error ->");
		exit(ret);
	}
	//重要参数st_size 或者文件的大小
	cout << "buf.size = " << buf.st_size << endl;;//long int  7696 ,然后我们使用ls -l查看,文件大小是一样的
	cout << "buf.st_mode = " << buf.st_mode << endl;;//long int 33204 这个33204包含了文件类型,文件所有者权限,文件所有组权限,其他人权限
	//那么这个33204我们怎么知道代表的是啥呢? 在接下来的知识点会说明



	cout<<buf.st_atim.tv_sec<<endl; //(long int)
	cout << buf.st_atim.tv_nsec << endl;;//long int
	cout << buf.st_blksize << endl;;//long int
	cout << buf.st_blocks << endl;;//long int
	cout << buf.st_ctim.tv_nsec << endl;;//long int
	cout << buf.st_ctim.tv_sec << endl;;//long int
	cout << buf.st_dev << endl;;//unsigned long int
	cout << buf.st_gid << endl;;//unsigned int
	cout << buf.st_ino << endl;;//unsigned long int
	cout << buf.st_mode << endl;;//unsigned int
	cout << buf.st_mtim.tv_nsec << endl;;//long int
	cout << buf.st_mtim.tv_sec << endl;;//long int
	cout << buf.st_nlink << endl;;//unsigned long int
	cout << buf.st_rdev << endl;;//unsigned long int
	cout << buf.st_size << endl;;//long int
	cout << buf.st_uid << endl;;//unsigned int

	return 0;
}

上述遗留的问题,mode 是 文件类型,文件所有者权限,文件所有组权限,其他人权限,上述例子得到的是33204,那么怎么从这个mode中获得我们想要的结果呢?

man 2 statu 中已经给出了方案,参考:

方案一

    if ((buf.st_mode & S_IFMT) == S_IFREG) {
        //意思就是一般文件
        cout << "此文件类型为一般文件" << endl;
    }
    if ((buf.st_mode & S_IFMT) == S_IFDIR) {
        //意思就是一般文件
        cout << "此文件类型为文件夹" << endl;
    }

方案二

if (S_ISREG(m)) {
        cout << " is it a regular file" << endl;//普通文件
    }


       The following mask values are defined for the file type of the st_mode field:

           S_IFMT     0170000   bit mask for the file type bit field

           S_IFSOCK   0140000   socket
           S_IFLNK    0120000   symbolic link
           S_IFREG    0100000   regular file
           S_IFBLK    0060000   block device
           S_IFDIR    0040000   directory
           S_IFCHR    0020000   character device
           S_IFIFO    0010000   FIFO

       Thus, to test for a regular file (for example), one could write:

           stat(pathname, &sb);
           if ((sb.st_mode & S_IFMT) == S_IFREG) {
               /* Handle regular file */
           }



Because tests of the above form are common, additional macros are defined by POSIX to allow the test of the file type in st_mode to be written more concisely:

           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.)

       The preceding code snippet could thus be rewritten as:

           stat(pathname, &sb);
           if (S_ISREG(sb.st_mode)) {
               /* Handle regular file */
           }

S_ISLNK(st_mode):是否是一个连接.
S_ISREG(st_mode):是否是一个常规文件.
S_ISDIR(st_mode):是否是一个目录
S_ISCHR(st_mode):是否是一个字符设备.
S_ISBLK(st_mode):是否是一个块设备
S_ISFIFO(st_mode):是否 是一个FIFO文件.
S_ISSOCK(st_mode):是否是一个SOCKET文件

int main() {
	//const char * filepath = "/home/hunandede/projects/linuxcpp/main.cpp";
	const char * filepath = "/home/hunandede/projects/linuxcpp/aaa";
	struct stat buf ;
	int ret = stat(filepath, &buf);
	if (ret == -1) {
		perror("stat /home/hunandede/projects/linuxcpp/main.cpp has been error ->");
		exit(ret);
	}
	//重要参数st_size 或者文件的大小
	cout << "buf.size = " << buf.st_size << endl;;//long int  7696 ,然后我们使用ls -l查看,文件大小是一样的
	cout << "buf.st_mode = " << buf.st_mode << endl;;//long int 33204 这个33204包含了文件类型,文件所有者权限,文件所有组权限,其他人权限
	//那么这个33204我们怎么知道代表的是啥呢? 在接下来的知识点会说明

	if ((buf.st_mode & S_IFMT) == S_IFREG) {
		//意思就是一般文件
		cout << "此文件类型为一般文件" << endl;
	}
	if ((buf.st_mode & S_IFMT) == S_IFDIR) {
		//意思就是一般文件
		cout << "此文件类型为文件夹" << endl;
	}
	__mode_t m = buf.st_mode;
	if (S_ISREG(m)) {
		cout << " is it a regular file" << endl;//普通文件
	}
	if (S_ISDIR(m)) {
		cout << " is it a directory file" << endl;//文件夹
	}
	if (S_ISCHR(m)) {
		cout << " is it a character device file" << endl;//字符设备
	}
	if (S_ISBLK(m)) {
		cout << " is it a S_ISBLK device file" << endl;//块设备
	}
	if (S_ISFIFO(m)) {
		cout << " is it a  FIFO(named pipe) file" << endl;//FIFO 文件
	}
	if (S_ISLNK(m)) {
		cout << " is it a  symbolic link file" << endl; //软链接
	}
	if (S_ISSOCK(m)) {
		cout << " is it a  symbolic link file" << endl; //是否是一个socket文件
	}



	cout<<buf.st_atim.tv_sec<<endl; //(long int)
	cout << buf.st_atim.tv_nsec << endl;;//long int
	cout << buf.st_blksize << endl;;//long int
	cout << buf.st_blocks << endl;;//long int
	cout << buf.st_ctim.tv_nsec << endl;;//long int
	cout << buf.st_ctim.tv_sec << endl;;//long int
	cout << buf.st_dev << endl;;//unsigned long int
	cout << buf.st_gid << endl;;//unsigned int
	cout << buf.st_ino << endl;;//unsigned long int
	cout << buf.st_mode << endl;;//unsigned int
	cout << buf.st_mtim.tv_nsec << endl;;//long int
	cout << buf.st_mtim.tv_sec << endl;;//long int
	cout << buf.st_nlink << endl;;//unsigned long int
	cout << buf.st_rdev << endl;;//unsigned long int
	cout << buf.st_size << endl;;//long int
	cout << buf.st_uid << endl;;//unsigned int

	return 0;
}

存在的问题:

我们发现如果是一个软链接,那么不会打印出来 is it a  symbolic link file

    if (S_ISLNK(m)) {
        cout << " is it a  symbolic link file" << endl; //软链接
    }

这是因为stat函数有软链接穿透的功能,会指向软链接真正指向的那个文件类型

fix 方案 使用lstat 函数

该函数和stat 函数一样,唯一的不同是:对于软连接的穿透效果会

三、access函数

测试指定文件是否存在/拥有某种权限。

int access(const char *pathname,  int mode); 成功/具备该权限:0;失败/不具备 -1 设置errno为相应值。

参数2:R_OK、W_OK、X_OK

通常使用access函数来测试某个文件是否存在。F_OK

       access() checks whether the calling process can access the file pathname.  If pathname is a symbolic link, it is dereferenced.

       The  mode  specifies the accessibility check(s) to be performed, and is either the value F_OK, or a mask consisting of the bitwise OR of one or more of R_OK, W_OK, and
       X_OK.  F_OK tests for the existence of the file.  R_OK, W_OK, and X_OK test whether the file exists and grants read, write, and execute permissions, respectively.

四 chmod函数

修改文件的访问权限

int chmod(const char *path, mode_t mode); 成功:0;失败:-1设置errno为相应值

int fchmod(int fd, mode_t mode);

       The chmod() and fchmod() system calls change the permissions of a file.  They differ only in how the file is specified:

       * chmod() changes the permissions of the file specified whose pathname is given in pathname, which is dereferenced if it is a symbolic link.

       * fchmod() changes the permissions of the file referred to by the open file descriptor fd.

       The new file permissions are specified in mode, which is a bit mask created by ORing together zero or more of the following:

       S_ISUID  (04000)  set-user-ID (set process effective user ID on execve(2))

       S_ISGID  (02000)  set-group-ID  (set  process  effective group ID on execve(2); mandatory locking, as described in fcntl(2); take a new file's group from parent direc‐
                         tory, as described in chown(2) and mkdir(2))

       S_ISVTX  (01000)  sticky bit (restricted deletion flag, as described in unlink(2))

       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

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
 

五 truncate 函数

改变文件的大小

如果length比文件大,则文件扩展为length大小

如果length比文件下,则文件会被截断为length大小

       #include <unistd.h>
       #include <sys/types.h>

       int truncate(const char *path, off_t length);
       int ftruncate(int fd, off_t length);
 

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
 

六 link函数

思考,为什么目录项要游离于inode之外,画蛇添足般的将文件名单独存储呢??这样的存储方式有什么样的好处呢?

其目的是为了实现文件共享。Linux允许多个目录项共享一个inode,即共享盘块(data)。不同文件名,在人类眼中将它理解成两个文件,但是在内核眼里是同一个文件。

link函数,可以为已经存在的文件创建目录项(硬链接)。

int link(const char *oldpath,  const char *newpath); 成功:0;失败:-1设置errno为相应值

注意:由于两个参数可以使用“相对/绝对路径+文件名”的方式来指定,所以易出错。

如:link("../abc/a.c", "../ioc/b.c")若a.c,b.c都对, 但abc,ioc目录不存在也会失败。

mv命令既是修改了目录项,而并不修改文件本身。

七 unlink函数

 删除一个文件的目录项;

int unlink(const char *pathname); 成功:0;失败:-1设置errno为相应值

练习:编程实现mv命令的改名操作 【imp_mv.c】

注意Linux下删除文件的机制:不断将st_nlink -1,直至减到0为止。无目录项对应的文件,将会被操作系统择机释放。(具体时间由系统内部调度算法决定)

因此,我们删除文件,从某种意义上说,只是让文件具备了被释放的条件。

unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放。要等到所有打开该文件的进程关闭该文件,系统才会挑时间将该文件释放掉。 【unlink_exe.c】

上面关于link 和 unlink 的一个应用是:

假设我们要做mv a.txt b.txt 的工作

那么理论上核心代码是这样了

link("a.txt","b.txt");

unlink("a.txt");

关于unlink函数的特征的原文是:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放。要等到所有打开该文件的进程关闭该文件,系统才会挑时间将该文件释放掉。

代码解释如下:

八 隐式回收

当进程结束运行时,所有该进程打开的文件会被关闭申请的内存空间会被释放。系统的这一特性称之为隐式回收系统资源。

  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值