inode译成中文就是索引节点,它用来存放档案及目录的基本信息,包含时间、档名、使用者及群组等。

static int eachreg_open(struct inode *inode, struct file *file)
{file->private_data = inode->i_private;return 0;
}static ssize_t eachreg_write(struct file *file, const char __user *ubuf,size_t count, loff_t *ppos)
{...}static ssize_t eachreg_read(struct file *file, char __user *ubuf,size_t count, loff_t *ppos)
{...}

上面寄存器操作遇到inode结构体,看看下面资料。

先看一个驱动模型:

简单的debugfs模型


#include <linux/init.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm-generic/uaccess.h>static char abc_str[32] =
{ };
static char blob_inof[32] = "dragon blob wrapper\n";
static struct debugfs_blob_wrapper blob;
static u32 my_u32;
static u32 my_x32;
static u16 my_x16;
static u16 my_u16;static struct dentry *dragon_root;
static struct dentry *dragon_sub;static int abc_open(struct inode * inode, struct file * file)
{
file->private_data = inode->i_private;
return 0;
}static ssize_t abc_read(struct file * file, char __user * buf, size_t size,
loff_t * offset)
{
printk("###dragon### abc_read size=%ld\n", size);
if (*offset >= 32)
{
return 0;
}
if (*offset + size > 32)
{
size = 32 - *offset;
}if (copy_to_user(buf, abc_str + *offset, size))
{
return -EFAULT;
}*offset += size;
return size;
}static ssize_t abc_write(struct file * file, char __user * buf, size_t size,
loff_t * offset)
{
printk("###dragon### abc_write size=%ld\n", size);if (*offset >= 32)
{
return 0;
}
if (*offset + size > 32)
{
size = 32 - *offset;
}if (copy_from_user(abc_str + *offset, buf, size))
{
return -EFAULT;
}*offset += size;
return size;
}
struct file_operations abc =
{ .owner = THIS_MODULE, .open = abc_open, .read = abc_read, .write =
abc_write, };static int hello_init(void)
{
printk("###dragon### %s\n", __FUNCTION__);//在根目录中创建dragon_debug文件夹
dragon_root = debugfs_create_dir("dragon_debug", NULL);
if (IS_ERR(dragon_root))
{
printk("DEBUGFS DIR create failed\n");
return -1;
}dragon_sub = debugfs_create_dir("sub", dragon_root);
if (IS_ERR(dragon_sub))
{
debugfs_remove_recursive(dragon_root);
printk("DEBUGFS DIR create failed\n");
return -1;
}//注册任意访问文件
struct dentry * entry = debugfs_create_file("abc", 0644, dragon_root, NULL,
&abc);
if (IS_ERR(entry))
{
debugfs_remove_recursive(dragon_root);
dragon_root = NULL;
printk("###dragon### abc create error\n");
return -1;
}//注册只读字符串
blob.data = blob_inof;
blob.size = strlen(blob_inof);
debugfs_create_blob("blob", 0644, dragon_root, &blob);//注册直接访问读写的整形
debugfs_create_u32("u32", 0644, dragon_root, &my_u32);
debugfs_create_u32("x32", 0644, dragon_root, &my_x32);
debugfs_create_u32("x16", 0644, dragon_root, &my_x16);
debugfs_create_u32("u16", 0644, dragon_root, &my_u16);
return 0;
}static void hello_exit(void)
{
printk("###dragon### %s\r\n", __FUNCTION__);
debugfs_remove_recursive(dragon_root);
}MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);

#include <linux/init.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm-generic/uaccess.h>

static char abc_str[32] =
{ };
static char blob_inof[32] = "dragon blob wrapper\n";
static struct debugfs_blob_wrapper blob;
static u32 my_u32;
static u32 my_x32;
static u16 my_x16;
static u16 my_u16;

static struct dentry *dragon_root;
static struct dentry *dragon_sub;

static int abc_open(struct inode * inode, struct file * file)
{
file->private_data = inode->i_private;
return 0;
}

static ssize_t abc_read(struct file * file, char __user * buf, size_t size,
loff_t * offset)
{
printk("###dragon### abc_read size=%ld\n", size);
if (*offset >= 32)
{
return 0;
}
if (*offset + size > 32)
{
size = 32 - *offset;
}

if (copy_to_user(buf, abc_str + *offset, size))
{
return -EFAULT;
}

*offset += size;
return size;
}

