20169203《Linux内核原理与分析》第七周作业

对于本周的实验是使用gdb跟踪分析一个系统调用中断处理过程,分析系统调用从system_call开始到iret结束之间的整个过程。
首先进入实验楼虚拟机打开终端进入LinuxKernel中将原有的menu删除代码如下

rm -rf menu

再拷贝新的menu

git clone https://github.com/mengning/menu.git

进入到test.c中添加上周写的系统调用


添加后编译运行menu

make rootfs

我们以调试的方式启动menuos

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

再打开另一个中断,输入符号表,连接menuos

file linux-3.18.6/vmlinux
target remote:1234

在系统调用出设置断点

b sys_getuid18

即可在断点处列出其代码

通过本次的实验我了解的系统调用的过程那程序运行到getuid()时程序需要进行系统调用那么会通过init 0x80陷入到内核态并将其对应的系统调用号传送给eax寄存器中,并且在系统调用的过程中还会有可能放生进程调度进入到另一个函数中,在system_call的最后是iret汇编语言指令,从系统调用退出,从内核态切换回用户态。

# system call handler stub
ENTRY(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)   // 获取thread_info结构的信息
                    # system call tracing in operation / emulation
    testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) // 测试是否有系统跟踪
    jnz syscall_trace_entry   // 如果有系统跟踪,先执行,然后再回来
    cmpl $(NR_syscalls), %eax // 比较eax中的系统调用号和最大syscall,超过则无效
    jae syscall_badsys  // 无效的系统调用 直接返回
syscall_call:
    call *sys_call_table(,%eax,4) // 调用实际的系统调用程序
syscall_after_call:
    movl %eax,PT_EAX(%esp)      // 将系统调用的返回值eax存储在栈中
syscall_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  //检测是否所有工作已完成
    jne syscall_exit_work           //工作已经完成,则去进行系统调用推出工作

restore_all:
    TRACE_IRQS_IRET         // iret 从系统调用返回
syscall_exit_work:
    testl $_TIF_WORK_SYSCALL_EXIT, %ecx //测试syscall的工作完成
    jz work_pending
    TRACE_IRQS_ON  //切换中断请求响应追踪可用
    ENABLE_INTERRUPTS(CLBR_ANY) # could let syscall_trace_leave() call
                    //schedule() instead
    movl %esp, %eax
    call syscall_trace_leave //停止追踪系统调用
    jmp resume_userspace //返回用户空间,只需要检查need_resched
END(syscall_exit_work)
work_pending:
    testb $_TIF_NEED_RESCHED, %cl  // 判断是否需要调度
    jz work_notifysig   // 不需要则跳转到work_notifysig
work_resched:
    call schedule   // 调度进程
    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
    andl $_TIF_WORK_MASK, %ecx  // 是否所有工作都已经做完
    jz restore_all              // 是则退出
    testb $_TIF_NEED_RESCHED, %cl // 测试是否需要调度
    jnz work_resched            // 重新执行调度代码

work_notifysig:             // 处理未决信号集
#ifdef CONFIG_VM86
    testl $X86_EFLAGS_VM, PT_EFLAGS(%esp) // 判断是否在虚拟8086模式下
    movl %esp, %eax
    jne work_notifysig_v86      // 返回到内核空间
1:
#else
    movl %esp, %eax
#endif
    TRACE_IRQS_ON  // 启动跟踪中断请求响应
    ENABLE_INTERRUPTS(CLBR_NONE)
    movb PT_CS(%esp), %bl
    andb $SEGMENT_RPL_MASK, %bl
    cmpb $USER_RPL, %bl
    jb resume_kernel        // 恢复内核空间
    xorl %edx, %edx
    call do_notify_resume  // 将信号投递到进程
    jmp resume_userspace  // 恢复用户空间

#ifdef CONFIG_VM86
    ALIGN
work_notifysig_v86:
    pushl_cfi %ecx          # save ti_flags for do_notify_resume
    call save_v86_state     // 保存VM86模式下的CPU信息
    popl_cfi %ecx
    movl %eax, %esp
    jmp 1b
#endif
END(work_pending)

对于教材内容的学习本周主要学习的是Linux对于内核同步与并发的方法,多线程相当于一个并发系统。并发系统一般同时执行多个任务。如果多个任务可以共享资源,特别是同时写入某个变量的时候,就需要解决同步的问题。比如说,我们有一个多线程火车售票系统,用全局变量i存储剩余的票数。多个线程不断地卖票(i = i - 1),直到剩余票数为0。如果只有一个线程执行上面的程序的时候(相当于一个窗口售票),则没有问题。但如果多个线程都执行上面的程序(相当于多个窗口售票), 我们就会出现问题。其根本原因在于同时发生的各个线程都可以对i读取和写入。这就需要引入同步对于多线程程序来说,同步是指在一定的时间内只允许某一个线程访问某个资源 。而在此时间内,不允许其它的线程访问该资源。我们可以运用自旋锁,信号量,互斥体等来对内核进行同步.

posted @ 2016-11-06 15:53  李学生  阅读(123)  评论(0编辑  收藏  举报