linux 0.11 内核学习 -- system_call.s,系统调用仅是如此。
来源:互联网 发布:itunes windows xp版 编辑:程序博客网 时间:2024/06/06 02:50
/*
* 本程序主要是实现系统调用中断int 0x80的入口处理过程机信号检测过程,
* 同时给出了两个系统调用功能的底层接口sys_execve和sys_fork。还列出了
* 处理过程类似的协处理器出错int 16,设备不存在int 7,硬盘中断int 46,
* 软盘中断int 38的中断处理程序。
*
*/
/*
* linux/kernel/system_call.s
*
* (C) 1991 Linus Torvalds
*/
/*
* system_call.s contains the system-call low-level handling routines.
* This also contains the timer-interrupt handler, as some of the code is
* the same. The hd- and flopppy-interrupts are also here.
*
* NOTE: This code handles signal-recognition, which happens every time
* after a timer-interrupt and after each system call. Ordinary interrupts
* don't handle signal-recognition, as that would clutter them up totally
* unnecessarily.
*
* Stack layout in 'ret_from_system_call':
*
* 0(%esp) - %eax
* 4(%esp) - %ebx
* 8(%esp) - %ecx
* C(%esp) - %edx
*10(%esp) - %fs
*14(%esp) - %es
*18(%esp) - %ds
*1C(%esp) - %eip
*20(%esp) - %cs
*24(%esp) - %eflags
*28(%esp) - %oldesp
*2C(%esp) - %oldss
*/
SIG_CHLD= 17# 信号,子进程结束或者是终止。
##############################################
# 堆栈中寄存器的偏移量
EAX= 0x00
EBX= 0x04
ECX= 0x08
EDX= 0x0C
FS= 0x10
ES= 0x14
DS= 0x18
EIP= 0x1C
CS= 0x20
EFLAGS= 0x24
OLDESP= 0x28# 当特权级变化时
OLDSS= 0x2C
#############################################
##############################################
# 以下是定义任务结构task_struct中偏移量。
state= 0# these are offsets into the task-struct.
# 进程状态码
counter= 4# 任务运行时间片数
priority = 8
signal= 12# 信号位图
sigaction = 16# MUST be 16 (=len of sigaction)
blocked = (33*16)
# offsets within sigaction
sa_handler = 0# 信号处理过程句柄
sa_mask = 4# 信号屏蔽码
sa_flags = 8# 信号集
sa_restorer = 12
nr_system_calls = 72# linux 0.11中系统调用总数
/*
* Ok, I get parallel printer interrupts while using the floppy for some
* strange reason. Urgel. Now I just ignore them.
*/
.globl _system_call,_sys_fork,_timer_interrupt,_sys_execve
.globl _hd_interrupt,_floppy_interrupt,_parallel_interrupt
.globl _device_not_available, _coprocessor_error
# 错误的系统调用
.align 2# 内存4字节对齐
bad_sys_call:
movl $-1,%eax
iret
# 重新执行调度程序入口
.align 2
reschedule:
pushl $ret_from_sys_call# $ret_from_sys_call地址入栈
jmp _schedule
# int 0x80 -- linux系统调用入口点,eax为中断号
.align 2
_system_call:
# 调用号如果超出范围,退出
cmpl $nr_system_calls-1,%eax
ja bad_sys_call
# 如果调用号没有超出范围,继续执行。
# 保护原来寄存器
push %ds
push %es
push %fs
# 下面的代码是将系统调用c函数的参数入栈,此过程是在执行
# 系统函数调用时实现的
pushl %edx
pushl %ecx# push %ebx,%ecx,%edx as parameters
pushl %ebx# to the system call
# ds,es指向内核数据段
# 参见文档 <linux0_11系统调用的执行过程是怎样的.doc>
movl $0x10,%edx# set up ds,es to kernel space
mov %dx,%ds
mov %dx,%es
# fs指向局部数据段
movl $0x17,%edx# fs points to local data space
mov %dx,%fs
# _sys_call_table(,%eax,4)使用的at&t格式寻址。其实就是调用eax
# 对应的系统调用函数
call _sys_call_table(,%eax,4)
pushl %eax# 系统调用号入栈
movl _current,%eax# 将当前进程数据结构地址保存在eax
cmpl $0,state(%eax)# state,如果当前进程不就绪
jne reschedule# 执行调度程序reschedule
cmpl $0,counter(%eax)# counter,如果当前的进程就绪
# 但是时间片用完,调度去了。
je reschedule
# ret_from_sys_call是在中断处理程序完成之后,对信号量进行识别处理
ret_from_sys_call:
# 得到当前进程数据结构地址到eax
movl _current,%eax# task[0] cannot have signals
cmpl _task,%eax# 当前的任务是task[0]?task在c语言定义。
je 3f# 如果是直接返回
##########################################
# 通过对调用程序代码的选择符的检查来判断调用程序是否
# 是超级用户。如果是超级用户直接退出,无须信号处理,
# 否则需要进行信号处理。这里比较选择符是否为普通用户
# 代码选择符0x000f(RPL = 3,局部表,第一个段(代码段))。
# 暂时认为的是linux内核在实现用户权限时的设置时这样的:
# 首先是普通用户的应用程序是在用户的空间实现的,但是root
# 用户的程序是在内核空间运行的。
cmpw $0x0f,CS(%esp)# was old code segment supervisor ?
jne 3f
###########################################
# 如果员堆栈的段选择符不是0x17,即是原来的堆栈
# 不在用户的的数据段,则也退出。
cmpw $0x17,OLDSS(%esp)# was stack segment = 0x17 ?
jne 3f
##########################################
# 下面程序开始执行,首先查看是否有信号量到来。
#
# 下面的代码首先是取得当前的任务结构中的信号位图
# (32位,每一位代表一种信号),然后用任务结构中的
# 信号屏蔽码,阻塞不允许的信号位,取得数值最小的信号值
# 在把原信号位图中对该信号对应位置0,最后将该信号的参数
# 值作为参数调用函数do_signal
# do_signal函数包含13个参数。
movl signal(%eax),%ebx# 取得信号位图 -- ebx
movl blocked(%eax),%ecx# 取得阻塞信号位图 -- ecx
notl %ecx# 每位取反
andl %ebx,%ecx# 获得许可信号位图
bsfl %ecx,%ecx# 从低位开始扫描,看是否存在1
# 如果有,则eax保留该位的偏移量
je 3f# 如果没有的信号向前退出
btrl %ecx,%ebx# 复位该信号 ebx含有原signal位图
movl %ebx,signal(%eax)# 重新保存signal -- current->signal
incl %ecx# 将信号调整为从1开始的数
# 下面的代码是调用函数_do_signal
pushl %ecx# 参数入栈
call _do_signal# 函数调用
popl %eax# 弹出信号值
#下面的代码是恢复在ret_from_sys_call中保存的值
3:popl %eax
popl %ebx
popl %ecx
popl %edx
pop %fs
pop %es
pop %ds
iret
# 下面的这段代码是处理协处理器发出的出错信号。跳转发哦c函数math_error
# 去执行,返回之后调用ret_from_sys_call处继续执行。
.align 2
_coprocessor_error:
push %ds
push %es
push %fs
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movl $0x10,%eax# ds,es指向的是内核数据段
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax# fs指向的是出错程序的数据段
mov %ax,%fs
pushl $ret_from_sys_call# 函数的返回地址入栈
jmp _math_error# 执行c函数math_error
# int7 -- 设备部存在或者是协处理器不存在
# 控制寄存器中的cr0中的em标志位置位,则当cpu在执行esc转义指令时,
# 就会引发该中断,这样就能够让这个中断处理程序模拟esc转义指令。
# esc转义指令的纤细解释见文档 <<esc转义指令说明.doc>>
# cr0的ts标志是在cpu执行任务转换时设置的。ts可以确定什么时候
# 协处理器的内容(上下文)与cpu正常执行的人物不匹配。当cpu在运行
# 一个转义指令时发现ts指令置位,就引发该中断。
# 该中断最后将转移到标号$ret_from_sys_call处继续执行。
.align 2
_device_not_available:
push %ds
push %es
push %fs
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
pushl $ret_from_sys_call# 将$ret_from_sys_call地址入栈
clts# clear TS so that we can use math
movl %cr0,%eax
testl $0x4,%eax# EM (math emulation bit)
# 如果不是em引起的,则恢复新任务协处理器
# 状态。
je _math_state_restore# 执行c函数math_state_restore
pushl %ebp
pushl %esi
pushl %edi
call _math_emulate# 调用函数math_emulate
popl %edi
popl %esi
popl %ebp
ret# 跳转到$ret_from_sys_call执行
# int32 -- 时钟中断程序。
# 定时芯片8254/8253是在sched.c中完成初始化的。下面的
# 这段代码首先将jiffies的值增加1,发送中断指令给8259
# 控制器,然后用当前特权级作为参数调用c函数do_timer
# (long CPL)。当调用返回时转出检测并处理信号。
.align 2
_timer_interrupt:
# 保护现场
push %ds# save ds,es and put kernel data space
push %es# into them. %fs is used by _system_call
push %fs
pushl %edx# we save %eax,%ecx,%edx as gcc doesn't
pushl %ecx# save those across function calls. %ebx
pushl %ebx# is saved as we use that in ret_sys_call
pushl %eax
movl $0x10,%eax# ds,es指向的是内核的数据段
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax# fs指向的是局部的数据段,出错程序的
# 的数据段
mov %ax,%fs
incl _jiffies# 增加jiffies的值
# 由于初始化中断控制芯片时没有采用自动eoi,所以这里发送
# 指令结束硬件的中断。
movb $0x20,%al# EOI to interrupt controller #1
outb %al,$0x20# 操作命令字ocw2送到0x20端口
# 下面的3条语句从选择符中取出当前特权级0或者是3,并压入、
# 栈中,作为do_timer的参数
movl CS(%esp),%eax
andl $3,%eax# %eax is CPL (0 or 3, 0=supervisor)
pushl %eax
# do_timer执行任务的切换,计时等工作
call _do_timer# 'do_timer(long CPL)' does everything from
addl $4,%esp# task switching to accounting ...
jmp ret_from_sys_call
# 下面是系统调用sys_execve()函数ude调用过程。首先出去中断
# 调用程序代码指针作为参数传递给c函数do_execve,然后调用
# 函数do_execve
.align 2
_sys_execve:
lea EIP(%esp),%eax
pushl %eax
call _do_execve
addl $4,%esp
ret
# sys_fork系统调用,其主要的作用是创建子进程。
# 下面的代码首先调用c函数find_empty_process,
# 取得进程号pid,若果返回的是负值的话,说明
# 当前任务数组已满。然后调用copy_process复制
# 进程
.align 2
_sys_fork:
call _find_empty_process# 调用函数find_empty_process
testl %eax,%eax#测试函数的返回值否为负值?
js 1f# 为负值,跳转到ret指令
# 否则继续执行
push %gs
pushl %esi
pushl %edi
pushl %ebp
pushl %eax
call _copy_process# 复制进程
addl $20,%esp
1:ret
# int46 -- 硬盘中断处理程序。
# 首先向8259a中断控制从芯片发送结束硬件中断指令eoi,
# 然后取出变量do_hd中函数指针放入edx中,并置do_hd
# 的值为空,接着判断的是edx函数指针是否为空。如果
# 为空的话,则将edx指向unexcept_hd_interrupt,用于
# 显示错误信息。然后向8259a主芯片发送eoi指令,并
# 调用edx函数指向的函数:read_intr(),write_intr()
# unexcept_hd_interrup
_hd_interrupt:
# 保护寄存器
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax# ds,es指向的是内核的数据段
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax# fs指向的调用程序的局部数据段
mov %ax,%fs
# 想8259a从设备发送结束硬件中断指令
movb $0x20,%al
outb %al,$0xA0# EOI to interrupt controller #1
# 只为延时
jmp 1f# give port chance to breathe
1:jmp 1f
1:xorl %edx,%edx# edx为0
xchgl _do_hd,%edx# edx指向的是do_hd的指针,do_hd指向的是
# 原来edx的值,即是null
testl %edx,%edx# edx是否为空?
jne 1f
movl $_unexpected_hd_interrupt,%edx# 如果edx指向的值为空,则将edx指向该函数
# 向8259a主芯片发送“结束硬件中断”指令
1:outb %al,$0x20
# 调用函数,或者是read_intr(),write_intr()或是unexcept_hd_interrup
call *%edx# "interesting" way of handling intr.
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
# int38 -- 软盘驱动中断处理程序
# 软盘中断处理程序和硬盘中断处理程序大致相同。
_floppy_interrupt:
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
movb $0x20,%al
outb %al,$0x20# EOI to interrupt controller #1
xorl %eax,%eax
xchgl _do_floppy,%eax
testl %eax,%eax
jne 1f
movl $_unexpected_floppy_interrupt,%eax
1:call *%eax# "interesting" way of handling intr.
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
# int39 -- 并口中断处理程序
# 本程序还未实现,这里只是发送eoi指令
_parallel_interrupt:
pushl %eax
movb $0x20,%al
outb %al,$0x20
popl %eax
iret
- linux 0.11 内核学习 -- system_call.s,系统调用仅是如此。
- linux内核分析之system_call.s
- linux内核分析之system_call.s
- 从 system_call走进linux系统调用
- Linux内核分析之五——分析系统调用(system_call)的执行机制
- linux内核学习----系统调用
- Linux内核分析学习笔记:system_call中断处理过程
- Linux系统调用的system_call处理过程分析
- Linux内核学习之系统调用
- linux 内核学习之系统调用
- Linux内核学习之系统调用
- Linux内核学习-系统调用
- Linux系统内核分析实验——system_call中断处理过程
- 系统调用system_call的处理过程
- 系统调用system_call的处理过程
- Linux内核-系统调用
- 《Linux操作系统分析》之分析系统调用system_call的处理过程
- linux 0.11 内核学习 -- setup.s
- linux 0.11 内核学习 -- asm.s,机器难免出故障。
- 第一个蓝牙程序
- JSP生成Excel报表文档
- 自主开发Delphi控件[1]: THSImageExButton图片按钮,3状态控制
- 通过代码实现DataGrid按指定列排序
- linux 0.11 内核学习 -- system_call.s,系统调用仅是如此。
- VC中基于 Windows 的精确定时---中国科学院光电技术研究所 游志宇
- WIN7兼容性的个人体验
- nginx详细配置说明
- 转载Qt多线程的理解
- linux 0.11 内核学习 -- sched.c,调度进程。
- C语言中可变参数
- 批量修改mp3文件名
- C#网络编程笔记(1) -- TCP Socket通信基本过程和思路