static ssize_t abc_write(struct file * file, char __user * buf, size_t size,
loff_t * offset)
{
printk("###dragon### abc_write size=%ld\n", size);

if (*offset >= 32)
{
return 0;
}
if (*offset + size > 32)
{
size = 32 - *offset;
}

if (copy_from_user(abc_str + *offset, buf, size))
{
return -EFAULT;
}

*offset += size;
return size;
}
struct file_operations abc =
{ .owner = THIS_MODULE, .open = abc_open, .read = abc_read, .write =
abc_write, };

static int hello_init(void)
{
printk("###dragon### %s\n", __FUNCTION__);

//在根目录中创建dragon_debug文件夹
dragon_root = debugfs_create_dir("dragon_debug", NULL);
if (IS_ERR(dragon_root))
{
printk("DEBUGFS DIR create failed\n");
return -1;
}

dragon_sub = debugfs_create_dir("sub", dragon_root);
if (IS_ERR(dragon_sub))
{
debugfs_remove_recursive(dragon_root);
printk("DEBUGFS DIR create failed\n");
return -1;
}

//注册任意访问文件
struct dentry * entry = debugfs_create_file("abc", 0644, dragon_root, NULL,
&abc);
if (IS_ERR(entry))
{
debugfs_remove_recursive(dragon_root);
dragon_root = NULL;
printk("###dragon### abc create error\n");
return -1;
}

//注册只读字符串
blob.data = blob_inof;
blob.size = strlen(blob_inof);
debugfs_create_blob("blob", 0644, dragon_root, &blob);

//注册直接访问读写的整形
debugfs_create_u32("u32", 0644, dragon_root, &my_u32);
debugfs_create_u32("x32", 0644, dragon_root, &my_x32);
debugfs_create_u32("x16", 0644, dragon_root, &my_x16);
debugfs_create_u32("u16", 0644, dragon_root, &my_u16);
return 0;
}

static void hello_exit(void)
{
printk("###dragon### %s\r\n", __FUNCTION__);
debugfs_remove_recursive(dragon_root);
}

MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);

简介

inode 是 UNIX 操作系统中的一种数据结构,其本质是结构体,它包含了与文件系统中各个文件相关的一些重要信息。在 UNIX 中创建文件系统时,同时将会创建大量的 inode 。通常,文件系统磁盘空间中大约百分之一空间分配给了 inode 表。

有时,人们使用了一些不同的术语,如 inode索引编号 (inumber)。这两个术语非常相似,并且相互关联,但它们所指的并不是同样的概念。inode 指的是数据结构;而索引编号实际上是 inode 的标识编号,因此也称其为inode 编号 或者索引编号。索引编号只是文件相关信息中一项重要的内容。下一个部分将介绍 inode 中的其他一些属性。

inode 表包含一份清单,其中列出了对应文件系统的所有 inode 编号。当用户搜索或者访问一个文件时,UNIX 系统通过 inode 表查找正确的 inode 编号。在找到 inode 编号之后,相关的命令才可以访问该 inode ,并对其进行适当的更改。

例如,使用 vi 来编辑一个文件。当您键入 vi 时,在 inode 表中找到 inode 编号之后,才允许您打开该 inode 。在 vi 的编辑会话期间,更改了该 inode 中的某些属性,当您完成操作并键入 :wq 时,将关闭并释放该 inode 。通过这种方式,如果两个用户试图对同一个文件进行编辑, inode 已经在第一个编辑会话期间分配给了另一个用户 ID (UID),因此第二个编辑任务就必须等待,直到该 inode 释放为止。

结构

对于经验丰富的 UNIX 开发人员或者管理员来说, inode 的结构相对比较简单,但是可能还有一些您尚不了解的、令人惊讶的有关 inode 的内幕。下面的定义仅给出了 inode 中所包含的、UNIX 用户经常使用的一些重要信息:

● inode 编号

● 用来识别文件类型,以及用于 stat C 函数的模式信息

● 文件的链接数目

● 属主的ID (UID)

● 属主的组 ID (GID)

● 文件的大小

● 文件所使用的磁盘块的实际数目

● 最近一次修改的时间

● 最近一次访问的时间

● 最近一次更改的时间

从根本上讲, inode 中包含有关文件的所有信息(除了文件的实际名称以及实际数据内容之外)。可以在 AIX 的 Header 文件 /usr/include/jf/ino.h 中、或者 Web 页面 中可以找到完整的 inode 结构。

以上所列举的信息对于文件来说非常重要,并且在 UNIX 中频繁使用。如果没有这些信息,那么文件将被认为遭到破坏和不可用。

与其他的操作系统相比,UNIX 系统中的目录和文件可能看起来有所不同,但事实并非如此。在 UNIX 中,目录本身就是文件,只是在它们的 inode 中使用了一些附加的设置。目录 本质上就是一个包含了其他文件的文件。另外,其模式信息中设置了一些相应的标志,以告知系统该文件实际上是一个目录。

