System Call在HW和user space提供一層抽象層,主要目的有:
- 為user space提供硬體抽象層。比如,讀取檔案時,不用管檔案所在的媒體類型與檔案儲存類型。
- System call能確保系統的安全與穩定。避免user space的無意或惡意的破壞。
除了exception和trap以外,System call是user space進入kernel space的唯一管道。
User space的programming主要是base on API(Application Programming Interface)並非system call,從programmer的觀點來看,關注的是API(如C library)而非system call。
System call的return type為long,主要是要相容64bit,return value通常代表失敗或成功,失敗時,error code當常寫入global variable “errno”。
典型的system call都以sys_開頭,如getpid()的system call為:
asmlinkage long sys_getpid(void) { return current->tgid; }
在Linux中(x86),將所有的system call存放在一個system call table中,透過system call number來所引(index)要執行的system call,儘管每個platform所implement的system call table和system call number都不同,但是原理都是相同的,首先會將system call number存放在某個特定的CPU register(X86放在eax),並將system call的參數也存放置其他的register(最多5個參數,x86依序為ebx、ecx、edx、esi和edi),接著透過int 0x80進入system call處理程序,透過system call number(eax)在system call table中找到相對應的system call,並且執行該system call,因為參數存放是先就定義好了,所以就可以在registers(x86依序為ebx、ecx、edx、esi和edi)中依序讀出要處理的參數,超過五個參數就會用structure來傳遞,而ioctl這個不定參數的system call是傳遞pointer的方式來存取,ioctl是一個不好的例子,因為定義不是很明確,system call最好是能定義明確。
新增system call “brook()”到kernel 2.6.32的步驟(x86)
- 新增一筆system call entry到sys_call_table中arch/x86/kernel/syscall_table_32.s。 直接在最後面加入”.long sys_brook”
- 定義brook的system call number,arch/x86/include/asm/unistd_32.h,並將NR_syscalls做遞增。
- 定義system call的原型,include/linux/syscalls.h。
- 加入至system call table中,arch/x86/kernel/syscall_table_32.S。
- 撰寫system call的內容。 建立一個新的資料夾名為”brook_syscall”,並將這個資料夾加入Makefile的core-y+=中。 brook_syscall/Makefile
- 撰寫Application測試system call 這邊我們撰寫一個類似C library這層的功能"brook_app/brook.h"
#define __NR_brook 337 #define __NR_syscalls 338
asmlinkage long sys_brook(int n, unsigned long arg);
.long sys_brook;
obj-y := brook.o
brook_syscall/brook.c
#include <linux/kernel.h> #include <linux/syscalls.h> #include <linux/uaccess.h> SYSCALL_DEFINE2(brook, int, n, unsigned long, arg) { int __user *p = (int __user *) arg; int i, x, sum = 0, err = 0; printk("n=%d, ", n); for (i = 0; i < n; i++) { err = get_user(x, p + i); sum += x; if (err) { return err; } printk("[%d]=%d, ", i, x); } return sum; }
頂層的Makefile要將brook_syscall加入core-y中
ifeq ($(KBUILD_EXTMOD),) core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ brook_syscall/
#include <linux/unistd.h> #include#define __NR_brook 337 int brook(int n, ...) { int ret; va_list ap; va_start(ap, n); ret = syscall(__NR_brook, n, ap); va_end(ap); return ret; }
application呼叫brook(),再由brook()轉call我們的system call "sys_brook()"
#include <stdio.h> #include "brook.h" int main(int argc, char *argv[]) { return printf("%d\n", brook(3, 3, 2, 1)); }
Kernel Version:2.6.32
參考資料:
- Linux Kernel Development 2nd, Novell Press
- http://pradeepkumar.org/2010/01/implementing-a-new-system-call-in-kernel-version-2-6-32.html
- Professional Linux Kernel Architecture, Wiley Publishing