一、视频学习
1、系统调用的三层皮:xyz(API)、system call(中断向量)、sys_xyz(不同种类的服务程序)。
2、Libc库定义个一些API引用了封装例程(wrapper routine,唯一的目的就是发布系统调用,程序员在写代码的时候不需要用汇编指令来触发一个系统调用,而是直接触发一个函数就能进行系统调用了。)
3、system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号。
二、实验部分
使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用,理解系统调用的工作机制。实验中我选择24号和47号系统调用,分别获取当前用户uid(用户ID)和gid(组ID),即模拟Linux系统“id”命令。
(1)库函数API实现:
(2)嵌入汇编代码实现:
(3)运行结果如下:
(4)实验分析:
1)汇编代码分析:
首先将ebx寄存器清零,表示无参数传入。然后分别将0x18和0x2f(十进制24和47)赋值给eax寄存器,表示需要调用的系统调用号,24为getuid,47为getgid。执行int 0x80来执行系统调用。之后eax寄存器保存了返回值,将它分别赋值给输出uid或gid变量。完成整个汇编代码的系统调用。
2)Linux中的用户(UID)、组(GID)、进程(PID):
在 Linux 中,一个用户 UID 标示一个给定用户。Linux系统中的用户(UID)分为3类,即普通用户、根用户、系统用户。
1、普通用户是指所有使用Linux系统的真实用户,这类用户可以使用用户名及密码登录系统。Linux有着极为详细的权限设置,所以一般来说普通用户只能在其家目录、系统临时目录或其他经过授权的目录中操作,以及操作属于该用户的文件。通常普通用户的UID大于500,因为在添加普通用户时,系统默认用户ID从500开始编号。
2、根用户也就是root用户,它的ID是0,也被称为超级用户,root账户拥有对系统的完全控制权:可以修改、删除任何文件,运行任何命令。所以root用户也是系统里面最具危险性的用户,root用户甚至可以在系统正常运行时删除所有文件系统,造成无法挽回的灾难。所以一般情况下,使用root用户登录系统时需要十分小心。
3、系统用户是指系统运行时必须有的用户,但并不是指真实的使用者。比如在RedHat或CentOS下运行网站服务时,需要使用系统用户apache来运行httpd进程,而运行MySQL数据库服务时,需要使用系统用户mysql来运行mysqld进程。在RedHat或CentOS下,系统用户的ID范围是1~499。
真实的用户、组(uid、gid):进程的真正所有者。每当用户在shell终端登录时,都会将登录用户作为登录进程的真正所有者。通过getuid来获得进程的真正用户所有者,修改进程的真正用户所有者可以通过setuid、seteuid、setresuid、setreuid。
详细介绍见:
三、课本学习
1、中断产生原因:
处理器的速度跟外围硬件设备的速度往往不在一个数量级,所以提供中断机制,让硬件需要的时候再向内核发出信号,使得处理器和外部设备可以协同工作,这就比以前采用的轮询机制效率提高很多,轮询使CPU做太多“无用功”,而中断则更加聪明一些,在必要的时候通知CPU,减少了不必要的工作量提高了CPU的执行效率。
2、中断的请求过程:
外部设备当需要操作系统做相关的事情的时候,会产生相应的中断。设备通过相应的中断线向中断控制器发送高电平以产生中断信号,而操作系统则会从中断控制器的状态位取得那根中断线上产生的中断。而且只有在设备在对某一条中断线拥有控制权,才可以向这条中断线上发送信号。也由于现在的外设越来越多,中断线又是很宝贵的资源不可能被一一对应。因此在使用中断线前,就得对相应的中断线进行申请。无论采用共享中断方式还是独占一个中断,申请过程都是先讲所有的中断线进行扫描,得出哪些没有别占用,从其中选择一个作为该设备的IRQ。其次,通过中断申请函数申请相应的IRQ。最后,根据申请结果查看中断是否能够被执行。
以编程中断控制器8259A为例:
1)组成:为更好的处理外部设备,x86微机提供了两片可编程中断控制器,用来辅助cpu接受外部的中断信号;对于中断,cpu只提供两个外接引线:NMI和INTR;NMI只能通过端口操作来屏蔽,它通常用于:电源掉电和物理存储器奇偶验错;INTR可通过直接设置中断屏蔽位来屏蔽,它可用来接受外部中断信号,但只有一个引线,不够用;所以它通过外接两片级链了的8259A,以接受更多的外部中断信号。8259A主要完成这样一些任务:中断优先级排队管理,接受外部中断请求,向cpu提供中断类型号。
2)中断过程:外部设备产生的中断信号在IRQ(中断请求)管脚上首先由中断控制器处理。中断控制器可 以响应多个中断输入,它的输出连接到 CPU 的 INT 管脚,信号可通过INT 管脚,通知处理器产生了中断。如果 CPU 这时可以处理中断,CPU 会通过 INTA(中断确认)管脚上的信号通知中断控制器已接受中断,这时,中断控制器可将一个 8 位数据放置在数据总线上,这一 8 位数据也称为中断向量号,CPU 依据中断向量号和中断描述符表(IDT)中的信息自动调用相应的中断服务程序。图三中,两个中断控制器级联了起来,从属中断控制器的输出连接到了主中断控 制器的第 3 个中断信号输入,这样,该系统可处理的外部中断数量最多可达 15 个,图的右边是 i386 PC 中各中断输入管脚的一般分配。可通过对8259A的初始化,使这15个外接引脚对应256个中断向量的任何15个连续的向量;由于intel公司保留0- 31号中断向量用来处理异常事件(而默认情况下,IBM bios把硬中断设在0x08-0x0f),所以,硬中断必须设在31以后,linux则在实模式下初始化时把其设在0x20-0x2F,
3、中断的处理过程:
与Linux设备驱动中中断处理相关的首先是申请与释放IRQ的API request_ irq()和free_irq():
request_irq()的原型为:
int request_irq(unsigned int irq,void (*handler)(int irq, void *dev_id, struct pt_regs *regs),unsigned long irqflags,const char * devname,void *dev_id);
1. irq是要申请的硬件中断号;
2. handler是向系统登记的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递;
3. irqflags是中断处理的属性,若设置SA_ INTERRUPT,标明中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程 序不屏蔽;若设置SA_ SHIRQ,则多个设备共享中断,dev_id在中断共享时会用到,一般设置为这个设备的device结构本身或者NULL。
free_irq()的原型为:
void free_irq(unsigned int irq,void *dev_id);
另外,与Linux中断息息相关的一个重要概念是Linux中断分为两个半部:上半部(tophalf)和下半部(bottom half)。上半部的功能是"登记中断",当一个中断发生时,它进行相应地硬件读写后就把中断例程的下半部挂到该设备的下半部执行队列中去。因此,上半部 执行的速度就会很快,可以服务更多的中断请求。但是,仅有"登记中断"是远远不够的,因为中断的事件可能很复杂。因此,Linux引入了一个下半部,来完 成中断事件的绝大多数使命。下半部和上半部最大的不同是下半部是可中断的,而上半部是不可中断的,下半部几乎做了中断处理程序所有的事情,而且可以被新的 中断打断!下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行。
4、下半部的处理过程:
下半部的实现方式,在2.6的内核中,有3种:软中断、tasklet、工作队列。软中断用的比较少,只有有限的几种使用场景,更多的是使用tasklet。
1)软中断:我们不可中断部分的共同部分放在函数do_ IRQ中,需要添加中断处理函数时,通过request_ irq实现。下半部放在do_ softirq中,也就是软中断,通过open_softirq添加对应的处理函数。
2)tasklet:旧事物跟不上历史的发展时,总会有新事物出现。随着中断数的不停增加,软中断不够用了,于是下半部又做了进化。软中断用轮询的方式处理。假如正好是最后一种中断,则必须循环完所有的中断类型,才能最终执行对应的处理函数。显然当年开发人员为了保证轮询的效率,于是限制中断个数为32个。为了提高中断处理数量,顺道改进处理效率,于是产生了tasklet机制。
- Tasklet采用无差别的队列机制,有中断时才执行,免去了循环查表之苦。
- CDMA因为频谱重叠问题,必须降功率。而功率降低后,又发现原来功率低了有助于环保。
- Tasklet作为一种新机制,显然可以承担更多的优点。正好这时候SMP越来越火了,因此又在tasklet中加入了SMP机制,保证同种中断只
- 在一个cpu上执行。在软中断时代,显然没有这种考虑。因此同一种中断可以在两个cpu上同时执行,很可能造成冲突。
3)工作队列:前面的机制不论如何折腾,有一点是不会变的。它们都在中断上下文中。什么意思?说明它们不可挂起。而且由于是串行执行,因此只要有一个处理时间较长,则会导致其他中断响应的延迟。为了完成这些不可能完成的任务,于是出现了工作队列。工作队列说白了就是一组内核线程,作为中断守护线程来使用。多个中断可以放在一个线程中,也可以每个中断分配一个线程。工作队列对线程作了封装,使用起来更方便。因为工作队列是线程,所以我们可以使用所有可以在线程中使用的方法。
总结:
看课本基本了解了中断机制的大致实现过程,书中也举了一个时钟中断的例子, 我也结合了进一步从RTC设备了解了中断过程,以前学习操作系统只是进行简单中断过程的了解,现在又加入了上、下部分别处理,下部的功能实现有点难理解,还将进一步查找资料深入学习。