使用

了解如何在 UNIX 中使用 inode 可以节约大量的时间,并提高工作效率。在尚未了解 inode 之前,您可以使用下面的命令,以减少可能碰到的问题。

df 命令

如前所述,当您在 UNIX 中创建一个文件系统时,将为 inode 表分配大约百分之一的总磁盘空间。每次在文件系统中创建一个文件时,都会为该文件分配一个 inode 。通常,与一个文件系统相关联的 inode 的数目足够多,但耗尽 inode 的可能性始终存在。要监视是否发生了这种情况,您可以观察 df 的输出。

使用 df 命令,您可以查看所有已挂载的文件系统或者特定的文件系统。在该命令的输出中,您可以查看各个文件系统中已使用的 inode 的数目,以及文件系统中总体使用情况百分比,如清单 1 中所示。

清单 1. 使用 df 来监视 inode 的使用

# df -i

Filesystem Inodes IUsed IFree IUse% Mounted on

/dev/sda2 1036320 147459 888861 15% /

tmpfs 254955 6 254949 1% /dev/shm

/dev/sda1 76912 38 76874 1% /boot

如果由于某种原因,某个文件系统 inode 的使用率达到百分之百,那么您将无法在该文件系统中创建更多的文件、设备、目录等等。对于这种情况,一种解决方案是通过 smitty chfs 命令为该文件系统添加更多的空间,如图 1 所示。另一种解决方案是创建较小的 inode 区段。现在,在增强的日志文件系统 (Enhanced Journal File System) 中,IBM AIX 5L 允许 inode 区段小于 16KB 的缺省大小。请记住,如果您在 AIX 5L 中使用这个选项,那么将无法从较早版本的 AIX 访问该文件系统。

istat 和 stat

在 AIX 中检查 inode 的一种快捷的方式是使用 istat 命令。使用这个命令,您可以找到特定文件的索引编号,以及其他的 inode 项目,如权限、文件类型、UID、GID、链接的数目(非符号链接)、文件大小和最近一次更新、最近一次修改以及最近一次访问的时间戳。

清单 2 显示了 AIX 中文件 /usr/bin/ksh 的 inode 信息。

清单 2. /usr/bin/ksh 的 inode 信息

# istat /usr/bin/ksh

Inode 18150 on device 10/8 File

Protection: r-xr-xr-x

Owner: 2(bin) Group: 2(bin)

Link count: 5 Length 237804 bytes

Last updated: Wed Oct 24 17:37:10 EDT 2007

Last modified: Wed Apr 18 23:58:06 EDT 2007

Last accessed: Mon Apr 28 11:25:35 EDT 2008

除了显示来自 istat 的标准信息之外,现在您还知道了 /usr/bin/ksh 对应的索引编号。如果您同时还找到了该文件所处的逻辑卷,那么甚至可以显示更多的信息。要查找该信息,一种方式是通过使用 df 命令来查看该文件位于哪个已挂载的文件系统中:

# df /usr/binFilesystem 512-blocks Free %Used Iused %Iused Mounted on/dev/hd2 16056320 1925384 89% 110034 33% /usr

文件 /usr/bin/ksh 位于目录 /usr/bin 中。查看 df 命令的输出,您可以发现,目录 /usr/bin 包含于 /usr 文件系统中,并且 /usr 文件系统位于逻辑卷 /dev/hd2 之中。现在,您已经知道了索引编号和逻辑卷的名称,那么就可以将这两个信息项作为参数来使用 istat,这样一来,您可以确定组成该文件的磁盘块的十六进制地址,如清单 3 中所示。

清单 3. 确定文件磁盘块的十六进制地址

# istat 18150 /dev/hd2

Inode 18150 on device 10/8 File

Protection: r-xr-xr-x

Owner: 2(bin) Group: 2(bin)

Link count: 5 Length 237804 bytes

Last updated: Wed Oct 24 17:37:10 EDT 2007

Last modified: Wed Apr 18 23:58:06 EDT 2007

Last accessed: Mon Apr 28 11:44:20 EDT 2008

Block pointers (hexadecimal):

11620 ef8c0

Linux 提供了其特有的 istat 版本:stat。Linux stat 命令可以显示类似的信息,并且还包括一些在 AIX istat 命令中没有提供的命令开关:

# stat /bin/bash File: `/bin/bash' Size: 722684 Blocks: 1432 IO Block: 4096 regular fileDevice: fd00h/64768d Inode: 12799859 Links: 1Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)Access: 2008-04-06 19:13:50.000000000 -0400Modify: 2006-07-12 03:11:53.000000000 -0400Change: 2007-11-22 04:05:30.000000000 -0500

