• 2018-2019-1 20189221 《Linux内核原理与分析》第六周作业


    2018-2019-1 20189221 《Linux内核原理与分析》第六周作业

    实验五

    实验过程

    将Fork函数移植到Linux的MenuOS

    fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程。在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。通过fork返回的值来判断当前进程是子进程还是父进程。

    启动MenuOS:

    在test.c中添加代码如下:

    
    int Fork(int argc, char *argv[])
    {
    
        pid_t fpid;
        int count = 0;
        fpid = fork();
        if (fpid < 0)
            printf("error in fork!");
        else if (fpid == 0) {
            printf("i am the child process, my process id is %d
    ",getpid());
            count++;
        }
        else {
            printf("i am the parent process, my process id is %d
    ",getpid());
            count++;
        }
        printf("count: %d
    ",count);
        return 0;
    }
    
    int main()
    {
        PrintMenuOS();
        SetPrompt("MenuOS>>");
        MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL);
        MenuConfig("quit","Quit from MenuOS",Quit);
        MenuConfig("fork","fork a child process",Fork);
    
        ExecuteMenu();
    }
    
    

    运行结果:

    sys_call过程分析

    gdb调试:

    由于实验楼多次卡顿,十分浪费时间

    调试代码:

    $ gdb
    (gdb) file linux-3.18.6/vmlinux
    (gdb) target remote:1234
    (gdb) continue
    

    在sys_fork设置断点,在qemu中输入fork-asm命令,可以看到停在了sys_fork()函数中。

    然后s单步执行,finish返回do_fork函数,返回值$2=866,即分配了pid=866的子进程。继续单步,到了schedule()中,此时发生了进程调度。finish后schedule返回。

    再次单步执行,出现Cannot find bounds of current function。此时gdb已无法跟踪。

    gdb中输入c继续执行,看到qemu中输出fid = 866,即子进程的pid为866。

    system_call的代码:

    ENTRY(system_call)
         RING0_INT_FRAME    
         ASM_CLAC        
         pushl_cfi %eax            //保存系统调用号;
         SAVE_ALL                  //可以用到的所有CPU寄存器保存到栈中
         GET_THREAD_INFO(%ebp)     //ebp用于存放当前进程thread_info结构的地址
         testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
         jnz syscall_trace_entry
         cmpl $(nr_syscalls), %eax  //检查系统调用号(系统调用号应小于NR_syscalls),
         jae syscall_badsys         //不合法,跳入到异常处理
     syscall_call:
         call *sys_call_table(,%eax,4) //合法,对照系统调用号在系统调用表中寻找相应服务例程
         movl %eax,PT_EAX(%esp)        //保存返回值到栈中
     syscall_exit:  
         testl $_TIF_ALLWORK_MASK, %ecx   //检查是否需要处理信号
         jne syscall_exit_work        //需要,进入 syscall_exit_work
     restore_all: 
         TRACE_IRQS_IRET              //不需要,执行restore_all恢复,返回用户态
     irq_return:
         INTERRUPT_RETURN             //相当于iret
    

    system_call流程:

    实验总结

    系统调用是特殊的中断函数,是多种中断处理过程的集合

    系统调用的过程其实是另一种上下文切换的实现:

    首先SAVE ALL保存上下文

    根据IDT调用内核函数

    执行RESTORE_ALL并返回用户模式

    系统调用与中断的共同之处

    • 保存现场

      在系统调用时,用SAVE_ALL来保存系统调用时的上下文。
      中断处理的第一步也是要保存中断程序现场。
      中断处理完之后,可以返回到原来被中断的地方,在原有的运行环境下继续正确的执行下去。

    • 确定中断信息

      在系统调用中,需要将系统调用号通过eax传入,通过sys_call_table查询到调用的系统调用,然后跳转到相应的程序进行处理。
      中断处理时系统也需要有一个中断号,通过检索中断向量表,了解中断的类型和设备。

    • 处理中断

      跳转到相应的中断处理程序后,对中断进行处理。

    • 返回

      系统调用时最后要restore_all恢复系统调用时的现场,并用iret返回用户态。
      同样,执行完中断处理程序,内核也要执行特定指令序列,恢复中断时现场,并使得进程回到用户态。

    系统调用与一般函数的不同之处

    • 不是通过“CALL”指令而是通过“INT”指令发起调用;

    • 不是通过“RET”指令,而是通过“IRET”指令完成调用返回;

    • 当到达内核态后,操作系统需要严格检查系统调用传递的参数,确保不破坏整个系统的安全性;

    • 执行系统调用可导致进程等待某事件发生,从而可引起进程切换;

  • 相关阅读:
    java.lang.OutOfMemoryError: Java heap space解决办法
    android网络图片加载缓存,避免重复加载。
    Android控制软键盘拉起
    mysql create database 指定utf-8编码
    仿黑客帝国文字雨效果
    C++循环去掉文件的后缀名
    使用python读取多重文件夹下的word(doc、docx)文件,并处理存储到excel(xls、xlsx)文件
    Python操作文件夹
    (转载)Python:列表作为参数
    595. Big Countries (Easy)
  • 原文地址:https://www.cnblogs.com/gdman/p/9955183.html
Copyright © 2020-2023  润新知