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


    实验

    给MenuOS增加time和time-asm命令的方法:

    1. 更新menu代码到最新版
    2. 再main()函数中增加MenuConfig
    3. 增加对应的Time函数和TimeAsm函数(这里的函数要换成我们自己编写的使用系统调用的函数,比如mkdir和mkdirAsm)
    4. make rootfs (帮我们自动编译自动生成根文件系统,自动帮我们启动起来menuos)

    接下来我要使用gdb跟踪分析一个系统调用内核函数(mkdir)

    这次我实验所用的系统调用仍然是是mkdir

    首先,我们需要把上周做的两个实验加入到我们的MenuOS中,变成MenuOS中的两个命令。如图:

    方法是要将函数加入到test.c中(因为实验楼中的clone无法使用),加入命令为:

    int Mkdir()
    {
            int flag;
            flag = mkdir("/home/shiyanlou/testdir");
            if (flag == -1)
                    printf("mkdir failed!
    ");
            else
                    printf("make dirctory success!
    ");
            return 0;
    }
    
    int MkdirAsm()
    {
       int flag;
       char *dir = "/home/shiyanlou/testdir2";
       asm volatile(
           "movl $0x27,%%eax
    	"
           "movl %1,%%ebx
    	"
           "int $0x80
    	"
           "movl %%eax,%0
    	"
           :"=m"(flag)
           :"c"(dir)
           );
       if(flag==0)
          printf("mkdir success!
    ");
       else
          printf("mkdir failed!
    ");
       return 0;
    }
    

    在main函数中加入:

        MenuConfig("mkdir","Make A Directory",Mkdir);
        MenuConfig("mkdir-asm","Make A Directory(asm)",MkdirAsm);
    
    

    make rootfs就可以自动生成,输入help可以看到mkdir,如下结果:

    下面开始用gdb调试:

    cd LinuxKernel
    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
    水平分割
    gdb
    file linux-3.18.6/vmlinux   //加载内核
    target remote:1234          //链接到menu os里
    b start_kernel              //在start_kernel处设置断点
    c                           //继续执行,到start_kernel 停下来
    

    list                        //查看startkernel这段代码
    b sys_mkdir                 //在我们要分析的这个系统调用处设置断点
    c                           //继续执行
    c
    c
    

    启动menuos,输入mkdir,看gdb调试结果

    如果我们换用视频里的sys_time设置断点进行调试,之前的方法与mkdir类似,然后可以继续跟踪调试如下:

    b sys_time
    c
    c
    c
    list
    s                           //单步执行
    s                           //单步执行
    finish                      //函数执行完
    s
    s
    n
    n
    

    这边代码就不大好调试了,如果我们再设置一个断点,当我们用int 0x80时,cpu自动跳转到system_call这个函数,下面我们直接设置断点system_call,是否能停下来呢?

    b system_call
    c
    

    发现time返回了。在menuos中输入time—asm,停在system_time的位置,system_call并不能停下。老师说这里的system_call他不是一个正常的函数,它是一段特殊的汇编代码,可能gdb对他不支持,我们只能调试系统调用函数和对应的内核函数,调试内核函数的处理过程,但不能跟踪entry_32.s这个汇编代码,在system_call处并没有停下来。system_call在entry_32.s中,可以找到ENTRY(system_call),gdb可以发现一个函数原型,实际上不是函数,是汇编代码的起点,gdb现在还不能跟踪它。

    关于从system_call到iret之间的过程

    画了一个大致的流程图,如下:

    首先保护现场(SAVE_ALL:保存需要用到的寄存器数据);退出恢复现场(RESTORE_ALL:退出中断程序,恢复保存寄存器的数据)。
    下面为SAVE_ALL的实现:

    .macro SAVE_ALL
        cld
        PUSH_GS
        pushl_cfi %fs
        /*CFI_REL_OFFSET fs, 0;*/
        pushl_cfi %es
        /*CFI_REL_OFFSET es, 0;*/
        pushl_cfi %ds
        /*CFI_REL_OFFSET ds, 0;*/
        pushl_cfi %eax
        CFI_REL_OFFSET eax, 0
        pushl_cfi %ebp
        CFI_REL_OFFSET ebp, 0
        pushl_cfi %edi
        CFI_REL_OFFSET edi, 0
        pushl_cfi %esi
        CFI_REL_OFFSET esi, 0
        pushl_cfi %edx
        CFI_REL_OFFSET edx, 0
        pushl_cfi %ecx
        CFI_REL_OFFSET ecx, 0
        pushl_cfi %ebx
        CFI_REL_OFFSET ebx, 0
        movl $(__USER_DS), %edx
        movl %edx, %ds
        movl %edx, %es
        movl $(__KERNEL_PERCPU), %edx
        movl %edx, %fs
        SET_KERNEL_GS %edx
    .endm
    
    

    在这段代码中,保存了相关寄存器的值。 他们依次是:ES,DS,EAX,EBP,EDI,ESI,EDX,ECX,EBX等等。从这里寄存器的顺序可以知道压栈的最后压入的是ebx,这里压入的栈是内核栈。

    遇到的问题

    1. 在使用实验楼clone的时候出现问题,后将test.c文件更改进行解决
    2. 实验楼一直很卡,试验了好多次最后在MenuOs上输入的mkdir命令上还是卡住了

    书上的内容整理

    1. 内核同步介绍:留意保护共享资源,防止共享资源并发访问,发生各个线程之间相互覆盖共享数据的情况,造成访问数据不一致;临界区和竞争条件,临界区:访问和操作共享数据的代码段。 竞争条件:两个执行线程处于同一个临界区中;内核各个部分都会调用两个函数,一个函数将新请求添加到队列尾部,另一个函数从队列头删除请求,然后处理它。
    2. 锁的使用是自愿的、非强制的,它完全属于一种编程者自选的编程手段;内核中造成并发的原因:(1)中断:任何时刻异步发生,打断当前执行的代码(2)软中断和tasklet:任何时刻唤醒或调度软中断、tasklet(3)内核抢占(preempt)(4)睡眠及与用户空间的同步:唤醒调度程序,调度新进程执行(5)对称多处理器(SMP);编程需注意的问题: (1)数据是否全局?除了当前线程,其他线程是否可以访问? (2)数据是否在进程/中断上下文中共享?是否在两个不同中断中共享?(3)进程在访问数据时可否被抢占?被调度的新进程是否会访问同一数据? (4)当前进程是否会睡眠(阻塞)在某些资源上?共享数据处于何种状态? (5)怎样防止数据失控?;加锁:按顺序加锁。使用嵌套锁必须以相同顺序获取锁;防止发生饥饿; 不要重复请求同一个锁; 设计力求简单;建议以获取锁相反的顺序来释放锁。
    3. 原子操作:原子操作执行过程不被打断,原子操作接口分为整数操作接口和单独位操作接口。
    4. 自旋锁:自旋锁最多只能被一个可执行线程持有。若一个线程试图获得一个被征用的自旋锁,线程会一直忙循环、选择、等待锁可用。缺点:由于自旋锁在等待时自旋(浪费处理器时间),因此自旋锁不应长时间持有。优点:线程不用睡眠,不用进行上下文切换。自旋锁可以使用在中断处理程序中。对于软中断,无论是否同种类型,如果数据被软中断共享,那么它必须得到锁的保护。读写自旋锁:读写不同锁,多人和可并发持有读者锁,写锁只能被一个任务持有。
    5. 信号量特点: Linux中的信号量是一种睡眠锁; 信号量适用于锁会被长期占有的情况;
      由于信号量会睡眠,所以只能用于进程上下文中,中断上下文不支持调度;可以在持有信号量时睡眠; 不可以在持有信号量时使用自旋锁; 信号量允许任意数目的锁持有者,自旋锁一个时刻只允许一个任务持有;在声明时课指定信号量拥有的持有者数量。
    6. 禁止抢占:内核代码使用自旋锁作为非抢占区域的标记。preempt_disable() 增加抢占计数值,从而禁止内核抢占;preempt_enable() 减少抢占计数值,当减为0时检查和执行被挂起的任务; preempt_count() 返回抢占计数。
  • 相关阅读:
    Seial port API and tool
    Simple HTTPD
    VC与Cygwin的结合
    zlib
    嵌入式开发系统编程文件格式解析
    ZB4O
    Wireshark基本介绍和学习TCP三次握手
    freeware
    Console2 A Better Windows Command Prompt
    iniparser
  • 原文地址:https://www.cnblogs.com/Jarvan210/p/6036725.html
Copyright © 2020-2023  润新知