ls 命令

在您的日常工作中总会碰到这样的情况,难以删除或者管理某些文件,因为这些文件的文件名中使用了短横线或者其他特殊字符、或者其文件名完全不正确。这很可能是有人对该文件进行了错误命名。

因为 UNIX 中的大多数命令,包括开关或者选项在内,都是以连字符 (-) 或者双连字符 (--) 开头的,很难使用诸如 rm、mv 和 cp 之类常用的命令来操作这些文件。幸运的是,某些命令提供了一些选项,以用来显示相关文件所关联的 inode 的索引编号。ls 命令就提供了一个这样的选项:

# ls - -- -p fileA fileB fileC fileDfileE fileF fileG fileH fileI fileJ fileK fileL

使用 ls -i 命令,您可以看到文件名称旁边的索引编号,如清单 4 中所示。现在,您已经知道了文件的索引编号,那么就可以很容易地操作该文件了。

清单 4. 查看文件的索引编号

# ls –i

38988 38991 -p 38984 fileC 38982 fileF 38977 fileI 38978 fileL

38989 - 38980 fileA 38986 fileD 38983 fileG 38987 fileJ

38990 -- 38979 fileB 38976 fileE 38985 fileH 38981 fileK

find 命令

使用 UNIX find 命令,您可以完成使用 ls 命令所开始的工作。对于要进行操作的文件,您已经知道了它们的索引编号,那么就可以开始进行相应的操作了!

要删除看似无名的文件,您只需要使用 find 和 -inum 开关对索引编号和文件进行定位。然后,在找到该文件之后,使用 find 和 -exec 开关删除该文件:

# find . -inum 38988 -exec rm {} \;

要对该文件进行重命名,可以再次进行相同的操作,但这一次使用 mv 而不是 rm:

# find . -inum 38989 -exec mv {} fileM \;

为了验证取得了预期的结果,只需要再次使用 ls -i 命令:

# ls -i38990 -- 38979 fileB 38976 fileE 38985 fileH 38981 fileK38991 -p 38984 fileC 38982 fileF 38977 fileI 38978 fileL38980 fileA 38986 fileD 38983 fileG 38987 fileJ 38989 fileM

fsck 命令

不幸的是,硬件设备不可能一直使用下去,系统可能会在使用多年后出现故障。当发生这种情况,以及由于电源故障或者某些其他问题而导致操作系统异常关闭的时候,您可能会在还原系统备份时碰到一些在崩溃期间处于打开状态的文件,并且现在需要对其加以处理。此时,您可能会碰到一些需要修复 inode 或者存在错误的消息。如果发生这种状况,那么 fsck 命令可以用来救急!您可以使用 fsck 来修复文件系统或者修正受损的 inode ,而不是还原系统、或者甚至重新构建操作系统。

下面的命令可以尝试修复逻辑卷 /dev/hd1:

# fsck –p /dev/hd1 –y

通过使用 fsck 命令,您还可以缩小受损 inode 的搜索范围。如果您正在搜索一个特定的 inode ,那么可以使用带 -ii-NodeNumber 开关的 fsck 命令。

意义

如果没有 inode ,那么 UNIX 中的文件和目录将根本无法使用。希望在阅读完本文之后,您可以更好地了解 inode 、它们对于 AIX 系统的重要性,以及如何管理它们。您可能会对 df 命令的看法大为改观。






剖析Linux内核文件系统之(inode)

一、inode是什么?

理解inode,要从文件储存说起。

文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。

操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个”块”(block)。这种由多个扇区组成的”块”,是文件存取的最小单位。”块”的大小,最常见的是4KB,即连续八个 sector组成一个 block。

文件数据都储存在”块”中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。

每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。

二、inode的内容

inode包含文件的元信息,具体来说有以下内容:

* 文件的字节数

* 文件拥有者的User ID

* 文件的Group ID

* 文件的读、写、执行权限

* 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。

* 链接数,即有多少文件名指向这个inode

* 文件数据block的位置

可以用stat命令,查看某个文件的inode信息:

总之,除了文件名以外的所有文件信息,都存在inode之中。至于为什么没有文件名,下文会有详细解释。

三、inode的大小

inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。

每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。

查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。

查看每个inode节点的大小,可以用如下命令:

sudo dumpe2fs -h /dev/sda1 | grep "Inode size"

由于每个文件都必须有一个inode,因此有可能发生inode已经用光,但是硬盘还未存满的情况。这时,就无法在硬盘上创建新文件。

