Linux内核学习

本文主要目的是学习并归纳Linux操作系统中内核的几个基本重要知识

进程

  • 一个进程在地址空间中执行一个单独的指令序列

地址空间是允许进程引用的内存空间的集合,操作系统允许具有多个执行流的进程,即在相同的地址空间可执行多个指令序列。
进程能并发活动,竞争的系统资源主要是CPU
区分程序和进程:

  • 几个进程能并发执行同一个程序,同时,同一进程能顺序执行多个程序

现在的CPU处理器一般都是多核CPU,可通过任务管理器查看。而在单处理器系统上,只有一个进程能占用CPU。因此,在某时刻,只能有一个执行流。
CPU有多少个,就有多少个进程可同时执行。选择哪个进程执行由操作系统的调度程序决定,进程分为抢占式进程和非抢占式进程。
对于抢占式进程,只有进程自愿放弃CPU,调度程序才会被调用;
对于非抢占式进程,如多用户系统中的进程调度,操作系统记录下每个进程占优的CPU时间,并周期性地激活调度程序。


为了标识一个特定的文件,进程使用路径名,路径名由斜杠及一列指向文件的目录名交替组成。若路径名第一个字符是斜杠那么该路径就为绝对路径,因为其起点是根目录。若第一项是目录名或文件名,那么路径就是相对路径,因为其起点是进程的当前目录。


Linux文件与目录

操作系统为了解决信息能独立于进程之外被长期存储,而引入了文件。

  • 文件作为进程创建信息的逻辑单元,可被多个进程并发使用
    在Unix系统中,操作系统为磁盘上的文本与图像、鼠标与键盘等输入设备及网络交互等I/O操作设计了一组通用的API,使他们被处理时均可统一使用字节流。
  • Lniux系统中除进程外的一切都是文件,为了便于文件管理而引入目录,这使Linux文件系统形成了一个层级结构的目录树。

Linux系统顶层目录结构
/ 根目录
├── bin 存放用户二进制文件
├── boot 存放内核引导配置文件
├── dev 存放设备文件
├── etc 存放系统配置文件
├── home 用户主目录
├── lib 动态共享库
├── lost+found 文件系统恢复时的恢复文件
├── media 可卸载存储介质挂载点
├── mnt 文件系统临时挂载点
├── opt 附加的应用程序包
├── proc 系统内存的映射目录,提供内核与进程信息
├── root root 用户主目录
├── sbin 存放系统二进制文件
├── srv 存放服务相关数据
├── sys sys 虚拟文件系统挂载点
├── tmp 存放临时文件
├── usr 存放用户应用程序
└── var 存放邮件、系统日志等变化文件

  • Linux系统并不区分文件和目录,目录是记录其他文件名的文件
  • Linux将设备当做文件处理

打开并读取设备文件

1
2
3
4
5
6
7
int fd;
struct input_event ie;
fd = open("/dev/input/event3", O_RDONLY);
read(fd, &ie, sizeof(struct input_event));
printf("type = %d code = %d value = %d\n",
ie.type, ie.code, ie.value);
close(fd);

其展示了如何打开设备文件event3并读取文件内容,文件event3表示一直输入设备,其可能是鼠标或键盘,通过查看 /proc/bus/input/devices 可知 event3 对应设备的类型。设备文件 /dev/input/event3 使用 read() 以字符流的方式被读取。结构体 input_event 被定义在内核头文件 linux/input.h 中。

  • 文件都有文件名和数据,在Linux上被分成两部分:用户数据与元数据
  • 用户数据:文件数据块(data block),数据块是记录文件内容的地方
  • 元数据:文件的附加属性,即文件大小、创建时间及所有者等信息。

在Linux中,元数据中的inode号为索引节点号,是文件的唯一标识。而文件名是为了方便人们记忆和使用,系统或程序是通过inode号来寻找正确的文件数据块。

  • 查看inode号使用stat或ls -i命令

硬链接&软链接

链接不仅解决了Linux系统文件的共享使用,而且带来了隐藏文件路径、增加权限安全及节省存储等好处。

  • 硬链接:一个inode号对应多个文件名
    文件A是文件B的硬链接,则A的目录项中的索引inode节点号与B的目录项中的inode节点相同,即同一个inode节点对应不同的文件名,两个文件名指向同一文件,A和B对于文件系统来说是完全等同的。

  • 特点

    文件有相同的 inode 及 data block
    只能对已存在的文件进行创建
    不能交叉文件系统进行硬链接的创建
    不能对目录进行创建,只可对文件创建
    删除一个硬链接文件并不影响其他有相同 inode 号的文件

1
2
3
4
5
6
7
8
9
$ touch test
$ echo "This is a test." > test
$ cat test
This is a test.
$ ln test filetxt
$ ls -li
total 8
809185 -rw-rw-r-- 2 wonderxiao wonderxiao 16 10月 13 20:42 filetxt
809185 -rw-rw-r-- 2 wonderxiao wonderxiao 16 10月 13 20:42 test

其中,809185是文件的inode值,你可简单将文件看作C语言的指针,它指向物理硬盘的一个区块。事实上,文件系统会维护一个引用计数,只要有文件指向这个区块,它就不会从硬盘上消失。

inode 是随着文件的存在而存在,因此只有当文件存在时才可创建硬链接,即当 inode 存在且链接计数器不为 0 时。

若修改刚才创建的filetxt文件:

1
2
3
4
$ echo "New one" >> filetxt
$ cat test
This is a test.
New one

可看到,这两个就如同一个文件一样,inode值一样,都指向同一个区块。

软链接——符号链接

文件用户数据块中存放的内容是另一文件的路径名的指向,则该文件就是软连接。软链接就是一个普通文件,只是数据块内容有点特殊。软链接有着自己的 inode 号以及用户数据块,因此软链接的创建与使用没有类似硬链接的诸多限制。

  • 特点

    软链接有自己的文件属性及权限等
    可对不存在的文件或目录创建软链接
    软链接可交叉文件系统
    软链接可对文件或目录创建
    创建软链接时,链接计数 i_nlink 不会增加
    删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接被称为死链接

链接的访问

链接的访问

1
2
3
4
5
6
$ ln -s filetxt softtxt
$ ls -li
total 8
809185 -rw-rw-r-- 2 wonderxiao wonderxiao 24 10月 13 21:11 filetxt
809068 lrwxrwxrwx 1 wonderxiao wonderxiao 7 10月 13 21:14 softtxt -> filetxt
809185 -rw-rw-r-- 2 wonderxiao wonderxiao 24 10月 13 21:11 test

这个软链接的inode值不一样了,且文件属性为1,说明刚才创建的软链接文件根本不是同一类型。

若删除filetxt文件,然后输出软硬链接文件内容:

1
2
3
4
$ rm filetxt
$ cat test
This is a test.
New one

1
2
$ cat softtxt
cat: softtxt: No such file or directory

之前硬链接没有任何影响,这是由于其inode所指向的区块有一个硬链接在指向它,所以此区块仍然有效,可继续访问。而软链接的inode所指向的内容实际上保存的是一个绝对路径,当用户访问这个文件时,即访问的是这个文件路径所在的文件。此时,这个文件已被删除,所以找不到它。

  • 软链接:保存其代表的绝对路径,是另外一个文件,在硬盘上有独立的块,访问时会替换自身路径。
  • 硬链接:代表的是文件的副本

    Linux VFS

    Linux有极其丰富的文件系统,可分为:
  • 网络文件系统,如nfs、cifs等
  • 磁盘文件系统,如ext4、ext3等
  • 特殊文件系统,如proc、sysfs、ramfs、tmpfs等

这些文件系统系统在Linux下共存的基础是Linux VFS(Virtual File System),VFS作为一个通用的文件系统,抽象定义了文件系统的四个基本概念:

  • 文件
  • 目录项
  • 索引节点
  • 挂载点

其在内核中,为用户空间层的文件系统提供了相关接口,即VFS实现了open()、read()等系统调用的函数,这就真正实现:在Linux系统中,除进程外一切都是文件。

VFS在系统中架构

VFS在系统架构

VFS存在四个基本对象:

  • 超级块对象(superblock object)
    一个已安装的文件系统
  • 索引节点对象(inode object)
    一个文件
  • 目录项对象(dentry object)
    一个目录项,如设备文件event3在路径/dev/input/event3中,存在四个目录项对象:/、dev/、input/及event3。
  • 文件对象(file object)
    代表进程打开的文件

这四个对象与进程及磁盘文件间的关系如下图:
对象与进程及磁盘文件间的关系

其中,d_inode为硬链接,是文件路径的快速解析。同时,Linux VFS设计了目录项缓存dcache。

在Linux中,索引节点结构存在于系统内存及磁盘中,其可分为VFS inode与实际文件系统的inode。VFS inode是作为实际文件系统中inode的抽象。

VFS中inode与inode_operations结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct inode {
...
const struct inode_operations *i_op; // 索引节点操作
unsigned long i_ino; // 索引节点号
atomic_t i_count; // 引用计数器
unsigned int i_nlink; // 硬链接数目
...
}
struct inode_operations {
...
int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);
int (*mkdir) (struct inode *,struct dentry *,int);
int (*rmdir) (struct inode *,struct dentry *);
...
}

每个文件存在两个计数器:

  • i_count:引用计数,用于跟踪文件被访问的数量,或者说 i_count 跟踪文件在内存中的情况
  • i_nlink:硬链接计数,使用 ls -l 等命令查看到的文件硬链接数,即是磁盘计数器。当文件被删除时,则 i_nlink 先被设置成 0。

文件的这两个计数器使得 Linux 系统升级或程序更新变的容易。系统或程序可在不关闭的情况下(即文件 i_count 不为 0),将新文件以同样的文件名进行替换,新文件有自己的 inode 及 data block,旧文件会在相关进程关闭后被完整的删除。

文件系统 ext4 中的 inode

1
2
3
4
5
6
7
8
9
10
struct ext4_inode {
...
__le32 i_atime; // 文件内容最后一次访问时间
__le32 i_ctime; // inode 修改时间
__le32 i_mtime; // 文件内容最后一次修改时间
__le16 i_links_count; // 硬链接计数
__le32 i_blocks_lo; // Block 计数
__le32 i_block[EXT4_N_BLOCKS]; // 指向具体的 block
...
};

其中三个时间的定义可对应与命令 stat 中查看到三个时间。i_links_count 不仅用于文件的硬链接计数,也用于目录的子目录数跟踪(目录并不显示硬链接数,命令 ls -ld 查看到的是子目录数)。由于文件系统 ext3 对 i_links_count 有限制,其最大数为:32000(该限制在 ext4 中被取消)。

总结

本文描述了 Linux 系统中文件与目录被引入的原因及 Linux 处理文件的方式,然后我们通过区分硬链接与软链接的不同,了解 Linux 中的索引节点的相关知识,并以此引出了 inode 的结构体。索引节点结构体存在在于 Linux VFS 以及实际文件系统中,VFS 作为通用文件模型是 Linux 中“一切皆是文件”实现的基础。文章并未深入 Linux VFS,也没涉及实际文件系统的实现,文章只是从 inode 了解 Linux 的文件系统的相关内容。