[301页]
8-4 sys_call.s 程序
sys_call.s 程序简单总结:
int 0x80 – _system_call
int16 – 处理器错误中断
int7 – 设备不存在或协处理器不存在。
int32 – (int 0x20)时钟中断处理程序。
两个系统功能的底层接口,分别是 sys_execve 和 sys_fork 。
int46 – (int 0x2E)硬盘中断处理程序
int32 – (int 0x26)软盘驱动器中断处理程序
int39 – (int 0x27)并行口中断处理程序
8-4-1 功能描述
(a)在linux0.12中,用户使用中断调用int 0x80和放在寄存器eax中的功能号来使用内核提供的各种功能服务,这些操作系统体用的功能被称为系统调用功能。
(b)通常用户并不是直接使用系统调用中断,而是通过函数库(例如libc)中提供的接口函数来调用的。例如创建进程的系统调用fork可直接使用函数fork()即可。函数库libc中的fork()函数会实现对中断int0x80的调用
过程并把调用结果返回给用户程序。
©对于所有系统调用的实现函数,内核把它们按照系统调用功能号顺序排列成一张函数指针(地址)表(在include/linux/sys.h文件中)。然后在中断int0x80的处理过程中 根据用户提供的功能号 调用对应 系统调用函数建处理。
(d)本程序主要实现系统调用(system_call)中断int0x80的入口处理过程以及信号检查处理(从代码第80行开始),同时给出了两个系统功能的底层接口,分别是sys_execve和sys_fork。还是有列出了处理过程类似的
协处理器出差(int16)、
设备不存在(int7)、
时钟中断(int32)、
硬盘中断(int46)、
软盘中断(int38)的中断处理程序。
(e)对于软中断(system_call、coprocessor_error、device_not_available),其处理过程基本上是首先为调用相应C函数处理程序作准备,将一些参数压入堆栈。系统调用最多可以带3个参数,分别通过寄存器ebx、ecx和edx传入。然后调用C函数进行相应功能的处理,处理返回后再去检查当前任务的信号位图,对值最小的一个信号进行处理并复位信号位图中的该信号。系统调用的C语言处理函数分布在整个linux内核代码中,由include/linux/sys.h头文件中的系统函数指针数组表来匹配。
(f)对于硬件中断请求信号IRQ发来的中断,其处理过程首先是向中断控制芯片8259A发送结束硬件中断控制指令EOI,然后 调用相应的C函数处理程序。对于时钟中断也要对当前任务的信号位图进行检测处理。系统调用(int 0x80)
(a)对于系统调用(int 0x80)的中断处理过程,可以把它看作是一个"接口程序"。
实际上每个系统调用功能的处理过程基本上都是通过调用相应的C函数进行的。即所谓的"Bottom half"函数。
(b)这个程序在刚执行时会首先检查eax中的功能号是否有效(在给定的范围内),然后保存一些会用到的寄存器到堆栈上。Linux内核默认把段寄存器ds,es用于内核数据段,而fs用于用户数据段。接着通过一个地址跳转表(sys_call_table)
调用相应系统调用的C函数。在C函数返回后,程序就把返回值压入堆栈保存起来。
©接下来,该程序查看执行本次调用进程的状态。如果由于上面C函数的操作或者其他情况而是进程的状态从执行态变成了其他状态,或者由于时间片已经用完(counter==0),则调用进程调度函数schedule()(jmp _schedule)。由于在执行jmp _schedule之前已经把返回地址ret_from_sys_call入栈,因此在执行完schedule()后最终会返回到ret_from_sys_call处继续执行。
(d)从 ret_from_sys_call标号处开始的代码执行一些系统调用的后处理工作。主要判断当前进程是否初始进程0,如果是就直接退出此次系统调用,中断返回。否则根据代码段描述符和所使用的堆栈来判断本次调用的进程是不是一个普通进程,
若不是则说明是内核进程(例如初始进程1)或其他。则也会立刻弹出堆栈内容退出系统调用中断。末端的一块代码用来处理调用系统调用进程的信号。若进程结构的信号位图表明该进程接收到信号,则调用信号处理函数do_signal()。
(e)最后,该程序回复保存的寄存器内容,退出此次中断处理过程并返回调用程序。若有信号时则程序会首先"返回"到相应信号处理函数中去执行,然后返回调用system_call的程序。
8-4-2 代码注释
/*
* linux/kernel/system_call.s
*
* (C) 1991 Linus Torvalds
*/
/*
system_call.s文件包含系统调用(system-call)底层处理子程序。由于有些代码比较类似,
所以同时也包括时钟中断处理timer-interrupt句柄。硬盘和软盘的中断处理程序也在这里。
注意:这段代码处理信号(signal)识别,在每次时钟中断和系统调用之后都会进行识别。
一般中断过程并不处理信号识别,因为会给系统造成混乱。
从系统调用返回('ret_from_system_call')时堆栈的内容见上面19-30行。
#上面linus原注释中一般中断过程是指除了系统调用中断(int 0x80)和时钟中断(int 0x20)
#以外的其他中断。这些中断会在内核态或用户态随机发生,若在这些中断过程中也处理信号识别的话,
#就有可能与系统调用中断和时钟中断过程中对信号的识别处理过程相冲突,违反了内核代码非抢占
#原则。因此系统无必要在这些“其他”中断中处理信号,也不允许这样做。
*/
SIG_CHLD = 17 #定义SIG_CHLD信号(子进程停止或结束)。
EAX = 0x00 #堆栈中各个寄存器的偏移位置。
EBX = 0x04
ECX = 0x08
EDX = 0x0C
ORIG_EAX = 0x10 #如果不是系统调用(是其他中断)时,该值为-1。
FS = 0x14
ES = 0x18
DS = 0x1C
EIP = 0x20 #这一行 -- (OLDSS=0x30)行 由CPU自动入栈。
CS = 0x24
EFLAGS = 0x28
OLDESP = 0x2C #当特权级变化时,原堆栈指针也会入栈。
OLDSS = 0x30
#以下这些是任务结构(task_struct)中变量的偏移值,参见include/linux/sched.h,105行开始。
state = 0 # these are offsets into the task-struct. #进程状态码。
counter = 4 # 任务运行时间技术(递减)(滴答数),运行时间片。
priority = 8 # 运行优先数。任务开始运行时counter=priority,越大则运行时间越长。
signal = 12 # 是信号位图,每个位代表一种信号,信号值=位偏移值+1.
sigaction = 16 # MUST be 16 (=len of sigaction) #sigaction结构长度必须是16字节。
blocked = (33*16) # 受阻塞信号位图的偏移量。
# 以下定义在sigaction结构体中的偏移量,参见include/signal.h,第55行开始
sa_handler = 0 # 信号处理过程的句柄(描述符)。
sa_mask = 4 #信号屏蔽码。
sa_flags = 8 #信号集。
sa_restorer = 12 #恢复函数指针,参见kernel/signal.c程序说明。
nr_system_calls = 82 #Linux 0.12版内核中的系统调用总数。
ENOSYS = 38 #系统调用号出错码。
/*
好了,在使用软驱时我收到了并行打印机中断,很奇怪。呵,现在不管它。
*/
.globl _system_call,_sys_fork,_timer_interrupt,_sys_execve
.globl _hd_interrupt,_floppy_interrupt,_parallel_interrupt
.globl _device_not_available, _coprocessor_error
#系统调用号错误时将返回出错码-ENOSYS
.align 2 #内存4字节对齐。
bad_sys_call:
pushl $-ENOSYS #eax中置-ENOSYS。
jmp ret_from_sys_call
# 重新执行调度程序入口。调度程序schedule()在kernel/sched.c,119行处开始。
# 当调度程序schedule()返回是就从ret_from_sys_call处(107行)继续执行。
.align 2
reschedule:
pushl $ret_from_sys_call #将ret_from_sys_call的地址入栈(107行)。
jmp _schedule
#int 0x80 -- linux系统调用入口点(调用中断 int 0x80,eax中时调用号)。
.align 2
_system_call:
push %ds # 保存原段寄存器值。
push %es
push %fs
pushl %eax # save the orig_eax #保存eax原值。
# 一个系统调用最多可带有3个参数,也可以不带参数。下面入栈的ebx、ecx和edx中放着系统
# 调用相应C语言函数(见第99行)的调用参数。这几个寄存器入栈的属性是由GUN gcc规定的,
# ebx中可存放第1个参数,ecx中存放第二个参数,edx中存放第3个参数。
#系统调用语句可参见头文件includeunistd.h中第150到200行的系统调用宏。
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
# 在保存过段寄存器之后,让ds,es指向内核数据段,而fs指向当前局部数据段,即指向指行本次系统
# 调用的用户程序的数据段。注意,在Linux 0.12中内核给任务分配的代码和数据内存段是重叠的,
# 它们的段基址和段限长是相同。参见fork.c程序中copy_mem()函数。
movl $0x10,%edx # set up ds,es to kernel space
mov %dx,%ds
mov %dx,%es
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
cmpl _NR_syscalls,%eax #调用号如果超出范围的话就跳转。
jae bad_sys_call
#下面这句操作数的含义是:调用地址=[_sys_call_table+%eax*4]
call _sys_call_table(,%eax,4) # 间接调用指定功能C函数。
pushl %eax # 把系统调用返回值入栈。
#下面的5行查看当前任务的运行状态。如果不在就绪态(state不等于0)就去执行调度程序。
#如果该任务在就绪状态,但是其时间片已经用完(counter=0),则也去执行调度程序。
#例如当后台进程组中的进程执行控制终端读写操作时,那么默认条件下该后台进程组所有进程会受到
#SIGTTIN或SIGTTOU信号,导致进程组中所有进程处于停止状态。而当前进程则会立刻返回。
2:
movl _current,%eax # 读当前任务(进程)数据结构指针->eax。
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
#以下这段代码执行从系统调用C函数返回后,对信号进行识别处理。其他中断服务程序退出时也将跳转
#到这里进行处理后才退出中断过程,例如后面131行上的处理器出错中断int 16。
#首先判断当前任务是不是初始任务task0,如果是则不必对其进行信号方面的处理,直接返回。
#109行上的_task对应C程序中的task[]数组,直接引用task相当于引用task[0]。
ret_from_sys_call:
movl _current,%eax
cmpl _task,%eax # task[0] cannot have signals
je 3f # 向前(forward)跳转到标号3处退出中断处理。
# 通过对原调用程序代码选择符的检查来判断调用程序是不是用户任务。如果不是则直接退出中断。
# 这是因为任务在内核态执行时不可抢占。否则对任务进行信号量的识别处理。这里通过比较选择符是否
# 为用户代码段的选择符0x000f(RPL=3,局部表,代码段)来判断是否为用户任务。如果不是
# 则说明是某个中断服务程序(例如中断16)跳转到第107行执行到此,于是跳转退出中断程序。
# 另外,如果原堆栈段选择符部位0x17(即原堆栈不在用户段中),也说明本次系统调用的抵用者
# 不是用户任务,则也退出。
cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
jne 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
jne 3f
# 下面这段代码用于处理当前任务中的信号。首先取当前任务结构中的信号位图(32位,每位代表1中信号),
# 然后用任务结构中的信号阻塞(屏蔽)码,阻塞不允许的信号位,取得数值最小的信号值,
# 再把原信号位图中该信号对应的位复位(置0),最后将该信号值作为参数之一调用do_signal()。
# do_signal()在(kernel/signal.c,128)中,其参数包括13个入栈的信息。在do_signal()或信号处理函数返回之后,
# 若返回值不为0则再看看是否需要切换进程或继续处理其他信号。
movl signal(%eax),%ebx #取信号位图->ebx,每1位代表1中信号,共32个信号。
movl blocked(%eax),%ecx #取阻塞信号位图->ecx。
notl %ecx #每位取反。ecx=~ecx
andl %ebx,%ecx #获得许可的信号位图。 ecx=ebx&ecx
bsfl %ecx,%ecx #从低位(位0)开始扫描位图,看是否有1的位, 若有,则ecx保留该位的偏移值(即地址为0--31)。
je 3f #如果没有信号则向前跳转退出。
btrl %ecx,%ebx #复位该信号(ebx含有原signal位图)。
movl %ebx,signal(%eax) #重新保存signal位图信号->current->signal。
incl %ecx #将信号调整为从1开始的数(1--32)。
pushl %ecx #信号值入栈作为调用do_signal的参数之一。
call _do_signal #调用C函数信号处理程序(kernel/signal.c,128)。
popl %ecx #弹出入栈的信号值。
testl %eax, %eax #测试返回值,若不为0则跳转到前面标号2处。
jne 2b # see if we need to switch tasks, or do more signals
3: popl %eax #eax中含有第100行入栈的系统调用返回值。
popl %ebx
popl %ecx
popl %edx
addl $4, %esp # skip orig_eax #跳过(丢弃)原eax值。
pop %fs
pop %es
pop %ds
iret
#int16 -- 处理器错误中断。 类型:错误; 无错误码。
#这是一个外部的基于硬件的异常。当协处理器检测到自己发生错误是,就会通过ERROR引脚
#通知CPU。下面代码用于处理协处理器发出的出错信号。并跳转去执行C函数math_error()
#(kernel/math/error.c 11)。返回后将跳转到标号ret_from_sys_call处继续执行。
.align 2
_coprocessor_error:
push %ds
push %es
push %fs
pushl $-1 # fill in -1 for orig_eax #填-1,表明不是系统调用。
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 #执行math_error()(kernel/math/error.c,11)。
#int7 -- 设备不存在或协处理器不存在。 类型:错误;无错误码。
#1、如果控制寄存器CR0中EM(模拟)标志置位,则当CPU执行一个协处理器指令时就会引发该中断,
#这样CPU就可以有机会让这个中断处理程序模拟协处理器指令(call _math_emulate)。
#2、CR0的 交换标志TS是在CPU执行任务转换时设置的。TS可以用来确定什么时候协处理器中的内容
#与CPU正在执行的任务不匹配了。当CPU在运行一个协处理器转移指令时发现TS置位时,就会引发
#该中断。此时就可以保存前一个任务的协处理器内容,并恢复新任务的协处理器执行状态(_math_state_restore)。
#参见kernel/sched.c,92行。该中断最后将转移到标号ret_from_sys_call处执行下去(监测并处理信号)。
.align 2
_device_not_available:
push %ds
push %es
push %fs
pushl $-1 # fill in -1 for orig_eax #填-1,表明不是系统调用。
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
#清CR0中任务已交换标志TS,并取CR0值。若其中协处理器仿真标志EM没有置位,说明不是
#EM引起的中断,则恢复任务协处理器状态,指向C函数math_state_restore(),并在返回时
#去执行ret_from_sys_call处的代码。
pushl $ret_from_sys_call #把下面跳转或调用的返回地址入栈。
clts # clear TS so that we can use math (清除TS,以便我们可以用软件协处理器。)^_^linus大神当时机器没有硬件协处理器
movl %cr0,%eax
testl $0x4,%eax # EM (math emulation bit)
je _math_state_restore #执行math_state_restore()(kernel/sched.c,92行)。
#若EM标志置位,则去执行数学仿真程序math_emulate()。
pushl %ebp
pushl %esi
pushl %edi
pushl $0 # temporary storage for ORIG_EIP
call _math_emulate #调用C函数(math/math_emulate.c,476行)。
addl $4,%esp #丢弃临时存储。
popl %edi
popl %esi
popl %ebp
ret
#int32 -- (int 0x20)时钟中断处理程序。中断频率设置为100Hz(include/linux/sched.h,4),
#定时芯片8253/8254实在(kernel/sched.c,438)处初始化的。因此这里jiffies每10毫秒加1。
#这段代码将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 # 保存ds、es并让其指向内核数据段。fs将用于system_call。
pushl $-1 # fill in -1 for orig_eax #填-1,表明不是系统调用。
#下面我们保存寄存器eax、ecx和edx。这是因为gcc编译器在调用函数时不会保存他们。
#这里也保存了ebx寄存器,因为在后面ret_from_sys_call中会用到它。
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 #de,es置为指向内核数据段。
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax #fs置为指向局部数据段(程序的数据段)。
mov %ax,%fs
incl _jiffies
#由于初始化中断控制芯片时乜有采用自动EOI,所以这里需要发指令结束该硬件中断。
movb $0x20,%al # EOI to interrupt controller #1
outb %al,$0x20
#下面从堆栈中取出指向系统调用代码的选择符(CS段寄存器值)中的当前特权级(0或3)并压入堆栈,
#作为do_timer的参数。do_timer()函数执行任务切换、计时等工作,在kernel/sched.c,324行实现。
movl CS(%esp),%eax
andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor)
pushl %eax
call _do_timer # 'do_timer(long CPL)' does everything from
addl $4,%esp # task switching to accounting ...
jmp ret_from_sys_call
#这是sys_execve()系统调用。取中断调用程序需的代码指针作为参数调用C函数do_execve().
#do_execve()在fs/exec.c,207行。
.align 2
_sys_execve:
lea EIP(%esp),%eax #eax指向堆栈中保存用户程序eip指针处。
pushl %eax
call _do_execve
addl $4,%esp #丢弃调用时压入栈的EIP值。
ret
#sys_fork()调用,用于创建子进程,是system_call功能2。原形在include/linux/sys.h中。
#首先调用C函数find_empty_process(),取得一个进程号last_pid。
#若返回负数则说明目前任务数组已满。然后调用copy_process()复制进程。
.align 2
_sys_fork:
call _find_empty_process #为新进程取得进程号last_pid。(kernel/fork.c,143)。
testl %eax,%eax #在eax中返回进程号。若返回负数则退出。
js 1f
push %gs
pushl %esi
pushl %edi
pushl %ebp
pushl %eax
call _copy_process #调用C函数copy_process()(kernel/fork.c,68)。
addl $20,%esp #丢弃这里所有压栈内容。
1: ret
#int 46 -- (int 0x2E)硬盘中断处理程序,响应硬件中断请求IRQ14。
#当请求的硬盘操作完成或出错就会发出此中断信号。(参见kernel/blk_drv/hd.c)。
#首先想8259A中断控制从芯片发送结束硬件中断指令(EOI),然后取变量do_hd中的函数指针放入edx
#寄存器中,并置do_hd为NULL,接着判断edx函数指针是否为空。如果为空,则给edx赋值指向
#unexpected_hd_interrupt(),用于显示出错信息。随后想8259A主芯片发送EOI指令,并调用edx中
#指针指向的函数:read_intr(0、write_intr()或unexpected_hd_interrupt()。
_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
#由于初始化中断控制芯片时没有采用自动EOI,所以这里需要发指令结束该硬件中断。
movb $0x20,%al
outb %al,$0xA0 # EOI to interrupt controller #1 #送从8259A。
jmp 1f # give port chance to breathe #这里jmp起延时作用。
1: jmp 1f
#do_hd定义为一个函数指针,将被赋值read_intr()或write_intr()函数地址。放到edx寄存器后
#就将do_hd指针变量置为NULL。然后测试得到的函数指针,若该指针为空,则赋予该指针指向C函数
#unexpected_hd_interrupt(),以处理未知硬盘中断。
1: xorl %edx,%edx
movl %edx,_hd_timeout #hd_timeout置为0.表示控制器已在规定时间内产生了中断。
xchgl _do_hd,%edx
testl %edx,%edx
jne 1f #若空,则让指针指向C函数unexpected_hd_interrupt()。
movl $_unexpected_hd_interrupt,%edx
1: outb %al,$0x20 #送8259A主芯片EOI指令(结束硬件中断)。
call *%edx # "interesting" way of handling intr. 上局调用do_hd指向的C函数。
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
#int32 -- (int 0x26)软盘驱动器中断处理程序,响应硬件中断请求IRQ6。
#器处理过程与上面对硬盘的处理基本一样。(kernel/blk_drv/floppy.c)。
#首先向8259A中断控制器主芯片发送EOI指令,然后取变量do_floppy中的函数指针放入eax寄存器中,
#并置do_floppy位NULL,接着判断eax函数指针是否为空。如为空,则给eax赋值指向unexpected_floppy_interrupt(),
#用于显示出错信息。随后调用eax指向的函数:
#rw_interrupt,seek_interrupt,recal_interrupt,reset_interrupt或unexpected_floppy_interrupt。
_floppy_interrupt:
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax #de,es置为内核数据段。
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax #fs置为调用程序的局部数据段。
mov %ax,%fs
movb $0x20,%al #送主8259A中断控制器EOI指令(结束硬件中断)。
outb %al,$0x20 # EOI to interrupt controller #1
xorl %eax,%eax
#do_floppy为函数指针,将被赋值实际处理C函数指针。
#该指针在被交换放到eax寄存器后就将do_floppy变量置空。
#然后测试eax中原指针是否为空若是则使指针指向C函数unexpected_floppy_interrupt()
xchgl _do_floppy,%eax
testl %eax,%eax #测试函数指针是否=NULL?
jne 1f #若空,则使指针指向C函数unexpected_floppy_interrupt()。
movl $_unexpected_floppy_interrupt,%eax
1: call *%eax # "interesting" way of handling intr. #上局调用do_floppy指向的函数。
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
#int39 -- (int 0x27)并行口中断处理程序,对应硬件中断请求信号IRQ7。
#本版本内核还未实现。这里只是发送EOI指令。
_parallel_interrupt:
pushl %eax
movb $0x20,%al
outb %al,$0x20
popl %eax
iret
8-4-3 其他信息
1、 GUN汇编语言的32位寻址方式
GNU汇编语言采用的是AT&T的汇编语言语法。32位寻址的规则格式为:
AT&T: immed32(basepointer,indexpointer,indexscale)
Inter:[basepointer+indexpointerindexscale+immed32]
(1)对一个指定的C语言变量寻址:
AT&T: _booga
Inter: [_booga]
(2)对寄存器内容指向的位置寻址:
AT&T: (%eax)
Inter: [eax]
(3)通过寄存器中的内容作为基址寻址一个变量:
AT&T: _variable(%eax)
Inter: [eax+_variable]
(4)在一个整数数组中寻址一个值(比例值为4):
AT&T: _array(,%eax,4)
Inter: [eax4+_array]
(5)使用直接数寻址偏移量:
对于C语言:(p+1)其中p是字符的指针char
AT&T: 1(%eax)其中eax中时p的值。
Inter: [eax+1]
(6)在一个8字节为一个记录的数组寻址指定的字符。其中eax中时指定的记录号,
ebx中时指定字符在记录中的偏移址:
AT&T: _array(%ebx,%eax,8)
Inter: [ebx+eax*8+_array]
2、 增加系统调用功能
(1)在kernel/sys.c添加系统的底层函数sys_sethostname
(2)在include/unistd.h文件中增加新系统调用功能号和原型定义。
#define __NR_sethostname 72
int sethostname(char *name,int len);
(3)在include/linux/sys.h文件中加入外部函数声明并
在函数指针表sys_call_table末端插入新系统调用处理函数的名称。
extern int sethostname(char *name,int len);
fn_ptr sys_call_table[] = {……,sethostname};
(4)修改sys_call.s程序 nr_system_calls = 82
改nr_system_calls = 83
(5)参照lib/目录下库函数的实现方法在libc库中国增加新的系统调用库函数sethostname
#define __LIBRARY__
#include <unistd.h>
_syscall2(int,sethostname,char *,name,int,len)
3、 在汇编程序中直接使用系统调用
.text
_entru:
movl $4,%eax #系统调用号,写操作。
movl $1,%ebx #写调用的参数,是文本描述符。数值1对应标准输出stdout。
movl $message,%ecx #参数,缓冲区指针。
movl $12,%edx #参数,写数据长度(数数下面字符串的长度(-:)。
int $0x80
movl $1,%eax #系统调用号,退出程序。
int $0x80
message:
.ascii "Helllo World\n"
运行结果