四、inode号码

每个inode都有一个号码,操作系统用inode号码来识别不同的文件。

这里值得重复一遍,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。

表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

使用ls -i命令,可以看到文件名对应的inode号码:

五、目录文件

Unix/Linux系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。

目录文件的结构非常简单,就是一系列目录项(dirent)的列表。每个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码。

ls命令只列出目录文件中的所有文件名:

ls /etc/

ls -i命令列出整个目录文件,即文件名和inode号码:

ls -i /etc/

如果要查看文件的详细信息,就必须根据inode号码,访问inode节点,读取信息。ls -l命令列出文件的详细信息。

ls -l /etc/

六、硬链接

一般情况下,文件名和inode号码是”一一对应”关系,每个inode号码对应一个文件名。但是,Unix/Linux系统允许,多个文件名指向同一个inode号码。

这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为”硬链接”(hard link)。

ln命令可以创建硬链接:

ln 源文件 目标文件

运行上面这条命令以后,源文件与目标文件的inode号码相同,都指向同一个inode。inode信息中有一项叫做”链接数”,记录指向该inode的文件名总数,这时就会增加1。

反过来,删除一个文件名,就会使得inode节点中的”链接数”减1。当这个值减到0,表明没有文件名指向这个inode,系统就会回收这个inode号码,以及其所对应block区域。

这里顺便说一下目录文件的”链接数”。创建目录时,默认会生成两个目录项:”.”和”..”。前者的inode号码就是当前目录的inode号码,等同于当前目录的”硬链接”;后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的”硬链接”。所以,任何一个目录的”硬链接”总数,总是等于2加上它的子目录总数(含隐藏目录)。

七、软链接

除了硬链接以外,还有一种特殊情况。

文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的”软链接”(soft link)或者”符号链接(symbolic link)。

这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:”No such file or directory”。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode”链接数”不会因此发生变化。

ln -s命令可以创建软链接。

ln -s 源文文件或目录 目标文件或目录

八、inode的特殊作用

由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象。

  1. 有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用。

  2. 移动文件或重命名文件,只是改变文件名,不影响inode号码。

  3. 打开一个文件以后,系统就以inode号码来识别这个文件,不再考虑文件名。因此,通常来说,系统无法从inode号码得知文件名。

第3点使得软件更新变得简单,可以在不关闭软件的情况下进行更新,不需要重启。因为系统通过inode号码,识别运行中的文件,不通过文件名。更新的时候,新版文件以同样的文件名,生成一个新的inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向新版文件,旧版文件的inode则被回收。

九、拓展软硬链接

1、硬链接

硬链接是通过索引节点进行的链接。在Linux中,多个文件指向同一个索引节点是允许的,像这样的链接就是硬链接。硬链接只能在同一文件系统中的文件之间进行链接,不能对目录进行创建。如果删除硬链接对应的源文件,则硬链接文件仍然存在,而且保存了原有的内容,这样可以起到防止因为误操作而错误删除文件的作用。由于硬链接是有着相同 inode 号仅文件名不同的文件,因此,删除一个硬链接文件并不影响其他有相同 inode 号的文件。

2、软链接

软链接(也叫符号链接)与硬链接不同,文件用户数据块中存放的内容是另一文件的路径名的指向。软链接就是一个普通文件,只是数据块内容有点特殊。软链接可对文件或目录创建。

软链接主要应用于以下两个方面:一是方便管理,例如可以把一个复杂路径下的文件链接到一个简单路径下方便用户访问;另一方面就是解决文件系统磁盘空间不足的情况。例如某个文件文件系统空间已经用完了,但是现在必须在该文件系统下创建一个新的目录并存储大量的文件,那么可以把另一个剩余空间较多的文件系统中的目录链接到该文件系统中,这样就可以很好的解决空间不足问题。删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接就变成了死链接。






理解Linux的文件描述符FD与Inode

FD 文件描述符

一、概念

  Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。

二、文件描述符、文件、进程间的关系

1.描述:

我们可以通过linux的几个基本的I/O操作函数来理解什么是文件操作符。

fd = open(pathname, flags, mode)
// 返回了该文件的fd
rlen = read(fd, buf, count)
// IO操作均需要传入该文件的fd值
wlen = write(fd, buf, count)
status = close(fd)

每当进程用open()函数打开一个文件,内核便会返回该文件的文件操作符(一个非负的整形值),此后所有对该文件的操作,都会以返回的fd文件操作符为参数。【注1】

