linux系統呼叫原理
x86架構
trap_init
在系統啟動的時候start_kernel會呼叫trap_init來初始化異常向量表
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
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是在哪裡定義的:
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
#!/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部分內容如下:
#
# 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的部分內容:
__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) 編譯和安裝核心
測試自定義的系統呼叫
#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來初始化異常向量表
start_kernel
trap_init
set_except_vector(8, handle_sys);
exception_handlers[n] = 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檔案中定義
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
.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的入口地址。
...
/* 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)
...