本文共 12469 字,大约阅读时间需要 41 分钟。
1/解释了系统调用所在的层次
2/系统调用接口的过程(无代码)
3/系统调用的不常用的两种调用方式
4/linux-0.11的调用路径
5/glibc-2.25和linux-3.10中的调用路径
上层系统调用1/由于安全的问题,出现了系统调用,系统调用是内核提供的 唯一的 上层访问内核的 接口2/把用户从底层的硬件编程中解放出来3/使用户程序具有可移植性内核
系统调用不同的是他仅仅在我们开来就是一个函数,而系统调用是通过软中断向内核发出一个明确的功能请求那么还有一个问题就是所谓的软中断的入口参数是什么呢,实际上就和函数的参数列表一样,我们除了知道他的中断号还要知道功能代码还有就是要知道他的输入是什么(见linux内核分析一)这就是入口参数。那么系统调用 通过软中断或系统调用指令向内核发出一个明确的请求,内核将调用内核相关函数来实现(如sys_read() , sys_write() , sys_fork())。用户程序不能直接调用这些Sys_read,sys_write等函数。这些函数运行在内核态。理论:就如同调用了一个命令 int 80 intrupt_num argv1 argv2 ...就像这样的一个指令会陷入中断,并且会获取参数.根据不同的中断号 进行相应的中断处理函数实例:好比上层我调用了一个open file1 //open 对应的中断号是60那么 到底层就对应 int 80 60 file1然后下面调用 60 对应的中断处理函数,sys_open
通常API函数库(如glibc)中的函数会调用封装例程,封装例程负责发起系统调用(通过发软中断或系统调用指令),这些都运行在用户态。内核开始接收系统调用后,cpu从用户态切换到内核态(cpu处于什么状态,程序就叫处于什么状态,所以很多地方也说程序从用户态切换到内核态,实际是cpu运行级别的切换,通常cpu 运行在3级表示用户态,cpu 运行在0级表示内核态),内核调用相关的内核函数来处理再逐步返回给封装例程,cpu进行一次内核态到用户态的切换,API函数从封装例程拿到结果,再处理完后返回给用户。但是PI函数不一定需要进行系统调用,如某些数学函数,没有必要进行系统调用,直接glibc里面就给处理了,整个过程运行在用户态。所以作为我们编写linux用户程序的时候,是不能直接调用内核里面的函数的,内核里面的函数位于进程虚拟地址空间里面的内核空间,用户空间函数及函数库都处于进程虚拟地址空间里面的用户空间,用户空间调用内核空间的函数只有一个通道,这个通道就是系统调用指令,所以通常要调用glibc等库的接口函数,glibc也是用户空间的,但glibc自己实现了调用特殊的宏汇编系统调用指令进行cpu运行状态的切换,把进程从用户空间切换到内核空间。例子:fopen 调用 open,调用 一条指令这里的指令就是 通过发软中断或者系统调用指令实现的. 例如int 80 intrupt_num argv1 argv2 ...例如这一句过后,进入cpu从用户态切换到内核态然后进入一个 system_call 函数system_call 调用与中断号相关的中断处理函数,中断函数返回system_call函数返回然后直接进入用户态?然后open返回然后fopen返回xyz()函数执行的过程中会执行SYSCALL汇编指令,此指令将会把cpu从用户态切换到内核态。-----------------------------------------------------------------------------------内核SYACALL汇编指令中会包含将要调用的内核函数的系统调用号和参数,内核在上图系统调用处理程序中去查一个sys_call_talbe数组来找到这个系统调用号对应的服务例程(如sys_xyz())函数的地址,然后调用这个地址的函数执行。(这里glibc里面的系统调用号和内核里面的系统调用号必须完全相等,当然,这是约定好的) 系统调用是一个软中断,中断号是0x80,它是上层应用程序与Linux系统内核进行交互通信的唯一接口。 所以,系统调用过程是这样的:应用程序调用libc中的函数->libc中的函数引用系统调用宏->系统调用宏中使用int 0x80完成系统调用并返回。
//所以我们看到了,open 函数只是存储了一些参数到相应的寄存器,然后调用了 int 80,所以如果我们自己实现这个过程,也可以调用底层的中断处理函数,也可以实现一次系统调用.//所以我们可以说open层其实是封装了中断 的系统调用API//真正的系统调用的实现在内核中.//不过我们用的时候,也可以用在系统调用API上 再一次封装的 API//例如 fopen 封装了 openvoid main(){ char *str = "hello world\n"; asm( "movl $13,%%edx\n\t" "movl %0,%%ecx\n\t" "movl $0,%%ebx\n\t" "movl $4,%%eax\n\t" "int $0x80\n\t" : :"m"(str) :"edx","ecx","ebx" ); asm( "movl $42,%ebx\n\t" "movl $1,%eax\n\t" "int $0x80\n\t" );}
#include#include #include #include int main(){ int rc; rc = syscall(SYS_chmod, "/etc/passwd", 0444); if (rc == -1) fprintf(stderr, "chmod failed, errno = %d\n", errno); else printf("chmod succeess!\n"); return 0;}
open("./test",0775);
//这是linux0.11中的open,已经在内核中int open(const char * filename, int flag, ...){ register int res; va_list arg; va_start(arg,flag); __asm__( "int $0x80" :"=a" (res) :"0" (__NR_open),"b" (filename),"c" (flag),"d" (va_arg(arg,int)) ); if (res>=0) return res; errno = -res; return -1;}
main(init目录) sched_init set_system_gate(0x80,&system_call);然后int 80 的时候,中断处理函数在 system_call (kernel/system_call.s)_system_call: cmpl $nr_system_calls-1,%eax ja bad_sys_call push %ds push %es push %fs pushl %edx pushl %ecx # push %ebx,%ecx,%edx as parameters pushl %ebx # to the system call movl $0x10,%edx # set up ds,es to kernel space mov %dx,%ds mov %dx,%es movl $0x17,%edx # fs points to local data space mov %dx,%fs call _sys_call_table(,%eax,4) pushl %eax movl _current,%eax cmpl $0,state(%eax) # state jne reschedule cmpl $0,counter(%eax) # counter je rescheduleret_from_sys_call: movl _current,%eax # task[0] cannot have signals cmpl _task,%eax je 3f cmpw $0x0f,CS(%esp) # was old code segment supervisor ? jne 3f cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? jne 3f movl signal(%eax),%ebx movl blocked(%eax),%ecx notl %ecx andl %ebx,%ecx bsfl %ecx,%ecx je 3f btrl %ecx,%ebx movl %ebx,signal(%eax) incl %ecx pushl %ecx call _do_signal popl %eax3: popl %eax popl %ebx popl %ecx popl %edx pop %fs pop %es pop %ds iret
1/mkdir
2/INLINE_INSTALL
3/INTERNAL_SYSCALL
4/INTERNAL_SYSCALL_MAIN_x
5/INTERNAL_SYSCALL_MAIN_INLINE
6/int $0x80
1/mkdir
2/INLINE_SYSCALL
3/INTERNAL_SYSCALL
4/INTERNAL_SYSCALL_RAW
5/swi 0x0 @ syscall ” #name
//sysdeps/unix/sysv/linux/generic/mkdir.cint__mkdir (const char *path, mode_t mode){ return INLINE_SYSCALL (mkdirat, 3, AT_FDCWD, path, mode);}weak_alias (__mkdir, mkdir)//weak_alias(name1,name2) //为标号name1定义一个弱化名name2。//仅当name2没有在任何地方定义时,连接器就会用name1解析name2相关的符号。//在文件中定义的标号name1也会同样处理。
arm中的INLINE_SYSCALL//sysdeps/unix/sysv/linux/arm/sysdep.h#undef INLINE_SYSCALL#define INLINE_SYSCALL(name, nr, args...) \ ({ unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args); \ if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_sys_result, ), 0)) \ { \ __set_errno (INTERNAL_SYSCALL_ERRNO (_sys_result, )); \ _sys_result = (unsigned int) -1; \ } \ (int) _sys_result; })//sysdeps/unix/sysv/linux/arm/sysdep.h#undef INTERNAL_SYSCALL#define INTERNAL_SYSCALL(name, err, nr, args...) \ INTERNAL_SYSCALL_RAW(SYS_ify(name), err, nr, args)//sysdeps/unix/sysv/linux/arm/sysdep.h# undef INTERNAL_SYSCALL_RAW# define INTERNAL_SYSCALL_RAW(name, err, nr, args...) \ ({ \ register int _a1 asm ("r0"), _nr asm ("r7"); \ LOAD_ARGS_##nr (args) \ _nr = name; \ asm volatile (\ "swi 0x0 @ syscall " #name \ : "=r" (_a1) \ : "r" (_nr) ASM_ARGS_##nr \ : "memory"\ ); \ _a1; \ })
i386中的INLINE_SYSCALL//sysdeps/unix/sysv/linux/i386/sysdep.h# define INLINE_SYSCALL(name, nr, args...) \ ({ \ unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args); \ __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar, )) \ ? __syscall_error (-INTERNAL_SYSCALL_ERRNO (resultvar, )) \ : (int) resultvar; })//sysdeps/unix/sysv/linux/i386/sysdep.h#define INTERNAL_SYSCALL(name, err, nr, args...) \ ({ \ register unsigned int resultvar; \ INTERNAL_SYSCALL_MAIN_##nr (name, err, args); \ (int) resultvar; })//sysdeps/unix/sysv/linux/i386/sysdep.h#define INTERNAL_SYSCALL_MAIN_0(name, err, args...) \ INTERNAL_SYSCALL_MAIN_INLINE(name, err, 0, args)#define INTERNAL_SYSCALL_MAIN_1(name, err, args...) \ INTERNAL_SYSCALL_MAIN_INLINE(name, err, 1, args)#define INTERNAL_SYSCALL_MAIN_2(name, err, args...) \ INTERNAL_SYSCALL_MAIN_INLINE(name, err, 2, args)#define INTERNAL_SYSCALL_MAIN_3(name, err, args...) \ INTERNAL_SYSCALL_MAIN_INLINE(name, err, 3, args)#define INTERNAL_SYSCALL_MAIN_4(name, err, args...) \ INTERNAL_SYSCALL_MAIN_INLINE(name, err, 4, args)#define INTERNAL_SYSCALL_MAIN_5(name, err, args...) \ INTERNAL_SYSCALL_MAIN_INLINE(name, err, 5, args)//sysdeps/unix/sysv/linux/i386/sysdep.h# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \ LOADREGS_##nr(args) \ asm volatile (\ \ "int $0x80" \ : "=a" (resultvar) \ : "a" (__NR_##name) ASMARGS_##nr(args) \ : "memory", "cc"\ )
//start_kernel//init/main.c trap_init #ifdef CONFIG_X86_32 set_system_trap_gate(SYSCALL_VECTOR, &system_call); set_bit(SYSCALL_VECTOR, used_vectors); #endif//arch/x86/include/asm/irq_vectors.h#ifdef CONFIG_X86_32# define SYSCALL_VECTOR 0x80#endif
$ grep system_call * -nr | grep x86arch/x86/kernel/traps.c:70:asmlinkage int system_call(void);arch/x86/kernel/traps.c:768: set_system_trap_gate(SYSCALL_VECTOR, &system_call);arch/x86/kernel/entry_64.S:608:ENTRY(system_call)arch/x86/kernel/entry_64.S:620:GLOBAL(system_call_after_swapgs)arch/x86/kernel/entry_64.S:635:system_call_fastpath:arch/x86/kernel/entry_64.S:720: jmp system_call_fastpatharch/x86/kernel/entry_64.S:829:END(system_call)arch/x86/kernel/cpu/common.c:1122: wrmsrl(MSR_LSTAR, system_call);arch/x86/kernel/entry_32.S:506:ENTRY(system_call)arch/x86/kernel/entry_32.S:604:ENDPROC(system_call)arch/x86/xen/xen-asm_64.S:131: jmp system_call_after_swapgsarch/x86/include/asm/proto.h:8:void system_call(void);//arch/x86/kernel/entry_32.S/* * syscall stub including irq exit should be protected against kprobes */ .pushsection .kprobes.text, "ax" # system call handler stubENTRY(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_badsyssyscall_call: call *sys_call_table(,%eax,4) movl %eax,PT_EAX(%esp) # store the return valuesyscall_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_workrestore_all: TRACE_IRQS_IRETrestore_all_notrace: movl PT_EFLAGS(%esp), %eax # mix EFLAGS, SS and CS # Warning: PT_OLDSS(%esp) contains the wrong/random values if we # are returning to the kernel. # See comments in process.c:copy_thread() for details. movb PT_OLDSS(%esp), %ah movb PT_CS(%esp), %al andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax CFI_REMEMBER_STATE je ldt_ss # returning to user-space with LDT SSrestore_nocheck: RESTORE_REGS 4 # skip orig_eax/error_codeirq_return: INTERRUPT_RETURN//arch/x86/kernel/syscall_32.cconst 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};
//在内核中是没有open的,只有sys_open//下面这一句在open.c中SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)根据#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) 转换成SYSCALL_DEFINEx(3, _open, const char __user *, filename, int, flags, umode_t, mode)根据#define SYSCALL_DEFINEx(x, name, ...) \ asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \ static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \ asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \ { \ __SC_TEST##x(__VA_ARGS__); \ return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__)); \ } \ SYSCALL_ALIAS(sys##name, SyS##name); \ static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)) 转换成asmlinkage long sys_open(__SC_DECL3(const char __user *, filename, int, flags, umode_t, mode)); \ static inline long SYSC_open(__SC_DECL3(const char __user *, filename, int, flags, umode_t, mode)); \ asmlinkage long SyS_open(__SC_LONG3(const char __user *, filename, int, flags, umode_t, mode)) \ { \ __SC_TEST3(const char __user *, filename, int, flags, umode_t, mode); \ return (long) SYSC_open(__SC_CAST3(const char __user *, filename, int, flags, umode_t, mode)); \ } \ SYSCALL_ALIAS(sys_open, SyS_open); \ static inline long SYSC_open(__SC_DECL3(const char __user *, filename, int, flags, umode_t, mode))
转载地址:http://qtigi.baihongyu.com/