注1: 文件描述符可以理解为进程文件描述表这个表的 索引,或者把文件描述表看做一个数组的话,文件描述符可以看做是数组的下标。当需要进行I/O操作的时候,会传入fd作为参数,先从进程文件描述符表查找该fd对应的那个条目,取出对应的那个已经打开的文件的 句柄,根据文件句柄指向,去系统fd表中查找到该文件指向的 inode,从而定位到该文件的真正位置,从而进行I/O操作。

  • 每个文件描述符会与一个打开的文件相对应
  • 不同的文件描述符也可能指向同一个文件
  • 相同的文件可以被不同的进程打开,也可以在同一个进程被多次打开

2.系统为维护文件描述符,建立了三个表

  • 进程级的文件描述符表
  • 系统级的文件描述符表
  • 文件系统的i-node表 (inode 见下文)

进程级别的文件描述表:

linux内核会为每一个进程创建一个task_truct结构体来维护进程信息,称之为 进程描述符,该结构体中 指针

struct files_struct *files

指向一个名称为file_struct的结构体,该结构体即 进程级别的文件描述表。

它的每一个条目记录的是单个文件描述符的相关信息

  1. fd控制标志,前内核仅定义了一个,即close-on-exec
  2. 文件描述符所打开的文件句柄的引用【注2】

[注释2]:文件句柄这里可以理解为文件名,或者文件的全路径名,因为linux文件系统文件名和文件是独立的,以此与inode区分

系统级别的文件描述符表

内核对系统中所有打开的文件维护了一个描述符表,也被称之为 【打开文件表】,表格中的每一项被称之为 【打开文件句柄】,一个【打开文件句柄】 描述了一个打开文件的全部信息。
主要包括:

  1. 当前文件偏移量(调用read()和write()时更新,或使用lseek()直接修改)
  2. 打开文件时所使用的状态标识(即,open()的flags参数)
  3. 文件访问模式(如调用open()时所设置的只读模式、只写模式或读写模式)
  4. 与信号驱动相关的设置
  5. 对该文件i-node对象的引用
  6. 文件类型(例如:常规文件、套接字或FIFO)和访问权限
  7. 一个指针,指向该文件所持有的锁列表
  8. 文件的各种属性,包括文件大小以及与不同类型操作相关的时间戳

Inode表

每个文件系统会为存储于其上的所有文件(包括目录)维护一个i-node表,单个i-node包含以下信息:

  1. 文件类型(file type),可以是常规文件、目录、套接字或FIFO
  2. 访问权限
  3. 文件锁列表(file locks)
  4. 文件大小
    等等
    i-node存储在磁盘设备上,内核在内存中维护了一个副本,这里的i-node表为后者。副本除了原有信息,还包括:引用计数(从打开文件描述体)、所在设备号以及一些临时属性,例如文件锁。

注:进程A的fd表中,左边fd0,fd1,fd2… 就是各个文件描述符,它是fd表的索引,fd不是表里那个fd flags!这里不要搞混淆了,fd flags 目前只有一个取值。

在进程A中,文件描述符1和30都指向了同一个打开的文件句柄(标号23)。这可能是通过调用dup()、dup2()、fcntl()或者对同一个文件多次调用了open()函数而形成的。
dup(),也称之为文件描述符复制函数,在某些场景下非常有用,比如:标准输入/输出重定向。在shell下,完成这个操作非常简单,大部分人都会,但是极少人思考过背后的原理。

大概描述一下需要的几个步骤,以标准输出(文件描述符为1)重定向为例:

  1. 打开目标文件,返回文件描述符n;
  2. 关闭文件描述符1;
  3. 调用dup将文件描述符n复制到1;
  4. 关闭文件描述符n;

进程A的文件描述符2和进程B的文件描述符2都指向了同一个打开的文件句柄(标号73)。这种情形可能是在调用fork()后出现的(即,进程A、B是父子进程关系)【注3】,或者当某进程通过UNIX域套接字将一个打开的文件描述符传递给另一个进程时,也会发生。再者是不同的进程独自去调用open函数打开了同一个文件,此时进程内部的描述符正好分配到与其他进程打开该文件的描述符一样。

注3: 子进程会继承父进程的文件描述符表,也就是子进程继承父进程打开的文件 这句话的由来。

此外,进程A的描述符0和进程B的描述符3分别指向不同的打开文件句柄,但这些句柄均指向i-node表的相同条目(1976),换言之,指向同一个文件。发生这种情况是因为每个进程各自对同一个文件发起了open()调用。同一个进程两次打开同一个文件,也会发生类似情况。

三、文件描述符限制

  有资源的地方就有战争,“文件描述符”也是一种资源,系统中的每个进程都需要有“文件描述符”才能进行改变世界的宏图霸业。世界需要秩序,于是就有了“文件描述符限制”的规定。

