內核提供了用戶進程和內核交互的接口,使得應用程序能夠受限制的訪問硬件設備。linux
提供這些接口主要是爲了保證系統穩定可靠,避免應用程序恣意妄行。redis
系統調用在用戶空間進程和硬件設備之間添加中間才能。做用有三:編程
API:應用程序不須要訪問內核,編程接口能夠組合成API安全
POSIX:依據大多數Unix系統接口創建的cookie
C庫:提供了Unix主要API,全部C程序均可以使用C庫app
例如,getpid中的系統調用:dom
SYSCALL_DEFINE0(getpid) { return task_tgid_vnr(current); //returns current->tgid }
SYSCALL_DEFINE0只是一個宏,它定義一個無參數的系統調用(所以數字0),展開後代碼以下:socket
asmlinkage long sys_getpid(void)
asmlinkage是一個限定詞,是一個編譯指令,通知編譯器僅從棧中提取該函數的參數。async
在Linux中,每一個系統調用被附於一個系統調用號。tcp
系統調用號,一旦分配就不能在更改,不然編譯好的應用程序會崩潰。
Linux中有一個"未實現"系統調用sys_ni_syscall(),會返回-ENOSYS。
內核記錄了系統調用表中全部已註冊過的系統調用的列表,存儲在sys_call_table中。定義於arch/i386/kernel/syscall_64.c
Linux系統調用比其餘操做系統執行的要快,上下文切換時間是一個重要的緣由,內核都被優化的簡潔高效。
通知內核的機制是靠軟中斷實現的:經過引起一個異常來促使系統切換到內核態去執行異常處理程序。
此時的異常處理程序就是系統調用處理程序。程序名字叫system_call(),而且與硬件系統密切相關。
system_call()經過將給定的系統調用號與NR_syscalls做比較來檢查有效性。若是它大於或者等於NR_syscalls,該函數就返回-ENOSYS。不然執行系統調用
call *sys_call_table(, %rax, 8)
參數依次用ebx、ecx、edx、esi和edi的順序存放前5個參數。
須要六個或六個以上參數的狀況很少見,此時,應該用一個單獨的寄存器存放指向全部這些參數在用戶空間地址的指針。
怎樣設計和實現一個系統調用是難題所在,而把它加到內核裏卻無須太多周折。
Linux中不提倡採用多用途的系統調用(一個系統調用經過傳遞不一樣的參數值來選擇完成不一樣的工做)。
若是用戶將不合法的輸入傳遞給內核,那麼系統的安全和穩定將面臨極大的考驗。
最重要的檢查就是檢查用戶提供的指針是否有效,在接收用戶空間的指針以前,內核必須保證:
向用戶空間寫入數據,內核提供了copy_to_user(),第一個參數是進程空間中的目的內存地址。第二個參數是內核空間的源地址,最後一個是數據長度。
從用戶空間讀取數據,內核提供了copy_from_user(),把第二個參數拷貝到第一個參數中,第三個參數是數據長度
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { char buffer[256]; /* 咱們只信任啓動系統的系統管理員 */ if(!capable(CAP_SYS_BOOT)) return -EPERM; /* 爲了安全起見,咱們須要"magic"參數 */ if(magic1 != LINUX_REBOOT_MAGIC1 || (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && magic2 != LINUX_REBOOT_MAGIC2B && magic2 != LINUX_REBOOT_MAGIC2C)) return -EINVAL; /* 當未設置pm_power_off時,請不要試圖讓power_off的代碼看起來像是能夠停機 * 而應該採用更簡單的方式 */ if((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off) cmd = LINUX_REBOOT_CMD_HALT; lock_kernel(); switch(cmd) { case LINUX_REBOOT_CMD_RESTART: kernel_restart(NULL); break; case LINUX_REBOOT_CMD_CAD_ON: C_A_D = 1; break; case LINUX_REBOOT_CMD_CAD_OFF: C_A_D = 0; break; case LINUX_REBOOT_CMD_HALT: kernel_halt(); unlock_kernel(); do_exit(0); break; case LINUX_REBOOT_CMD_POWER_OFF: kernel_power_off(); unlock_kernel(); do_exit(0); break; case LINUX_REBOOT_CMD_RESTART2: if(strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) { unlock_kernel(); return -EFAULT; } buffer[sizeof(buffer) - 1] = '\0'; kernel_restart(buffer); break; default: unlock_kernel(); return -EINVAL; } unlock_kernel(); return 0; }
具體capable()的權能和其對應的權限列表,能夠在<linux/capability.h>中找到
在進程上下文中,內核能夠休眠(好比系統調用阻塞或顯式調用schedule()的時候),而且能夠搶佔。
可以休眠說明系統調用可使用內核提供 ,休眠能力能給編程極大便利。
當系統調用返回的時候,控制權任然在system_call()中,最終會負責切換到用戶空間,而且用戶進程繼續執行下去。
當編寫完一個系統調用後,把註冊成一個正式的系統調用是件瑣碎的工做:
1)在系統調用表的最後加入一個表項。從0開始算起,系統調用在該表中的位置就是它的系統調用號
2)對於所支持的各類體系結構,系統調用號都必須定義於<asm/unistd.h>中
3)系統調用必須被編譯進內核映象(不能被編譯成模塊)。只要把它放進kernel/下的一個相關的文件中就能夠了,好比sys.c包含了各類各樣的系統調用。
文中說在entry.s中,多是x86結構。然而我看得是4412的內核,在 arch/arm/kernel/call.S :
/* * linux/arch/arm/kernel/calls.S * * Copyright (C) 1995-2005 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This file is included thrice in entry-common.S */ /* 0 */ CALL(sys_restart_syscall) CALL(sys_exit) CALL(sys_fork_wrapper) CALL(sys_read) CALL(sys_write) /* 5 */ CALL(sys_open) CALL(sys_close) CALL(sys_ni_syscall) /* was sys_waitpid */ CALL(sys_creat) CALL(sys_link) /* 10 */ CALL(sys_unlink) CALL(sys_execve_wrapper) CALL(sys_chdir) CALL(OBSOLETE(sys_time)) /* used by libc4 */ CALL(sys_mknod) /* 15 */ CALL(sys_chmod) CALL(sys_lchown16) CALL(sys_ni_syscall) /* was sys_break */ CALL(sys_ni_syscall) /* was sys_stat */ CALL(sys_lseek) /* 20 */ CALL(sys_getpid) CALL(sys_mount) CALL(OBSOLETE(sys_oldumount)) /* used by libc4 */ CALL(sys_setuid16) CALL(sys_getuid16) /* 25 */ CALL(OBSOLETE(sys_stime)) CALL(sys_ptrace) CALL(OBSOLETE(sys_alarm)) /* used by libc4 */ CALL(sys_ni_syscall) /* was sys_fstat */ CALL(sys_pause) /* 30 */ CALL(OBSOLETE(sys_utime)) /* used by libc4 */ CALL(sys_ni_syscall) /* was sys_stty */ CALL(sys_ni_syscall) /* was sys_getty */ CALL(sys_access) CALL(sys_nice) /* 35 */ CALL(sys_ni_syscall) /* was sys_ftime */ CALL(sys_sync) CALL(sys_kill) CALL(sys_rename) CALL(sys_mkdir) /* 40 */ CALL(sys_rmdir) CALL(sys_dup) CALL(sys_pipe) CALL(sys_times) CALL(sys_ni_syscall) /* was sys_prof */ /* 45 */ CALL(sys_brk) CALL(sys_setgid16) CALL(sys_getgid16) CALL(sys_ni_syscall) /* was sys_signal */ CALL(sys_geteuid16) /* 50 */ CALL(sys_getegid16) CALL(sys_acct) CALL(sys_umount) CALL(sys_ni_syscall) /* was sys_lock */ CALL(sys_ioctl) /* 55 */ CALL(sys_fcntl) CALL(sys_ni_syscall) /* was sys_mpx */ CALL(sys_setpgid) CALL(sys_ni_syscall) /* was sys_ulimit */ CALL(sys_ni_syscall) /* was sys_olduname */ /* 60 */ CALL(sys_umask) CALL(sys_chroot) CALL(sys_ustat) CALL(sys_dup2) CALL(sys_getppid) /* 65 */ CALL(sys_getpgrp) CALL(sys_setsid) CALL(sys_sigaction) CALL(sys_ni_syscall) /* was sys_sgetmask */ CALL(sys_ni_syscall) /* was sys_ssetmask */ /* 70 */ CALL(sys_setreuid16) CALL(sys_setregid16) CALL(sys_sigsuspend) CALL(sys_sigpending) CALL(sys_sethostname) /* 75 */ CALL(sys_setrlimit) CALL(OBSOLETE(sys_old_getrlimit)) /* used by libc4 */ CALL(sys_getrusage) CALL(sys_gettimeofday) CALL(sys_settimeofday) /* 80 */ CALL(sys_getgroups16) CALL(sys_setgroups16) CALL(OBSOLETE(sys_old_select)) /* used by libc4 */ CALL(sys_symlink) CALL(sys_ni_syscall) /* was sys_lstat */ /* 85 */ CALL(sys_readlink) CALL(sys_uselib) CALL(sys_swapon) CALL(sys_reboot) CALL(OBSOLETE(sys_old_readdir)) /* used by libc4 */ /* 90 */ CALL(OBSOLETE(sys_old_mmap)) /* used by libc4 */ CALL(sys_munmap) CALL(sys_truncate) CALL(sys_ftruncate) CALL(sys_fchmod) /* 95 */ CALL(sys_fchown16) CALL(sys_getpriority) CALL(sys_setpriority) CALL(sys_ni_syscall) /* was sys_profil */ CALL(sys_statfs) /* 100 */ CALL(sys_fstatfs) CALL(sys_ni_syscall) /* sys_ioperm */ CALL(OBSOLETE(ABI(sys_socketcall, sys_oabi_socketcall))) CALL(sys_syslog) CALL(sys_setitimer) /* 105 */ CALL(sys_getitimer) CALL(sys_newstat) CALL(sys_newlstat) CALL(sys_newfstat) CALL(sys_ni_syscall) /* was sys_uname */ /* 110 */ CALL(sys_ni_syscall) /* was sys_iopl */ CALL(sys_vhangup) CALL(sys_ni_syscall) CALL(OBSOLETE(sys_syscall)) /* call a syscall */ CALL(sys_wait4) /* 115 */ CALL(sys_swapoff) CALL(sys_sysinfo) CALL(OBSOLETE(ABI(sys_ipc, sys_oabi_ipc))) CALL(sys_fsync) CALL(sys_sigreturn_wrapper) /* 120 */ CALL(sys_clone_wrapper) CALL(sys_setdomainname) CALL(sys_newuname) CALL(sys_ni_syscall) /* modify_ldt */ CALL(sys_adjtimex) /* 125 */ CALL(sys_mprotect) CALL(sys_sigprocmask) CALL(sys_ni_syscall) /* was sys_create_module */ CALL(sys_init_module) CALL(sys_delete_module) /* 130 */ CALL(sys_ni_syscall) /* was sys_get_kernel_syms */ CALL(sys_quotactl) CALL(sys_getpgid) CALL(sys_fchdir) CALL(sys_bdflush) /* 135 */ CALL(sys_sysfs) CALL(sys_personality) CALL(sys_ni_syscall) /* reserved for afs_syscall */ CALL(sys_setfsuid16) CALL(sys_setfsgid16) /* 140 */ CALL(sys_llseek) CALL(sys_getdents) CALL(sys_select) CALL(sys_flock) CALL(sys_msync) /* 145 */ CALL(sys_readv) CALL(sys_writev) CALL(sys_getsid) CALL(sys_fdatasync) CALL(sys_sysctl) /* 150 */ CALL(sys_mlock) CALL(sys_munlock) CALL(sys_mlockall) CALL(sys_munlockall) CALL(sys_sched_setparam) /* 155 */ CALL(sys_sched_getparam) CALL(sys_sched_setscheduler) CALL(sys_sched_getscheduler) CALL(sys_sched_yield) CALL(sys_sched_get_priority_max) /* 160 */ CALL(sys_sched_get_priority_min) CALL(sys_sched_rr_get_interval) CALL(sys_nanosleep) CALL(sys_mremap) CALL(sys_setresuid16) /* 165 */ CALL(sys_getresuid16) CALL(sys_ni_syscall) /* vm86 */ CALL(sys_ni_syscall) /* was sys_query_module */ CALL(sys_poll) CALL(sys_nfsservctl) /* 170 */ CALL(sys_setresgid16) CALL(sys_getresgid16) CALL(sys_prctl) CALL(sys_rt_sigreturn_wrapper) CALL(sys_rt_sigaction) /* 175 */ CALL(sys_rt_sigprocmask) CALL(sys_rt_sigpending) CALL(sys_rt_sigtimedwait) CALL(sys_rt_sigqueueinfo) CALL(sys_rt_sigsuspend) /* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64)) CALL(ABI(sys_pwrite64, sys_oabi_pwrite64)) CALL(sys_chown16) CALL(sys_getcwd) CALL(sys_capget) /* 185 */ CALL(sys_capset) CALL(sys_sigaltstack_wrapper) CALL(sys_sendfile) CALL(sys_ni_syscall) /* getpmsg */ CALL(sys_ni_syscall) /* putpmsg */ /* 190 */ CALL(sys_vfork_wrapper) CALL(sys_getrlimit) CALL(sys_mmap2) CALL(ABI(sys_truncate64, sys_oabi_truncate64)) CALL(ABI(sys_ftruncate64, sys_oabi_ftruncate64)) /* 195 */ CALL(ABI(sys_stat64, sys_oabi_stat64)) CALL(ABI(sys_lstat64, sys_oabi_lstat64)) CALL(ABI(sys_fstat64, sys_oabi_fstat64)) CALL(sys_lchown) CALL(sys_getuid) /* 200 */ CALL(sys_getgid) CALL(sys_geteuid) CALL(sys_getegid) CALL(sys_setreuid) CALL(sys_setregid) /* 205 */ CALL(sys_getgroups) CALL(sys_setgroups) CALL(sys_fchown) CALL(sys_setresuid) CALL(sys_getresuid) /* 210 */ CALL(sys_setresgid) CALL(sys_getresgid) CALL(sys_chown) CALL(sys_setuid) CALL(sys_setgid) /* 215 */ CALL(sys_setfsuid) CALL(sys_setfsgid) CALL(sys_getdents64) CALL(sys_pivot_root) CALL(sys_mincore) /* 220 */ CALL(sys_madvise) CALL(ABI(sys_fcntl64, sys_oabi_fcntl64)) CALL(sys_ni_syscall) /* TUX */ CALL(sys_ni_syscall) CALL(sys_gettid) /* 225 */ CALL(ABI(sys_readahead, sys_oabi_readahead)) CALL(sys_setxattr) CALL(sys_lsetxattr) CALL(sys_fsetxattr) CALL(sys_getxattr) /* 230 */ CALL(sys_lgetxattr) CALL(sys_fgetxattr) CALL(sys_listxattr) CALL(sys_llistxattr) CALL(sys_flistxattr) /* 235 */ CALL(sys_removexattr) CALL(sys_lremovexattr) CALL(sys_fremovexattr) CALL(sys_tkill) CALL(sys_sendfile64) /* 240 */ CALL(sys_futex) CALL(sys_sched_setaffinity) CALL(sys_sched_getaffinity) CALL(sys_io_setup) CALL(sys_io_destroy) /* 245 */ CALL(sys_io_getevents) CALL(sys_io_submit) CALL(sys_io_cancel) CALL(sys_exit_group) CALL(sys_lookup_dcookie) /* 250 */ CALL(sys_epoll_create) CALL(ABI(sys_epoll_ctl, sys_oabi_epoll_ctl)) CALL(ABI(sys_epoll_wait, sys_oabi_epoll_wait)) CALL(sys_remap_file_pages) CALL(sys_ni_syscall) /* sys_set_thread_area */ /* 255 */ CALL(sys_ni_syscall) /* sys_get_thread_area */ CALL(sys_set_tid_address) CALL(sys_timer_create) CALL(sys_timer_settime) CALL(sys_timer_gettime) /* 260 */ CALL(sys_timer_getoverrun) CALL(sys_timer_delete) CALL(sys_clock_settime) CALL(sys_clock_gettime) CALL(sys_clock_getres) /* 265 */ CALL(sys_clock_nanosleep) CALL(sys_statfs64_wrapper) CALL(sys_fstatfs64_wrapper) CALL(sys_tgkill) CALL(sys_utimes) /* 270 */ CALL(sys_arm_fadvise64_64) CALL(sys_pciconfig_iobase) CALL(sys_pciconfig_read) CALL(sys_pciconfig_write) CALL(sys_mq_open) /* 275 */ CALL(sys_mq_unlink) CALL(sys_mq_timedsend) CALL(sys_mq_timedreceive) CALL(sys_mq_notify) CALL(sys_mq_getsetattr) /* 280 */ CALL(sys_waitid) CALL(sys_socket) CALL(ABI(sys_bind, sys_oabi_bind)) CALL(ABI(sys_connect, sys_oabi_connect)) CALL(sys_listen) /* 285 */ CALL(sys_accept) CALL(sys_getsockname) CALL(sys_getpeername) CALL(sys_socketpair) CALL(sys_send) /* 290 */ CALL(ABI(sys_sendto, sys_oabi_sendto)) CALL(sys_recv) CALL(sys_recvfrom) CALL(sys_shutdown) CALL(sys_setsockopt) /* 295 */ CALL(sys_getsockopt) CALL(ABI(sys_sendmsg, sys_oabi_sendmsg)) CALL(sys_recvmsg) CALL(ABI(sys_semop, sys_oabi_semop)) CALL(sys_semget) /* 300 */ CALL(sys_semctl) CALL(sys_msgsnd) CALL(sys_msgrcv) CALL(sys_msgget) CALL(sys_msgctl) /* 305 */ CALL(sys_shmat) CALL(sys_shmdt) CALL(sys_shmget) CALL(sys_shmctl) CALL(sys_add_key) /* 310 */ CALL(sys_request_key) CALL(sys_keyctl) CALL(ABI(sys_semtimedop, sys_oabi_semtimedop)) /* vserver */ CALL(sys_ni_syscall) CALL(sys_ioprio_set) /* 315 */ CALL(sys_ioprio_get) CALL(sys_inotify_init) CALL(sys_inotify_add_watch) CALL(sys_inotify_rm_watch) CALL(sys_mbind) /* 320 */ CALL(sys_get_mempolicy) CALL(sys_set_mempolicy) CALL(sys_openat) CALL(sys_mkdirat) CALL(sys_mknodat) /* 325 */ CALL(sys_fchownat) CALL(sys_futimesat) CALL(ABI(sys_fstatat64, sys_oabi_fstatat64)) CALL(sys_unlinkat) CALL(sys_renameat) /* 330 */ CALL(sys_linkat) CALL(sys_symlinkat) CALL(sys_readlinkat) CALL(sys_fchmodat) CALL(sys_faccessat) /* 335 */ CALL(sys_pselect6) CALL(sys_ppoll) CALL(sys_unshare) CALL(sys_set_robust_list) CALL(sys_get_robust_list) /* 340 */ CALL(sys_splice) CALL(sys_sync_file_range2) CALL(sys_tee) CALL(sys_vmsplice) CALL(sys_move_pages) /* 345 */ CALL(sys_getcpu) CALL(sys_epoll_pwait) CALL(sys_kexec_load) CALL(sys_utimensat) CALL(sys_signalfd) /* 350 */ CALL(sys_timerfd_create) CALL(sys_eventfd) CALL(sys_fallocate) CALL(sys_timerfd_settime) CALL(sys_timerfd_gettime) /* 355 */ CALL(sys_signalfd4) CALL(sys_eventfd2) CALL(sys_epoll_create1) CALL(sys_dup3) CALL(sys_pipe2) /* 360 */ CALL(sys_inotify_init1) CALL(sys_preadv) CALL(sys_pwritev) CALL(sys_rt_tgsigqueueinfo) CALL(sys_perf_event_open) /* 365 */ CALL(sys_recvmmsg) CALL(sys_accept4) CALL(sys_fanotify_init) CALL(sys_fanotify_mark) CALL(sys_prlimit64) /* 370 */ CALL(sys_name_to_handle_at) CALL(sys_open_by_handle_at) CALL(sys_clock_adjtime) CALL(sys_syncfs) CALL(sys_sendmmsg) /* 375 */ CALL(sys_setns) #ifndef syscalls_counted .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls #define syscalls_counted #endif .rept syscalls_padding CALL(sys_ni_syscall) .endr
要把新的系統調用加到這個表的末尾。
接下來咱們要把系統調用號加入到<asm/unistd.h>中,它的格式以下:
/* * This file contains the system call numbers. */ #define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0) #define __NR_exit (__NR_SYSCALL_BASE+ 1) #define __NR_fork (__NR_SYSCALL_BASE+ 2) #define __NR_read (__NR_SYSCALL_BASE+ 3) #define __NR_write (__NR_SYSCALL_BASE+ 4) #define __NR_open (__NR_SYSCALL_BASE+ 5) #define __NR_close (__NR_SYSCALL_BASE+ 6) #define __NR_name_to_handle_at (__NR_SYSCALL_BASE+370) #define __NR_open_by_handle_at (__NR_SYSCALL_BASE+371) #define __NR_clock_adjtime (__NR_SYSCALL_BASE+372) #define __NR_syncfs (__NR_SYSCALL_BASE+373) #define __NR_sendmmsg (__NR_SYSCALL_BASE+374) #define __NR_setns (__NR_SYSCALL_BASE+375)
加入一項好比:#define __NR_foo 338
4)而後它是實現函數,能夠放在最緊密關聯的代碼中去。好比功能和調度相關,能夠放在sched.c中
#include <asm/page.h> /* * sys_foo - 每一個人喜歡的系統調用 */ asmlinkage long sys_foo(void) { return THREAD_SIZE; }
一般,系統調用靠C庫支持。用戶程序經過包含標準頭文件並和C庫連接,就可使用系統調用。
Linux自己提供共了一組宏,用於直接對系統調用進行訪問。它會設置好寄存器並調用陷入指令。這些宏是_syscalln(),其中n範圍是0到6,表明須要傳遞給系統調用的參數個數。
long open(const char *filename, int flags, int mode) /* 而不靠庫支持,直接調用今後系統調用的宏形式爲 */ #define NR_open 5 _syscall3(long, open, const char *, filename, int, flags, int, mode) /* 這樣應用程序就能夠直接使用open */
對於每一個宏來講,都有2+2xn個參數。第一個參數對應着系統調用的返回值類型。第二個參數是系統調用的名稱。再之後是按照系統調用參數的順序排列的每一個參數的類型和名稱。
而後是foo()調用的測試代碼:
#define __NR_foo 283 __syscall0(long, foo) int main() { long stack_size; stack_size = foo(); printf("The kernel stack size is %ld\n", stack_size); return 0; }
儘管創建一個新的系統調用很是容易,但不提倡這麼作。一般都會有更好的辦法用來代替新建一個系統調用以做實現。
創建一個新的系統調用的好處:
問題是:
替代方法: