设备驱动程序是一个软件层,该软件层使硬件响应预定义好的编程接口,我们已经熟悉了这种接口,它由一组控制设备的VFS函数(open,read,lseek,ioctl等)组成,这些函数实际实现由设备驱动程序全权负责。
1.4 文件系统文件系统模块用于支持对外部设备的驱动和存储。虚拟文件系统模块通过向所有的外部存储设备提供一个通用的文件接口,隐藏了各种硬件设备的不同细节,从而提供并支持与其他操作系统兼容的多种文件系统格式。VFS所提供的这些统一的API,再经过系统调用包装一下,用户空间就可以经过SCI的系统调用来操作不同的文件系统。
1.5 中断和系统调用
中断是计算机的三大法宝之一,分外部中断(硬件中断)和内部中断(软件中断),其中软件系统调用作为一种特殊的中断,就是利用陷阱(trap)这种软件中断式主动从用户态进入内核态的。
中断执行过程:
确定中断向量,利用idtr找到中断入口地址,确定特权级是否匹配,是否需要变更堆栈段,然后在栈中保存eflags、cs和eip的内容(保存在被中断进程的内核栈中),如果异常产生一个硬件出错码,则将它保存在栈中,然后装载cs和eip,执行中断处理程序。
中断返回:
用保存在栈中的值装载cs、eip和eflags寄存器。如果一个硬件出错码曾被压入栈中,那么弹出这个硬件出错码,检查中断前是否在内核态,如果不是,从栈中装载ss和esp回退到用户态。
其中系统调用机制如下图:
Linux系统调用总体上可以划分为内核态和用户态,也可以说是内核和应用程序,系统调用就是提供给用户程序的一组可以访问内核的接口。普通的函数调用是通过将参数压栈的方式传递的。系统调用从用户态切换到内核态,在用户态只能访问到用户堆栈,在内核态可以访问内核和用户堆栈。
系统调用的过程:
1、用户执行 int $0X80 或者 syscall 触发系统调用
2、利用寄存器保存现场,其中eax中存放系统调用号
3、CPU切换到内核态执行system_call()汇编代码,此时需要从eax寄存器中获取系统调用号,内核才知道执行哪个系统调用
4、执行完后回复现场
5、系统调用返回
2 具体实例这里描述读写文件的过程。在读写文件之前,必须使用oepn打开一个文件,打开文件首先open会执行到C库,C库里有INT $0x80指令,然后在中断向量表中找到128项,中断向量表里有中断描述符,可以找到中断处理程序入口,第128项是系统调用处理函数,进入系统调用处理函数,保存现场,系统调用号存储eax中,根据系统调用号执行系统调用表中对那一项的函数。系统调用表是相应的函数指针,这里会执行sys_open。sys_open进行命令查找,找到文件控制块,根据不同文件类型,调用文件打开函数,文件打开函数会创建一个系统文件打开表file,file的很多内容来自文件控制块,填完之后,进程也有一个进程文件打开表,这个结构里面有fd数组,fd数组是指针,找个空闲的,把它指向之前已经创建的file结构,最后返回那一项的索引号,即fd。
当进程使用read系统调用读这个文件,就会根据fd数组的下标 找到fd数组的对应项,然后找到指向之前创建的file结构的指针,再找到这个file结构,最后找到file结构里的file_operations里的具体的read函数来读取文件。write系统调用原理类似。
3 分析Linux应用程序性能影响因素在分析linux程序性能影响因素时需要先确定程序的类型。主要有以下两种:
cpu密集型:例如web服务器像nginx node.js需要CPU进行批处理和数学计算都属于此类型
io密集型:例如数据库常见的mysql,大量消耗内存和存储系统,对CPU和网络要求不高,这种应用使用CPU来发起IO请求,然后进入sleep状态。
确定了应用类型就开始分析有哪些情况能影响性能:
大量的网页请求会填满运行队列、大量的上下文切换,中断。
大量的磁盘请求。
网卡大量的吞吐。
以及内存耗尽等。
归结起来就是4个方面cpu memory i/o和 network。