欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

VFS中的read/write系统调用

程序员文章站 2022-05-09 23:41:34
...

VFS中的read/write系统调用

​ VFS所隐含的主要思想在于引入了一个通用的文件模型(common file model),这个模型能够表示所有支持的文件系统。VFS是所有文件系统的抽象。

​ linux内核对每个文件读写操作都必须使用一个指针,指向要访问的具体文件系统的适当函数。换句话说,当应用程序对read()或是write()调用引起内核调用相应的sys_read()或是sys_write()服务例程,文件在内核内存中是由一个file数据结构来表示的。这种数据结构中包含一个称为f_op的字段,该字段中包含一个指向专对某一个文件系统(如sysfs虚拟文件系统)的读写函数指针,sys_read()或是sys_write()查找到指向该函数的指针,并调用它。这样一来,应用程序的read()或是write()就被转化为相对间接的调用:

file->f_op->read() 或 file->f_op->write()
或者 file->f_op->aio_write()

剖析printf函数

下面我们以printf函数的调用为例,说明该函数是如何一步一步最终落在内核函数上去的。

VFS中的read/write系统调用

write函数内部就是调用了int中断。一般的系统调用都是调用0x80号中断。而操作系统中一般不会的显式的写出write的实现代码,而是通过_syscall3宏展开的实现。

_syscall3是专门用来处理有3个参数的系统调用的函数的实现。

同理还有_syscall0_syscall1_syscall2等,目前最大支持的参数个数为3个,这三个参数是通过ebx, ecx,edx传递的。

如果有系统调用的参数超过了3个,那么可以通过一个参数结构体来进行传递。

// linux/lib/write.c
#define __LIBRARY__
#include <unistd.h>
// 
_syscall3(int,write,int,fd,const char *,buf,off_t,count) //_syscall后面的数字“3”表示系统调用有三个参数
// linux/include/unistd.h
#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
    return (type) __res; \
errno=-__res; \
return -1; \
}

所以宏展开后,write函数的实现实现为:

int write(int fd, const char *buf, off_t count)
{ 
    long __res; 
    __asm__ volatile ("int $0x80" 
        : "=a" (__res) 
        : "0" (__NR_write),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); 
    if (__res>=0) 
        return (type) __res; 
    errno=-__res; 
    return -1; 
}

我们看到实际函数内部并没有做太多的事情,主要就是调用int 0x80,将把相关的参数传递给一些通用寄存器,调用的结果通过eax返回。其中一个很重要的调用参数是__NR_write这个也是一个宏,就是wirte的系统调用号,在linux/include/unistd.h中被定义为4,同样还有很多其他系统调用号。因为所有的系统调用都是通过int 0x80,那怎么知道具体需要什么功能呢,只能通过系统调用号来识别。

下面我们来看看int 0x80是如何执行的。这是一个系统中断,操作系统对于中断处理流程一般为

  1. 关中断:CPU关闭中段响应,即不再接受其它外部中断请求
  2. 保存断点:将发生中断处的指令地址压入堆栈,以使中断处理完后能正确地返回。
  3. 识别中断源:CPU识别中断的来源,确定中断类型号,从而找到相应的中断服务程序的入口地址。
  4. 保护现场所:将发生中断处理有关寄存器(中断服务程序中要使用的寄存器)以及标志寄存器的内存压入堆栈。
  5. 执行中断服务程序:转到中断服务程序入口开始执行,可在适当时刻重新开放中断,以便允许响应较高优先级的外部中断。
  6. 恢复现场并返回:把“保护现场”时压入堆栈的信息弹回原寄存器,然后执行中断返回指令(IRET),从而返回主程序继续运行。

前3项通常由处理中断的硬件电路完成,后3项通常由软件(中断服务程序)完成。

VFS中的read/write系统调用