聊聊Linux内核创建新进程

本文介绍了Linux操作系统中的进程概念及其五种状态,详细解析了通过fork系统调用来创建新进程的过程,包括软中断、堆栈切换、相关寄存器保存及执行地址跳转等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        程  序原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

       计算机是一种精密的仪器,有一点错误都是不能成功执行的,计算机软件是靠一个个程序组成的,而程序又是一系列指令所组成。通过执行这样的一条条指令,计算机就能完成一个个任务了。

       这里有两个概念比较容易混淆,平时我们在学习计算机的时候也是这样,那就是进程和程序的区别,这也是这篇博客的主要内容,进程。简单点讲,进程就是程序的动态执行,是一个运行起来的程序。

       Linux其中一个重要的功能就是管理各种状态的进程,包括创建进程、管理进程、调度进程等一系列操作,正是由于有这些操作,才发挥了系统强大的能力。首先我们一起来熟悉一下进程。一般来说,进程都要经历3个状态,分别是就绪态、阻塞态、运行态。但是Linux操作系统将进程的状态进行了进一步划分,分为运行态、不可中断阻塞状态、可中断阻塞状态、挂起状态、僵尸状态5个状态。它们的具体含义是:

     运行状态:当进程正在被CPU执行,或处于就绪状态,则称进程处于运行状态。

     可中断睡眠状态:进程处于阻塞状态,系统不会调度其运行。

     暂停状态:进程收到有关信号就会进入暂停状态,可用SIGCONT进行唤醒

     僵死状态:进程停止运行

     不可中断睡眠状态:使用wake_up()函数明确唤醒时才能转换到可运行的就绪状态。

 

                               图1.进程的5种运行状态


                                                   图2.进程之间的转换

       有了这些基础知识,然后我们就可以开始进行创建新进程的分析了。首先,我们接触到一个系统调用函数fork,调用这个函数的时候,系统将会从用户态到内核态的切换。那么在程序中调用系统调用fork需要经过哪些步骤呢?目前主要有这么几个步骤:

     1.     调用fork这个系统调用,首先是软中断,堆栈切换到内核堆栈,保存相应的寄存器,进行执行地址的跳转。

     2.     查询相关的中断向量表,找到中断向量表中的函数名称,这里fork对应的函数名称是sys_clone函数。

     3.     执行sys_clone,这个函数不是直接执行,而是都会执行一个do_fork函数,产生一个新的的进程。

下面是do_fork()的解释

do_fork()
{
    copy_process(....){  //复制进程相关信息
         dup_tusk_struct(current){  //复制PCB
         alloc_task_struct_node()  //相关PCB指针赋值
         arch_dup_task_struct()
         alloc_thread_info_node()  //创建8KB大小的内存空间放置stack以及thread_info,返回stack起始地址ti
         setup_thread_info()  //初始化thread_info
                                    }
      
    retval=copy_thread(P)   //设置新进程的起始地址,新进程的内核堆栈           
                               }
}

下面是copy_thread()的代码解释

copy_thread()              
           p->thread.ip=ret_from_fork         //入口地址
           p->thread.sp=childregs             //将新进程的内核堆栈指向
           childregs=current_pt_regs          //拷贝父进程内核堆栈数据
            childregs->sp=sp;                   //将拷贝的sp设置为传入的sp

此时子进程的内核堆栈如下:

          stack(ti),childregs           :  current_pt_regs   复制
                      ax=0;                   修改ax
                      sp=sp;                  修改sp

       最后再进行一下跳转,并将所有子进程压入栈中的数据进行弹出,这样子进程的所有数据都有了,就可以自己运行了。

       说了这么多理论,下面来开始我们的实验,进行一下验证。


                                        图3.首先在test.c文件中写入Fork函数


                                           图4.导入相应的执行命令


                                                 图5.编译运行


                                 图6.编译运行完的结果,证明新进程已经建立


                                          图7.调试模式启动


                                  图8.在相应的执行行上打印上断点,主要针对fork功能打断点


                                                     图9.调试过程中

       总结:fork创建子进程也是一种特殊的系统调用,在执行这个系统调用之前,要进行保护现场操作,也就是要对有关寄存器的值进行存储,当新进程创建完成以后可以回到此处继续执行。在保护好现场以后,就是查找sys_call_table里面的有关信息,这样就能根据调用号来找到相应的入口地址。再通过函数入口地址来进行处理,处理过程也是拷贝父进程的有关信息,然后加上自己独特的信息,这就构成新的进程,这也是fork的整个工作流程。




                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值