Linux内核设计与实现 总结笔记(第五章)系统调用
系統(tǒng)調(diào)用
內(nèi)核提供了用戶進(jìn)程和內(nèi)核交互的接口,使得應(yīng)用程序可以受限制的訪問硬件設(shè)備。
提供這些接口主要是為了保證系統(tǒng)穩(wěn)定可靠,避免應(yīng)用程序恣意妄行。
?
一、內(nèi)核通信
系統(tǒng)調(diào)用在用戶空間進(jìn)程和硬件設(shè)備之間添加中間才能。作用有三:
- 為用戶空間提供一種硬件的抽象接口。無需理會(huì)物理結(jié)構(gòu)是怎么樣的。
- 系統(tǒng)調(diào)用保證了系統(tǒng)的穩(wěn)定和安全。內(nèi)核可以有選擇的對(duì)其訪問進(jìn)行控制。
- 每個(gè)進(jìn)程都運(yùn)行在虛擬系統(tǒng)中,用戶空間和系統(tǒng)的其余部分提供這樣一層公共接口。
?
二、API、POSIX和C庫
API:應(yīng)用程序不需要訪問內(nèi)核,編程接口可以組合成API
POSIX:依據(jù)大多數(shù)Unix系統(tǒng)接口建立的
C庫:提供了Unix主要API,所有C程序都可以使用C庫
?
三、系統(tǒng)調(diào)用
例如,getpid中的系統(tǒng)調(diào)用:
SYSCALL_DEFINE0(getpid) {return task_tgid_vnr(current); //returns current->tgid }SYSCALL_DEFINE0只是一個(gè)宏,它定義一個(gè)無參數(shù)的系統(tǒng)調(diào)用(因此數(shù)字0),展開后代碼如下:
asmlinkage long sys_getpid(void)asmlinkage是一個(gè)限定詞,是一個(gè)編譯指令,通知編譯器僅從棧中提取該函數(shù)的參數(shù)。
?
3.1 系統(tǒng)調(diào)用號(hào)
在Linux中,每個(gè)系統(tǒng)調(diào)用被附于一個(gè)系統(tǒng)調(diào)用號(hào)。
系統(tǒng)調(diào)用號(hào),一旦分配就不能在更改,否則編譯好的應(yīng)用程序會(huì)崩潰。
Linux中有一個(gè)"未實(shí)現(xiàn)"系統(tǒng)調(diào)用sys_ni_syscall(),會(huì)返回-ENOSYS。
內(nèi)核記錄了系統(tǒng)調(diào)用表中所有已注冊(cè)過的系統(tǒng)調(diào)用的列表,存儲(chǔ)在sys_call_table中。定義于arch/i386/kernel/syscall_64.c
?
3.2 系統(tǒng)調(diào)用的性能
Linux系統(tǒng)調(diào)用比其他操作系統(tǒng)執(zhí)行的要快,上下文切換時(shí)間是一個(gè)重要的原因,內(nèi)核都被優(yōu)化的簡(jiǎn)潔高效。
?
四、系統(tǒng)調(diào)用處理程序
通知內(nèi)核的機(jī)制是靠軟中斷實(shí)現(xiàn)的:通過引發(fā)一個(gè)異常來促使系統(tǒng)切換到內(nèi)核態(tài)去執(zhí)行異常處理程序。
此時(shí)的異常處理程序就是系統(tǒng)調(diào)用處理程序。程序名字叫system_call(),并且與硬件系統(tǒng)密切相關(guān)。
system_call()通過將給定的系統(tǒng)調(diào)用號(hào)與NR_syscalls作比較來檢查有效性。如果它大于或者等于NR_syscalls,該函數(shù)就返回-ENOSYS。否則執(zhí)行系統(tǒng)調(diào)用
call *sys_call_table(, %rax, 8)?
4.2 參數(shù)傳遞
參數(shù)依次用ebx、ecx、edx、esi和edi的順序存放前5個(gè)參數(shù)。
需要六個(gè)或六個(gè)以上參數(shù)的情況不多見,此時(shí),應(yīng)該用一個(gè)單獨(dú)的寄存器存放指向所有這些參數(shù)在用戶空間地址的指針。
?
五、系統(tǒng)調(diào)用的實(shí)現(xiàn)
怎樣設(shè)計(jì)和實(shí)現(xiàn)一個(gè)系統(tǒng)調(diào)用是難題所在,而把它加到內(nèi)核里卻無須太多周折。
?
5.1 實(shí)現(xiàn)系統(tǒng)調(diào)用
Linux中不提倡采用多用途的系統(tǒng)調(diào)用(一個(gè)系統(tǒng)調(diào)用通過傳遞不同的參數(shù)值來選擇完成不同的工作)。
- 系統(tǒng)調(diào)用的接口應(yīng)該力求簡(jiǎn)介,參數(shù)盡可能少。并且力求穩(wěn)定,不做改動(dòng)。
- 設(shè)計(jì)接口的時(shí)候要盡量為將來多做考慮,系統(tǒng)調(diào)用設(shè)計(jì)的越通用越好。
- 要時(shí)刻注意可移植性和健壯性。
?
5.2 參數(shù)驗(yàn)證
如果用戶將不合法的輸入傳遞給內(nèi)核,那么系統(tǒng)的安全和穩(wěn)定將面臨極大的考驗(yàn)。
最重要的檢查就是檢查用戶提供的指針是否有效,在接收用戶空間的指針之前,內(nèi)核必須保證:
- 指針指向的內(nèi)存區(qū)是用戶空間,進(jìn)程決不能哄騙內(nèi)核去讀其他進(jìn)程的數(shù)據(jù)。
- 指針知悉將發(fā)的內(nèi)存區(qū)域在進(jìn)程的地址空間里,進(jìn)程決不能哄騙內(nèi)核去讀其他進(jìn)程的數(shù)據(jù)。
- 如果是讀,該內(nèi)存應(yīng)被標(biāo)記為可讀;如果是寫,該內(nèi)存應(yīng)被標(biāo)記為可寫;如果可執(zhí)行,該內(nèi)存應(yīng)被標(biāo)記為可執(zhí)行。進(jìn)程不能繞過內(nèi)存訪問限制。
向用戶空間寫入數(shù)據(jù),內(nèi)核提供了copy_to_user(),第一個(gè)參數(shù)是進(jìn)程空間中的目的內(nèi)存地址。第二個(gè)參數(shù)是內(nèi)核空間的源地址,最后一個(gè)是數(shù)據(jù)長(zhǎng)度。
從用戶空間讀取數(shù)據(jù),內(nèi)核提供了copy_from_user(),把第二個(gè)參數(shù)拷貝到第一個(gè)參數(shù)中,第三個(gè)參數(shù)是數(shù)據(jù)長(zhǎng)度
SYSCALL_DEFINE4(reboot,int, magic1,int, magic2,unsigned int, cmd,void __user *, arg) {char buffer[256];/* 我們只信任啟動(dòng)系統(tǒng)的系統(tǒng)管理員 */if(!capable(CAP_SYS_BOOT))return -EPERM;/* 為了安全起見,我們需要"magic"參數(shù) */if(magic1 != LINUX_REBOOT_MAGIC1 ||(magic2 != LINUX_REBOOT_MAGIC2 &&magic2 != LINUX_REBOOT_MAGIC2A &&magic2 != LINUX_REBOOT_MAGIC2B &&magic2 != LINUX_REBOOT_MAGIC2C))return -EINVAL;/* 當(dāng)未設(shè)置pm_power_off時(shí),請(qǐng)不要試圖讓power_off的代碼看起來像是可以停機(jī)* 而應(yīng)該采用更簡(jiǎn)單的方式 */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; } SYSCALL_DEFINE4具體capable()的權(quán)能和其對(duì)應(yīng)的權(quán)限列表,可以在<linux/capability.h>中找到
?
六、系統(tǒng)上下文調(diào)用
在進(jìn)程上下文中,內(nèi)核可以休眠(比如系統(tǒng)調(diào)用阻塞或顯式調(diào)用schedule()的時(shí)候),并且可以搶占。
能夠休眠說明系統(tǒng)調(diào)用可以使用內(nèi)核提供 ,休眠能力能給編程極大便利。
當(dāng)系統(tǒng)調(diào)用返回的時(shí)候,控制權(quán)任然在system_call()中,最終會(huì)負(fù)責(zé)切換到用戶空間,并且用戶進(jìn)程繼續(xù)執(zhí)行下去。
?
6.1 綁定一個(gè)系統(tǒng)調(diào)用的最后步驟
當(dāng)編寫完一個(gè)系統(tǒng)調(diào)用后,把注冊(cè)成一個(gè)正式的系統(tǒng)調(diào)用是件瑣碎的工作:
1)在系統(tǒng)調(diào)用表的最后加入一個(gè)表項(xiàng)。從0開始算起,系統(tǒng)調(diào)用在該表中的位置就是它的系統(tǒng)調(diào)用號(hào)
2)對(duì)于所支持的各種體系結(jié)構(gòu),系統(tǒng)調(diào)用號(hào)都必須定義于<asm/unistd.h>中
3)系統(tǒng)調(diào)用必須被編譯進(jìn)內(nèi)核映象(不能被編譯成模塊)。只要把它放進(jìn)kernel/下的一個(gè)相關(guān)的文件中就可以了,比如sys.c包含了各種各樣的系統(tǒng)調(diào)用。
文中說在entry.s中,可能是x86結(jié)構(gòu)。然而我看得是4412的內(nèi)核,在 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_paddingCALL(sys_ni_syscall) .endr syscall表要把新的系統(tǒng)調(diào)用加到這個(gè)表的末尾。
接下來我們要把系統(tǒng)調(diào)用號(hào)加入到<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) asm/unistd.h加入一項(xiàng)比如:#define __NR_foo 338
?
4)然后它是實(shí)現(xiàn)函數(shù),可以放在最緊密關(guān)聯(lián)的代碼中去。比如功能和調(diào)度相關(guān),可以放在sched.c中
#include <asm/page.h> /** sys_foo - 每個(gè)人喜歡的系統(tǒng)調(diào)用*/ asmlinkage long sys_foo(void) {return THREAD_SIZE; } sys_foo?
6.2 用戶空間訪問系統(tǒng)調(diào)用
通常,系統(tǒng)調(diào)用靠C庫支持。用戶程序通過包含標(biāo)準(zhǔn)頭文件并和C庫鏈接,就可以使用系統(tǒng)調(diào)用。
Linux本身提供共了一組宏,用于直接對(duì)系統(tǒng)調(diào)用進(jìn)行訪問。它會(huì)設(shè)置好寄存器并調(diào)用陷入指令。這些宏是_syscalln(),其中n范圍是0到6,代表需要傳遞給系統(tǒng)調(diào)用的參數(shù)個(gè)數(shù)。
long open(const char *filename, int flags, int mode) /* 而不靠庫支持,直接調(diào)用從此系統(tǒng)調(diào)用的宏形式為 */ #define NR_open 5 _syscall3(long, open, const char *, filename, int, flags, int, mode) /* 這樣應(yīng)用程序就可以直接使用open */ open系統(tǒng)調(diào)用例子對(duì)于每個(gè)宏來說,都有2+2xn個(gè)參數(shù)。第一個(gè)參數(shù)對(duì)應(yīng)著系統(tǒng)調(diào)用的返回值類型。第二個(gè)參數(shù)是系統(tǒng)調(diào)用的名稱。再以后是按照系統(tǒng)調(diào)用參數(shù)的順序排列的每個(gè)參數(shù)的類型和名稱。
?
然后是foo()調(diào)用的測(cè)試代碼:
#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; } __NR_foo?
6.3 為什么不通過系統(tǒng)調(diào)用的方式實(shí)現(xiàn)
盡管建立一個(gè)新的系統(tǒng)調(diào)用非常容易,但不提倡這么做。通常都會(huì)有更好的辦法用來代替新建一個(gè)系統(tǒng)調(diào)用以作實(shí)現(xiàn)。
建立一個(gè)新的系統(tǒng)調(diào)用的好處:
- 系統(tǒng)調(diào)用創(chuàng)建容易且使用方便。
- Linux系統(tǒng)調(diào)用的高性能顯而易見。
?
問題是:
- 你需要一個(gè)系統(tǒng)調(diào)用號(hào),而這個(gè)需要一個(gè)內(nèi)核在處于開發(fā)版本的時(shí)候由官方分配給你
- 系統(tǒng)調(diào)用被加入穩(wěn)定內(nèi)核后就被固化了,為了避免應(yīng)用程序的崩潰,它的接口不允許做改動(dòng)
- 需要將系統(tǒng)調(diào)用分別注冊(cè)到每個(gè)需要支持的體系結(jié)構(gòu)中去
- 在腳本中不容易條用系統(tǒng)調(diào)用,也不能從文件系統(tǒng)直接訪問系統(tǒng)調(diào)用
- 由于你需要系統(tǒng)調(diào)用號(hào),因此在主內(nèi)核樹之外是很難維護(hù)和使用系統(tǒng)調(diào)用的
- 如果僅僅進(jìn)行簡(jiǎn)單的信息交換,系統(tǒng)調(diào)用就大材小用了
?
替代方法:
- 實(shí)現(xiàn)一個(gè)設(shè)備節(jié)點(diǎn),并對(duì)此實(shí)現(xiàn)read()和write()。使用ioctl()對(duì)特定的設(shè)置進(jìn)行操作或者對(duì)特定的信息進(jìn)行檢索。
- 像信號(hào)量這樣的某些接口,可以用文件描述符來表示,因此就可以按上述方式對(duì)其進(jìn)行操作。
- 把增加的信息作為一個(gè)文件放在sysfs的合適位置。
?
轉(zhuǎn)載于:https://www.cnblogs.com/ch122633/p/9982521.html
總結(jié)
以上是生活随笔為你收集整理的Linux内核设计与实现 总结笔记(第五章)系统调用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 以撒的结合开mod闪退?
- 下一篇: 化妆品备案名称使用规范?