Linux虚拟文件系统(3)– VFS系统调用

索引

Linux虚拟文件系统(1)

Linux虚拟文件系统(2)– 初始化流程

前言

前2章分别介绍了VFS的基本数据结构和初始化流程。本章介绍VFS文件系统的使用。文件系统使用大多是从应用层系统调用开始的,下表是对文件系统系统调用的一个整理。

Linux虚拟文件系统(3)-- VFS系统调用

MOUNT

VFS文件系统可以使用的前提,是系统挂载(Mount)成功。前一章中我们看到系统初始化过程中挂载了Rootfs,即根文件系统(/)。这里我们具体看一下rootfs挂载的细节。 继续阅读“Linux虚拟文件系统(3)– VFS系统调用”

Linux虚拟文件系统(2)– 初始化流程

索引

Linux虚拟文件系统(1)

Linux虚拟文件系统(3)– VFS系统调用

上文介绍了Linux VFS的基本架构与数据结构,本文介绍Linux虚拟文件系统的初始化过程。

如下图

+-----> start_kernel()
|
|
+-----> vfs_caches_init_early()
|
|
+-----> vfs_caches_init()
|                        +--->new thread(kernel_init)
|                        |       +
|                        |       |
+-----> rest_init()+------       v
                           kernel_init_freeable
                                 + 
                                 | 
                                 v
                             do_basic_setup
                                 +
                                 |
                                 v
                           prepare_namespace

在Linux kernel的入口函数start_kernel中,系统调用vfs_caches_init_early和vfs_caches_init进行必要的初始化。 继续阅读“Linux虚拟文件系统(2)– 初始化流程”

Linux虚拟文件系统(1)

索引

Linux虚拟文件系统(2)– 初始化流程

Linux虚拟文件系统(3)– VFS系统调用

VFS简介

Linux将系统中很多资源都抽象成文件,如Socket、设备节点、以及内存。可以如此做,归功于Linux操作系统的虚拟文件系统(VFS)。

有了VFS,无论底层文件系统格式是FAT、ext格式甚至是内存,(一般情况下)上层应用无论关心底层这些文件系统的实现细节,可以按照统一的方式对文件进行操作。

Linux虚拟文件系统(1)

如上图,用户态应用通过glibc提供的API对文件进行操作,如open()、read()、 write()等。Glibc将这些函数转换成Linux提供的系统调用,操作系统根据系统调用号执行对应的操作函数,如__syscall_open/__syscall_read/__syscall_write等。这样就走到了Linux的虚拟文件系统层(上图VFS)。

Linux文件系统模型

Linux VFS最重要的数据结构包括

  • SuperBlock:存储与已加载(Mount)的文件系统相关的信息
  • inode:与系统中文件关联的相关信息
  • dentry:文件系统目录描述符,用来关联文件系统中的目录
  • file:与特定进程锁打开文件相关的结构体

下图(引自<Understanding the Linux Kernel>)描述了这些数据结构间的关系。三个进程打开同一个文件,其中进程3通过不同的文件硬链接访问这个文件。可以看出进程打开一个文件后,通过对应的文件描述符关联了到文件对象。相同的硬链接文件,它对应的dentry对象也是相同的(虽然是同一个文件,不同硬链接的dentry对象也是不同的)。注:f_dentry目前已经由struct path f_path成员取代。不同的dentry对象最终指向的是同一个在某个超级块上的inode节点,最终指向磁盘文件。

注意到其中process 1访问对应dentry有一个dentry cache,这是Linux的一种缓存机制。可以将最近加载或者使用的dentry节点缓存到内存中,加速访问。

Linux虚拟文件系统(1)

下边看一下具体的数据结构。 继续阅读“Linux虚拟文件系统(1)”

Linux中断学习笔记(2) — 嵌入式设备中断

Linux中断学习笔记(1)提到,外设通过中断控制器连接到CPU的中断线。嵌入式系统也不例外。

