Linux-0.11内核分析04:进程2的创建及执行

进程2的创建及执行

代码树

--- init --- main.c --- sched_init()
 |                   |- init() --- setup() --- int 0x80
 |                              |- open("/dev/tty0") --- int 0x80//根文件系统挂载完毕,打开tty设备
 |                              |- dup(0) --- int 0x80//建立标准输出文件
 |                              |- dup(0) --- int 0x80//标准错误文件
 |                              |- pid=fork() --- int 0x80  //创建进程2
 |                              |- if(!pid) {         //进程2进入
 |                              |      close(0);      //关闭tty0
 |                              |      open("/etc/rc",O_RDONLY,0);       //打开rc文件
 |                              |      execve("/bin/sh",argv_rc,envp_rc);//执行shell程序,读取rc文件
 |                              |      _exit(2);      //进程2退出
 |                              |  }
 |                              |- if (pid>0)
 |                              |      while (pid != wait(&i))//调度到进程2,等待其退出
 |                              |- while (1) {
 |                              |      pid=fork();    //创建进程4
 |                              |      if (!pid) {    //进程4进入
 |                              |          close(0);close(1);close(2);//关闭rc文件
 |                              |          setsid();
 |                              |          (void) open("/dev/tty0",O_RDWR,0);//打开tty0
 |                              |          (void) dup(0);
 |                              |          (void) dup(0);
 |                              |          _exit(execve("/bin/sh",argv,envp));//执行shell程序,读取tty0
 |                              |      }
 |                              |      while (1)
 |                              |          if (pid == wait(&i))//调度到进程4
 |                              |              break;
 |                              |      sync();          //同步硬盘数据
 |                              |  }
 |
 |- kernel --- sched.c --- sched_init() --- set_system_gate(0x80,&system_call)
 |          |
 |          |- system_call.s --- system_call() --- sys_call_table(,%eax,4)
 |          |                 |- sys_execve() --- eax指向堆栈中eip指针处
 |          |                                  |- do_execve()
 |          |
 |          |- exit.c --- sys_waitpid --- 找到current的子进程*p,也就是进程2
 |                     |               |- p处于僵死态,则返回(*p)->pid
 |                     |               |  否则继续运行  
 |                     |               |- 设置current->state为等待状态
 |                     |               |- schedule()//切换到进程2
 |                     |               |- 检测到SIGCHLD信号量,返回第1条执行
 |                     |- sys_exit() --- do_exit()
 |                     |- do_exit() --- 释放shell进程所占据的内存页面
 |                                   |- 将update的父进程设置为进程1
 |                                   |- 关闭当前进程打开着的所有文件
 |                                   |- 把当前进程置为僵死状态
 |                                   |- tell_father()//通知父进程
 |                                   |- schedule()//发现进程1收到信号,切换到进程1
 |
 |- include --- linux --- sys_call_table[] --- sys_open()
 |                                          |- sys_dup(0)
 |                                          |- sys_fork()//复制父进程
 |                                          |- sys_waitpid()
 |                                          |- sys_close()
 |                                          |- sys_read()
 |                                          |- sys_exit()
 |
 |- fs --- open.c --- sys_open(pathname) --- 找到current中空闲filp[fd]
 |      |          |                      |- 在file_table[64]中获取空闲项的地址f
 |      |          |                      |- filp[fd]=f
 |      |          |                      |- 文件引用计数加1
 |      |          |                      |- open_namei(pathname,&inode)
 |      |          |                      |- current->tty = MINOR(inode->i_zone[0])
 |      |          |                      |- f->f_inode = inode//设置file_table[0]
 |      |          |- sys_close() --- filp = current->filp[fd]
 |      |                          |- current->filp[fd] = NULL
 |      |                          |- filp->f_count减1  
 |      |
 |      |- namei.c --- open_namei() --- dir=get_dir(pathname,&namelen,&namebase)
 |      |           |                |  //获取dev目录inode,以及'tty0'的长度和首地址
 |      |           |                |- find_entry(&dir,namebase,namelen,&de)
 |      |           |                |  //根据上面的信息得,从dev目录得到tty0的目录项de
 |      |           |                |- inr=de->inode; dev=dir->i_dev
 |      |           |                |- 保存inr和dev在table_inode[32]中
 |      |           |                |- inode=iget(inr,dev)//得到tty0的i节点
 |      |           |- get_dir() --- inode = current->root
 |      |                         |- pathname指向'd'
 |      |                         |- thisname = pathname
 |      |                         |- 获取thisname的长度namelen
 |      |                         |- pathname指向下一个目录
 |      |                         |- pathname是最后一个目录则返回inode
 |      |                         |- find_entry(&inode,thisname,namelen,&de)
 |      |                         |  //根据上面的信息得到目录项de
 |      |                         |- inr=de->inode; dev=dir->i_dev
 |      |                         |- iput(inode)
 |      |                         |- inode=iget(idev,inr)
 |      |                         |- 返回3执行
 |      |
 |      |- fcntl.c --- sys_dup() --- dupfd(0,0)
 |      |           |- dupfd() --- 在进程1的filp[20]中寻找空闲项filp[arg]
 |      |                       |- filp[arg]指向目标文件
 |      |
 |      |- exec.c --- do_execve() --- inode=namei(filename)
 |      |                          |- bh = bread(inode->i_dev,inode->i_zone[0])
 |      |                          |- ex = *((struct exec *) bh->b_data)
 |      |                          |- eip[0] = ex.a_entry//eip指向shell程序
 |      |                             //由于该线性空间对应的程序内容未加载,因此会触发 page_fault
 |      |
 |      |- read_write.c --- sys_read() --- 如果是普通文件,读取完成后返回-ERROR
 |
 |- mm --- page.s --- page_fault() --- do_no_page()
        |- memery.c --- do_no_page() --- page = get_free_page()
                     |                |- bread_page(page,current->executable->i_dev,nr)
                     |                |- put_page(page)//建立页表映射关系,之后shell程序开始执行
                     |                   //中断退出后,shell程序执行
                     |- put_page() --- page_table = (address>>20) & 0xffc
                                    |- page_table[(address>>12) & 0x3ff] = page | 7   


