Linux 系统调用
1. Linux 系统为每一个系统调用准备了一个号码,为系统调用号,对于32bit系统而言:exit 为 1, write 为4等。
2. 每当应用程序调用系统调用函数时,如write,首先到 C 库中,(glibc中),调用对应的函数 write。
3. C库中的writehansh ,将对应的调用号保存到对应的寄存器中,一般是R7寄存器。然后调用 SWI(old)或者SVC(new),触发一个异常(软中断),CPU需处理该异常(软中断),需跳转到异常向量表的对应位置去处理。
4. Linux 系统的异常向量表是在内核启动初始化时,会建立异常向量表(向量表初始地址是0XFFFF0000),软中断入口为 vector_swi,应用程序触发了软中断,然后CPU会到该入口去处理这个软中断。
5,进入到软中断 vector_swi入口之后,从R7寄存器中取出之前保存的系统调用号,然后在内核已经准备好的以系统调用号为索引下标的系统调用表中,找到对应的系统调用函数,(如果是4号 write,则对应的系统调用是sys_write),然后执行,(此时已经进入了内核空间)。
6. 返回用户空间。
总结:用户空间到内核空间,是通过软中断实现的,其中的桥梁就是C库实现的对应的“系统调用”中产生的对应的系统调用号,已经使用该系统调用号作为索引的异常向量表。
32bit的机器,0~3G 是用户空间,3G~4G是内核空间,该1G内核空间的入口,就是异常向量表的入口 0XFFFF0000。这个地址往上仅仅只有0XFFFF大小的空间了。所以异常向量表中的单个函数指针占用4个字节,网上 0XFFFF/4 最多可容纳的系统调用个数就是0X3FFF,即16383个。(这一段是我猜的。。。。)
直接从内核源码找那些所谓的类似write,socket系统调用,直接找是找不到的,因为中间有一个GLIBC的一层,从源码看的话,先要从GLIBC的代码中找到对应的接口定义,然后再跳转到内核的代码中,才能了解其全貌。
中断
1. 为什么需要中断(硬件中断)
外设/硬件处理速度远远低于CPU的处理速度,
如果采用轮询方式,(CPU一直忙等),处理外设,会大大降低CPU的利用率;
如果采用中断方式,将会大大提高CPU的利用率。
2. 中断的硬件触发或连接
外设的中断信号并不是直接输送给CPU的,而是先给中断控制器(一般集成在CPU内部),每当外设产生一个中断信号,次中单信号首先输送给中断控制器,经过中断控制器的判断之后,再决定是否给CPU发送。
中断控制器的作用:
能够屏蔽或者是某个外设中断
能够设置外设中断的优先级
能够设置CPU外部的外设的中断信号的有效触发方式
能够设置CPU外部的外设的中断信号给到哪个CPU进行发送
一旦CPU接收到外设的中断信号,CPU开始处理中断。
3. 中断的处理流程
带优先级的中断处理流程图
其中,从正常运行的用户任务,task任务中,接收到了1号中断信号,便跳转到异常向量表中,查询对应的系统调用,并且进行相应的中断服务程序的处理。
4. 中断的软件编程
4.1 准备异常向量表
4.2 编写保护现场的代码
4.3 编写中断服务程序或者中断处理函数
4.4 编写恢复现场的代码
一般上述4个过程,不可缺少,但是,实际编程中,一般的只需要完成第三步即可。
5 Linux 内核的中断编程
除了要试下对应的中断处理服务程序,或者说中断处理函数之外,我们还需要将该中断处理函数,注册到内核中。一旦对应的中断被触发,内核会自动调用我们提供的中断服务程序。
request_irq / free_irq
在Linux内核源码 interrupt.h中
功能:
1. 向硬件申请中断资源
2. 注册硬件中断的中断处理函数到内核
参数:
irq: 硬件中断对应的软件编号,中断号,一般32开始,0到31号保留。该中断号实际是由芯片厂家在内核源码中定义 。
handler: 中断处理函数。
flags: 中断标志
针对外部中断,flags要指定有效的中断触发方式。
IRQF_TRIGER_FALLING: 下降沿触发
IRQF_TRIGER_RISING: 上升沿触发
IRQF_TRIGER_HIGH: 高电平触发
IRQF_TRIGER_LOW: 低电平触发
可以进行位或运算
针对内部中断,flags 给0即可。
name: 中断名称。
dev: 中断处理函数的参数。无需参数时,传入NULL。