ARM嵌入式系统GIC架构

ARM官网所举图为例:ARM的中断控制器GIC(General Interrupt Controller)将从外设输入的中断通过CPU的IRQ信号线(ARM中主要为FIQ和IRQ)连接到系统中各CPU。

图1. GIC 简单结构图

Linux中断学习笔记(2) -- 嵌入式设备中断

中断控制器允许级联,一个有中断级联的终端流程如下图所示。次级GIC将中断信号通知到主GIC后,主GIC再通知CPU,CPU读各级中断控制器的Ack Register得到中断号,并开始执行相应的中断例程。执行完后,直接写次级中断控制器的寄存器标记中断服务结束。 继续阅读“Linux中断学习笔记(2) — 嵌入式设备中断”

Linux中断学习笔记(1)

什么是中断

CPU获取外设状态变化有两种方式:

  • Polling:不断跟外设询问它的状态
  • 当外设状态变化后主动通知CPU

CPU要负责处理系统中各种各样的业务,如果频繁地轮询外设状态,必然会对整个系统的吞吐量产生影响,影响操作系统的正常运作。
中断便是外设通知CPU其状态变化的一种机制。CPU会有中断线,由中断控制器的输出线连接。中断控制器作用:

  • 中断优先级处理
  • 接收中断控制器ACK
  • 分发中断

中断控制器连接到MCU的中断输入引脚(在ARM就是IRQ-Interrupt Request or FIQ-Fast Interrupt Request信号线)。中断控制器驱动负责Kernel中断号与物理中断号的Mapping。
当外设没有变化的时候,CPU可以专心地处理其他事务。外设准备好资料时,通过中断控制器通知CPU,CPU再对其进行相应的处理。

Linux的中断处理

顶半部(Top Half)和底半部(Bottom Half)

中断会将正在运行的程序给打断(操作系统会保留相应的执行数据以便返回)。外设中断有时要处理的数据量很大,为了避免中断处理长期占用CPU导致系统中其他事务无法正常进行,Linux将中断处理分为顶半部和底半部。顶半部主要负责响应中断,处理该外设一些必须的业务,同时将需要复杂处理的事务放到底半部执行。
Linux中中断的顶半部是不能被Block的,原因:
操作系统并没有为中断分配相应的进程管理单元,当中断服务例程因为发生Block而被系统Schedule出去后,便无法再返回到该中断服务例程。而因为中断被调度出的进程,被schedule后却不能得到调度。

实现底半部有多种方式,常用的是Tasklet(特殊的软中断)和Workqueue(内核线程)

软中断和Tasklet

当中断上半部执行结束后,操作系统会调度执行软中断。具体实现以linux-4.x为例:

./kernel/softirq.c

void arch_do_IRQ(unsigned int irq, struct pt_regs *regs)                //中断向量表调用相应的IRQ处理函数
{
    struct pt_regs *old_regs = set_irq_regs(regs);                      //保留中断现场

    irq_enter();                                                        //进入中断处理前的必要处理,如关闭硬件中断,关闭抢占(preempt),禁止ksoftirqd启动处理(softirq)-- 因为稍后会看到,中断结束后会做处理
    generic_handle_irq(irq);                                            //执行外设驱动注册的irq handler
    irq_exit();                                                         //进入到irq_exit
    set_irq_regs(old_regs);
}
void irq_exit(void)
{
#ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED
    local_irq_disable();
#else
    WARN_ON_ONCE(!irqs_disabled());
#endif
    account_irq_exit_time(current);
    preempt_count_sub(HARDIRQ_OFFSET);                                 //打开抢占
    if (!in_interrupt() && local_softirq_pending())                    //如果有软中断pending,执行softirq
        invoke_softirq();
    tick_irq_exit();
    rcu_irq_exit();
    trace_hardirq_exit(); 
}

继续阅读“Linux中断学习笔记(1)”