--- bin --- sh --- 读取/etc/rc文件,并执行
 |
 |- etc --- rc --- /etc/update &//创建update程序,挂起后返回进程2
                |- echo "/dev/hd1 /" > /etc/mtab --- read() --- int 0x80  
                |- 返回错误则执行exit() --- int 0x80                

总结

打开tty0
1. 进程1在init()函数中,执行open(“/dev/tty0”,O_RDWR,0)打开tty设备,系统触发0x80中断,跳转到sys_open()函数中执行。
2. 在sys_open()函数中,首先在进程1的任务结构的filp[20]找到一个空闲的项filp[fd],然后在file[64]中找到一个空闲项,其地址存在f中,然后将filp[fd]=f。接着调用open_namei()函数读取tty设备文件的inode。
3. 在open_namei()函数中,调用dir_namei()函数获取枝梢节点,然后使用find_entry()函数在枝梢节点中找到名字匹配的目录项,最后根据目录项中的设备号和inode号,调用iget()函数读取文件的inode,读取完成后,返回inode号
3.1. 在dir_namei()函数中,调用get_dir()函数得到枝梢节点
3.2. 在get_dir()函数中,获取下一个目录的名称,然后调用find_entry()在目录文件中找到名称相同的目录项,然后根据目录项中的设备号和inode号,调用iget()函数读取文件的inode。
3.3. 循环3.2过程,直到读取到枝梢节点为止,然后返回inode号。
4. 回到sys_open()函数,将f指向的文件结构中绑定该tty0的inode号。函数返回。
5. 回到init()函数中,调用dup(0)两次将当前进程的filp[20]中再找到两个空闲项,给它们filp[0]的值。

进程1创建进程2
1. 继续执行init()函数,接着执行fork()函数创建进程2,然后wait()函数切换到进程2执行。
2. 在wait()系统调用最终执行sys_waitpid()函数,该函数中遍历所有任务结构,若没有任何进程处于僵死态或停止态,则将当前进程置为可中断等待状态,然后调用schedule()函数,切换到进程2。
3. 切换到进程2,仍然在init()函数中执行,首先执行close(0)关闭进程1打开的tty0,然后调用open(“/etc/rc”,O_RDONLY,0)打开rc文件,最后调用execve(“/bin/sh”,argv_rc,envp_rc)执行脚本
4. execve()函数触发0x80中断,最终调用do_execve()函数。该函数中首先调用namei()函数获取shell文件所在的文件节点,然后将shell文件头读取到缓冲区,接着将current->executable置为shell文件的inode,释放掉进程2已经建立好的页表,并将环境变量放入数据段当中,最后设置脚本文件的入口地址和栈指针(栈指针指向数据段末尾)到栈中,调用ret即可跳转到脚本的入口地址运行。
5. 由于shell文件并没有读入代码段,所以会触发缺页中断,最终跳转到do_no_page()函数执行。
6. do_no_page()函数中,首先新申请一页新的内存,然后根据current->executable读取shell文件的一页数据到新的内存页中,最后调用put_page()将这页内存写入到页表和页目录。返回执行shell程序。
7. shell程序从filp[0]中读取命令并执行,filp[0]现在是rc文件,文件中重要的一点是执行了/etc/update &,该命令创建了一个新的update进程,也就是进程3。update进程用于将缓冲区的数据和硬盘进行同步。

进程2退出,重建新的shell进程
1. shell程序执行完rc文件后退出,调用do_exit()函数。
2. do_exit()函数首先释放当前进程的代码段和数据段,然后检测当前进程是否有子进程,若有子进程,将子进程的父进程置为进程1,接着将当前进程设置为僵死态,然后调用tell_father()函数给父进程(进程1)发送信号,最后调用schedule()函数进行调度。
3. schedule()函数中,遍历所有进程,找到接收到信号的进程(进程1)并将其置为就绪态,然后查找所有就绪态的进程,找到最合适的那一个,切换到那个进程。
4. 进程1在sys_waitpid()函数中切换到进程2,现在返回进程1,仍然从sys_waitpid()函数继续执行。
5. 在sys_waitpid()函数中,发现当前进程收到了SIGCHLD信号,返回sys_waitpid()函数头重新执行一次。遍历所有任务结构,找到僵死态进程(进程2),对进程2进行释放,返回释放的进程号(2).
6. 回到init()函数继续执行,进程1创建调用fork()函数创建一个新进程(进程4),然后再次调用wait()系统调用,切换到进程4中。
7. 进程4首先关闭了filp[0],filp[1]和filp[2],然后重新打开/dev/tty0,并重新执行/bin/sh程序。这次,因为进程4打开的文件是tty0,而非rc,因此shell程序开始执行后不会退出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值