如下表:

永久修改用户级限制时有三种设置类型:

  • soft 指的是当前系统生效的设置值
  • hard 指的是系统中所能设定的最大值
  • “-” 指的是同时设置了 soft 和 hard 的值

Inode 文件节点

一、inode是什么?

理解inode,要从文件储存说起。

文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。

操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个”块”(block)。这种由多个扇区组成的”块”,是文件存取的最小单位。”块”的大小,最常见的是4KB,即连续八个 sector组成一个 block。

文件数据都储存在”块”中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。

每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。

二、inode的内容

inode包含文件的元信息,具体来说有以下内容:

  • 文件的字节数
  • 文件拥有者的User ID
  • 文件的Group ID
  • 文件的读、写、执行权限
  • 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
  • 链接数,即有多少文件名指向这个inode
  • 文件数据block的位置

可以用stat命令,查看某个文件的inode信息:
stat 233.txt

总之,除了文件名以外的所有文件信息,都存在inode之中。至于为什么没有文件名,下文会有详细解释。

三、inode的大小

inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。

每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。

查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。

df -i

查看每个inode节点的大小,可以用如下命令:

sudo dumpe2fs -h /dev/hda | grep "Inode size"

由于每个文件都必须有一个inode,因此有可能发生inode已经用光,但是硬盘还未存满的情况。这时,就无法在硬盘上创建新文件。

四、inode号码

每个inode都有一个号码,操作系统用inode号码来识别不同的文件。

这里值得重复一遍,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。

表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

使用ls -i命令,可以看到文件名对应的inode号码:

ls -i 233.txt

五、目录文件

Unix/Linux系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。

目录文件的结构非常简单,就是一系列目录项(dirent)的列表。每个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码。

ls命令只列出目录文件中的所有文件名:

ls /etc

ls -i命令列出整个目录文件,即文件名和inode号码:

ls -i /etc

如果要查看文件的详细信息,就必须根据inode号码,访问inode节点,读取信息。ls -l命令列出文件的详细信息。

ls -l /etc

理解了上面这些知识,就能理解目录的权限。目录文件的读权限(r)和写权限(w),都是针对目录文件本身。由于目录文件内只有文件名和inode号码,所以如果只有读权限,只能获取文件名,无法获取其他信息,因为其他信息都储存在inode节点中,而读取inode节点内的信息需要目录文件的执行权限(x)。

六、硬链接

一般情况下,文件名和inode号码是”一一对应”关系,每个inode号码对应一个文件名。但是,Unix/Linux系统允许,多个文件名指向同一个inode号码。

这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为”硬链接”(hard link)。

ln命令可以创建硬链接:

ln 源文件 目标文件

运行上面这条命令以后,源文件与目标文件的inode号码相同,都指向同一个inode。inode信息中有一项叫做”链接数”,记录指向该inode的文件名总数,这时就会增加1。

反过来,删除一个文件名,就会使得inode节点中的”链接数”减1。当这个值减到0,表明没有文件名指向这个inode,系统就会回收这个inode号码,以及其所对应block区域。

这里顺便说一下目录文件的”链接数”。创建目录时,默认会生成两个目录项:”.”和”..”。前者的inode号码就是当前目录的inode号码,等同于当前目录的”硬链接”;后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的”硬链接”。所以,任何一个目录的”硬链接”总数,总是等于2加上它的子目录总数(含隐藏目录)。

七、软链接

除了硬链接以外,还有一种特殊情况。

文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的”软链接”(soft link)或者”符号链接(symbolic link)。

这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:”No such file or directory”。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode”链接数”不会因此发生变化。

ln -s命令可以创建软链接

ln -s 源文文件或目录 目标文件或目录

八、inode的特殊作用

由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象。

  • 有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用。
  • 移动文件或重命名文件,只是改变文件名,不影响inode号码。
  • 打开一个文件以后,系统就以inode号码来识别这个文件,不再考虑文件名。因此,通常来说,系统无法从inode号码得知文件名。

第3点使得软件更新变得简单,可以在不关闭软件的情况下进行更新,不需要重启。因为系统通过inode号码,识别运行中的文件,不通过文件名。更新的时候,新版文件以同样的文件名,生成一个新的inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向新版文件,旧版文件的inode则被回收。

