279|0

15

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

《Linux内核深度解析》-系统调用学习 [复制链接]

一、系统调用

        系统调用是内核给用户程序提供的编程接口。用户程序可使用glibc库对单个系统提供的函数,或使用syscall ( ):。系统调用fork()为例:

  • SYSCALL_DEFINE0(fork) //展开后为 asmlinkage long sys_fork(void)
  • {
  • #ifdef CONFIG_MMU
  • return _do_fork(SIGCHLD,0,0,NULL,NULL,0)
  • #else
  • Return -EINVAL;
  • #endif
  • }

            需要在系统调用表中保存系统调用号和处理函数的映射关系,sys_call_table如下:

  • /source/arch/x86/kernel/syscall_64.c
  • asmlinkage 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_64.h>
  • };

二、执行系统调用

系统调用划分到同步异常,在异常级别1的异常向量表中,64位调用入口为el0_sync函数。

  • el0_sync:
  • kernel_entry 0
  • mrs x25, esr_el1 // 读异常情况寄存器
  • lsr x24, x25, #ESR_ELx_EC_SHIFT // 判断异常类别
  • cmp x24, #ESR_ELx_EC_SVC64 // 64-bit 系统调用
  • b.eq el0_svc //跳转到el0_svc
  • ...

el0_svc负责执行系统调用,如果上层调用open系统调用打开文件时,就会从从sys_call_table,根据系统调用号,找到对应的sys_call_table元素,也即sys_open;并执行。代码如下:

  • sc_nr .reg x25 //系统调用数量
  • scno .req x26 //系统调用号
  • stbl .req x27 //系统调用表地址
  • Tsk .req x28 //当前进程的thread_info结构体的地址
  • el0_svc:
  • adrp stbl, sys_call_table // load syscall table pointer
  • uxtw scno, w8 // syscall number in w8
  • mov sc_nr, #__NR_syscalls //把寄存器x25设置为系统调用数量
  • el0_svc_naked: // compat entry point
  • stp x0, scno, [sp, #S_ORIG_X0] // save the original x0 and syscall number
  • enable_dbg //开启调试
  • enable_irq //开启中断
  • get_thread_info tsk
  • ldr x16, [tsk, #TI_FLAGS] // check for syscall tracing
  • tbnz x16, #TIF_SYSCALL_TRACE, __sys_trace // are we tracing syscalls?
  • adr lr, ret_fast_syscall // return address,用于返回用户空间
  • cmp scno, sc_nr // check upper syscall limit
  • b.hs ni_sys //如果超过系统调用限制,跳转到ni_sys 处理
  • ldr x16, [stbl, scno, lsl #3] // address in the syscall table
  • br x16 // call sys_* routine
  • ni_sys: //超过调用数量限制的处理
  • mov x0, sp
  • b do_ni_syscall
  • ENDPROC(el0_svc)

ret_fast_syscall从系统调用返回用户空间,代码如下

  • /*
  • * This is the fast syscall return path. We do as little as possible here,
  • * and this includes saving x0 back into the kernel stack.
  • */
  • ret_fast_syscall:
  • disable_irq // disable interrupts
  • str x0, [sp, #S_X0] // returned x0
  • ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for syscall tracing
  • and x2, x1, #_TIF_SYSCALL_WORK
  • cbnz x2, ret_fast_syscall_trace
  • and x2, x1, #_TIF_WORK_MASK
  • cbnz x2, work_pending
  • enable_step_tsk x1, x2
  • kernel_exit 0
  • ret_fast_syscall_trace:
  • enable_irq // enable interrupts
  • b __sys_trace_return_skipped // we already saved x0
  • /*
  • * Ok, we need to do extra processing, enter the slow path.
  • */
  • work_pending:
  • mov x0, sp // 'regs'
  • bl do_notify_resume
  • #ifdef CONFIG_TRACE_IRQFLAGS
  • bl trace_hardirqs_on // enabled while in userspace
  • #endif
  • ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for single-step
  • b finish_ret_to_user
  • /*
  • * "slow" syscall return path.
  • */
  • ret_to_user:
  • disable_irq // disable interrupts
  • ldr x1, [tsk, #TSK_TI_FLAGS]
  • and x2, x1, #_TIF_WORK_MASK
  • cbnz x2, work_pending
  • finish_ret_to_user:
  • enable_step_tsk x1, x2
  • kernel_exit 0
  • ENDPROC(ret_to_user)

work_pending调用do_notify_resume函数,代码如下:

  • asmlinkage void do_notify_resume(struct pt_regs *regs,
  • unsigned int thread_flags)
  • {
  • /*
  • * The assembly code enters us with IRQs off, but it hasn't
  • * informed the tracing code of that for efficiency reasons.
  • * Update the trace code with the current status.
  • */
  • trace_hardirqs_off();
  • do {
  • if (thread_flags & _TIF_NEED_RESCHED) {
  • schedule();
  • } else {
  • local_irq_enable();
  • if (thread_flags & _TIF_UPROBE)
  • uprobe_notify_resume(regs);
  • if (thread_flags & _TIF_SIGPENDING)
  • do_signal(regs);
  • if (thread_flags & _TIF_NOTIFY_RESUME) {
  • clear_thread_flag(TIF_NOTIFY_RESUME);
  • tracehook_notify_resume(regs);
  • }
  • if (thread_flags & _TIF_FOREIGN_FPSTATE)
  • fpsimd_restore_current_state();
  • }
  • local_irq_disable();
  • thread_flags = READ_ONCE(current_thread_info()->flags);
  • } while (thread_flags & _TIF_WORK_MASK);
  • }

 

查看本帖全部内容,请登录或者注册
此帖出自Linux开发论坛
点赞 关注(1)

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
Microchip 直播|利用motorBench开发套件高效开发电机磁场定向控制方案 报名中!
直播主题:利用motorBench开发套件高效开发电机磁场定向控制方案
直播时间:2025年3月25日(星期二)上午10:30-11:30
快来报名!

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表