x86架構
trap_init
在系統啟動的時候start_kernel會調用trap_init來初始化異常向量表
[code]start_kernel
trap_init
set_system_trap_gate(SYSCALL_VECTOR, &system_call);
...
memcpy(&idt[entry], gate, sizeof(*gate));
設置0x80號軟中斷的服務程序為system_call, system_call是所有系統調用的總入口.
當進程執行到用戶程序的系統調用命令時,實際上執行了由宏命令_syscallN()展開的函數。系統調用的參數由各通用寄存器傳遞,比如通過eax寄存器傳遞系統調用號和系統調用返回值,通過ebx/ecx/edx/esi/edi傳遞系統調用參數,然後執行INT 0x80,以內核態進入入口地址system_call。
system_call
在arch/x86/kernel/entry_32.S文件中定義了system_call,在system_call裡面調用了sys_call_table
[code]ENTRY(system_call)
RING0_INT_FRAME # can't unwind into user space anyway
ASM_CLAC
pushl_cfi %eax # save orig_eax
SAVE_ALL
GET_THREAD_INFO(%ebp)
# system call tracing in operation / emulation
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(NR_syscalls), %eax
jae syscall_badsys
syscall_call:
call *sys_call_table(,%eax,4)
movl %eax,PT_EAX(%esp) # store the return value
syscall_exit:
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
# setting need_resched or sigpending
# between sampling and the iret
TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
testl $_TIF_ALLWORK_MASK, %ecx # current->work
jne syscall_exit_work
...
ENDPROC(system_call)
sys_call_table
sys_call_table是在哪裡定義的:
[code]const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
/*
* Smells like a compiler bug -- it doesn't work
* when the & below is removed.
*/
[0 ... __NR_syscall_max] = &sys_ni_syscall,
#include <asm/syscalls_32.h>
};
編譯內核的時候,當執行到文件/usr/src/linux-3.10.21/arch/x86/syscalls/Makefile時,該文件會執行/usr/src/linux-3.10.21/arch/x86/syscalls/目錄下的shell腳本syscalltbl.sh,該腳本將同目錄下的syscall_32.tbl文件作為輸入,然後生成文件/usr/src/linux-3.10.21/arch/x86/include/generated/asm/syscalls_32.h,這個文件正是sys_call_table定義中包含的文件asm/syscalls_32.h。
來看下腳本syscalltbl.sh
[code]#!/bin/sh
in="$1"
out="$2"
grep '^[0-9]' "$in" | sort -n | (
while read nr abi name entry compat; do
abi=`echo "$abi" | tr '[a-z]' '[A-Z]'`
if [ -n "$compat" ]; then
echo "__SYSCALL_${abi}($nr, $entry, $compat)"
elif [ -n "$entry" ]; then
echo "__SYSCALL_${abi}($nr, $entry, $entry)"
fi
done
) > "$out"
其中in和out分別代表的就是syscall_32.tbl和syscalls_32.h文件的路徑。腳本大概意思就是讀取syscall_32.tbl內容,然後構造語句__SYSCALL_abi(nr, entry,entry)”。
輸入文件syscall_32.tbl部分內容如下:
[code]#
# 32-bit system call numbers and entry vectors
#
# The format is:
# <number> <abi> <name> <entry point> <compat entry point>
#
# The abi is always "i386" for this file.
#
0 i386 restart_syscall sys_restart_syscall
1 i386 exit sys_exit
2 i386 fork sys_fork stub32_fork
3 i386 read sys_read
4 i386 write sys_write
5 i386 open sys_open compat_sys_open
6 i386 close sys_close
輸出文件syscalls_32.h的部分內容:
[code]__SYSCALL_I386(0, sys_restart_syscall, sys_restart_syscall)
__SYSCALL_I386(1, sys_exit, sys_exit)
__SYSCALL_I386(2, sys_fork, stub32_fork)
__SYSCALL_I386(3, sys_read, sys_read)
__SYSCALL_I386(4, sys_write, sys_write)
__SYSCALL_I386(5, sys_open, compat_sys_open)
__SYSCALL_I386(6, sys_close, sys_close)
所以sys_call_table的定義中包含了asm/syscall_32.h,就相當於包含了上面這麼多宏定義,sys_call_table就是采用這種方法定義的。
添加自己的系統調用
1) 在文件/usr/src/linux-3.10/arch/x86/syscalls/syscall_32.tbl中加入自定義的系統調用號和函數名
2) 在文件/usr/src/linux-3.10/arch/x86/include/asm/syscalls.h文件中加入sys_foo函數的聲明
3) 在文件/usr/src/linux-3.10/kernel/sys.c文件中加入對sys_foo的定義
4) 編譯和安裝內核
測試自定義的系統調用
[code]#include<stdio.h>
#define __NR_foo 351
int main(void)
{
int rs;
rs = syscall(__NR_foo);
if (!rs)
printf("syscall success!\n");
return 0;
}
代碼中用到了syscall函數,這個函數功能就是根據給定的系統調用號來調用系統調用
編譯運行上面的代碼,用dmesg可以看到系統輸出的信息:
MIPS架構
trap_init
和x86架構一樣,在系統啟動的時候start_kernel會調用trap_init來初始化異常向量表
[code]start_kernel
trap_init
set_except_vector(8, handle_sys);
exception_handlers
= handler;
memcpy((void *) (RLX_TRAP_VEC_BASE), &rlx_trap_dispatch, RLX_TRAP_VEC_SIZE);
RLX_TRAP_VEC_BASE的地址是0x8000080,相當於是將異常處理函數rlx_trap_dispatch的地址復制到0x8000080的地方,當發生異常中斷的時候,便會跳到rlx_trap_dispatch的地方執行。
rlx_trap_dispatch
在arch/rlx/genex.S文件中定義
[code]NESTED(rlx_trap_dispatch, 0, sp)
.set push
.set noat
mfc0 k1, CP0_CAUSE
andi k1, k1, 0x7c
PTR_L k0, exception_handlers(k1)
jr k0
.set pop
END(rlx_trap_dispatch)
這個異常處理函數又會調用exception_handlers, 他就是之前用set_except_vector函數賦值的exception_handlers數組,這裡我們只關心去調用handle_sys函數。handle_sys函數在arch/rlx/kernel/scall32-o32.S中定義,他裡面又調用了sys_call_table。
sys_call_table
[code] .macro syscalltable
sys sys_syscall 8 /* 4000 */
sys sys_exit 1
sys __sys_fork 0
sys sys_read 3
sys sys_write 3
sys sys_open 3 /* 4005 */
sys sys_close 1
...
.type sys_call_table,@object
EXPORT(sys_call_table)
unistd.h
文件include/uapi/asm-generic/unistd.h為每個系統調用規定了唯一的編號,根據這個編號,可以在系統調用表sys_call_table中找到對應表項的內容,他正好是該系統調用的響應函數sys_name的入口地址。
[code]...
/* net/socket.c */
#define __NR_socket 198
__SYSCALL(__NR_socket, sys_socket)
#define __NR_socketpair 199
__SYSCALL(__NR_socketpair, sys_socketpair)
#define __NR_bind 200
__SYSCALL(__NR_bind, sys_bind)
#define __NR_listen 201
__SYSCALL(__NR_listen, sys_listen)
#define __NR_accept 202
__SYSCALL(__NR_accept, sys_accept)
#define __NR_connect 203
__SYSCALL(__NR_connect, sys_connect)
#define __NR_getsockname 204
__SYSCALL(__NR_getsockname, sys_getsockname)
#define __NR_getpeername 205
__SYSCALL(__NR_getpeername, sys_getpeername)
#define __NR_sendto 206
__SYSCALL(__NR_sendto, sys_sendto)
#define __NR_recvfrom 207
__SC_COMP(__NR_recvfrom, sys_recvfrom, compat_sys_recvfrom)
...
參考文章
linux系統調用的基本原理以及添加系統調用的方法
linux自定義系統調用