索引节点inode: Linux内核文件系统之(inode)相关推荐

  1. 【Linux】文件系统的inode是什么?软硬链接是什么?了解意义

    当我们在 LIiux 中输入 ll-i 指令时,会显示文件信息: 权限.拥有者.所属组等这些我都知道,但是最前面的比如 1051810 是什么,还有权限后面的3和6是什么呢?本章我们就来讲解一下 in ...

  2. Linux内核文件系统10

    2021SC@SDUSC inode.c(1) 今天来分析inode.c文件.有了前面对ext4_jbd2.h.acl.h等头文件的分析做基础,今天的分析将相对简单. 在看代码之前,首先要说一下ino ...

  3. Linux内核文件系统

    Linux 内核文件系统 概述 文件系统这一词在不同上下文时有不同的含义: 指一种具体的文件格式.例如Linux的文件系统是Ext2,MSDOS的文件系统是FAT16,而Windows NT的文件系统 ...

  4. linux内核文件系统的架构,《深入理解Linux内核》-文件系统学习心得

    内核中要注意的是各种结构体,结构体之间的联系和各个函数以及函数之间的调用关系,参数的传递和函数的功能. 内核中数据结构的字段无外乎包括三种字段:属性字段,调用方法,指向其它结构的指针.具体如下图所示: ...

  5. linux进入节点权限,一种基于索引节点的Linux访问权限控制方法与流程

    本发明涉及计算机领域,尤其涉及一种基于索引节点的Linux访问权限控制方法. 背景技术: 传统的Linux访问控制方式是DAC(Discretionary Access Control,自主访问控制) ...

  6. DM365 linux内核文件系统的烧写步骤及其uboot参数配置

    DM365 linux内核&文件系统的烧写步骤及其uboot参数配置     目录 源文档下载:http://download.csdn.net/detail/zhangjikuan/6443 ...

  7. 【技术分享篇】Linux内核——手把手带你实现一个Linux内核文件系统丨Linux内核源码分析

    手把手带你实现一个Linux内核文件系统 1. 内核文件系统架构分析 2. 行行珠玑,代码实现 [技术分享篇]Linux内核--手把手带你实现一个Linux内核文件系统丨Linux内核源码分析 更多L ...

  8. [OS-Linux]详解Linux的文件系统、inode和动静态库

    本文详解了Linux中的文件系统,包括inode.软连接,硬链接.动静态库. 目录 一.理解文件系统 二.inode 三.硬链接和软连接 1.硬链接 2.软连接 四.动态库和静态库 1. 静态库与动态 ...

  9. 【Linux】文件系统与inode、软硬链接

    目录 一.磁盘结构 二.文件系统 2.1 文件系统的区域划分 2.2 文件系统分区介绍 2.3 文件名与inode 三.软硬链接 3.1 软链接 3.2 硬链接 一.磁盘结构 理解文件系统前首先我们要 ...

最新文章

  1. 强化学习入门教程(附学习大纲)
  2. 【No.3 Ionic】超级逗表情 App
  3. HDU5470 Typewriter SAM 动态规划 单调队列
  4. python写抽奖转盘_[宜配屋]听图阁
  5. UWP 使用exe程序
  6. 1177: 按要求排序(指针专题)_数据结构 8 基础排序算法详解、快速排序的实现、了解分治法...
  7. day13【前台】搭建环境
  8. minnet sample
  9. 剑指 Offer 09. 用两个栈实现队列(day 03)
  10. 魔都上海为什么被称为“魔都”
  11. windows如何查看电脑开关机记录
  12. WDK10编译出最适合申请WHQL认证的驱动
  13. WinXP SP2发布以来的所有补丁集下载 0812(V1.3.0)[119M]
  14. 【LeGO-LOAM论文阅读(二)--特征提取(一)】
  15. SPRING IN ACTION 第4版笔记-第四章Aspect-oriented Spring-001-什么是AOP
  16. 内存与主板不兼容导致不定时蓝屏,查看日志
  17. struts2深入浅出(备java基础,javaweb,javaee,框架)-任亮-专题视频课程
  18. 史上最强悍的一张图,没有任何一个中国人能躲得开!
  19. 直播-动态礼物(豪华礼物)
  20. 【练习题】6道bootstrap 基础测试题

热门文章

  1. 无线蓝牙入耳式耳机哪个品牌好?入耳式无线蓝牙耳机排行
  2. 第十八届全国大学生智能车竞赛车模技术检查文档
  3. shellctf2022-writeup
  4. 12平键标准尺寸规格表_平键和键槽的标准尺寸规格表
  5. Vijava 学习笔记之模板
  6. 简单两步更换idea背景图片--超级简单
  7. 运动会分数统计 C语言
  8. 初次使用git,需要做什么,如何下载项目和提交项目
  9. 虚幻引擎(7)-持枪射击
  10. 爬了个爬(一)爬虫入门