【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】

熟悉linux的同学都知道,linux的启动顺序就是uboot -> linux -> shell。但是很少人研究linux是怎么调用shell程序的。今天,可以借助早期linux 0.11的内核版本分析一下。linux 0.11的地址可以参考这个链接,https://github.com/tinyclub/linux-0.11-lab。

1、内核态跳转到用户态,main.c

内核态跳转到用户态,os是通过代码move_to_user_mode()来完成的。执行完这段代码的意义就是,从下一行代码开始,当前代码就是用户态的程序了。只不过,这是一个特殊的用户态程序。普通的用户程序会有两个地址空间,一个指向内核,一个指向自己。而这个特殊的用户程序就只有一个空间,即内核空间。当然,有的同学会说,普通的用户程序不能执行内核代码,必须通过系统调用才可以,为什么这个用户程序可以?其实这个只要设置一下tlb属性就可以了。

void main(void)
{/*
* other init code :-)
*/move_to_user_mode();if (!fork()) {        /* we count on this going ok */init();}
/**   NOTE!!   For any other task 'pause()' would mean we have to get a* signal to awaken, but task0 is the sole exception (see 'schedule()')* as task 0 gets activated at every idle moment (when no other tasks* can run). For task0 'pause()' just means we go check if some other* task can run, and if not we return here.*/for(;;) pause();
}

另外,关于move_to_user_mode,这个也是有意思的,大家可以看看怎么实现的,特别是这么一句"pushl $1f\n\t"

#define move_to_user_mode() \
__asm__ ("movl %%esp,%%eax\n\t" \"pushl $0x17\n\t" \   "pushl %%eax\n\t" \"pushfl\n\t" \"pushl $0x0f\n\t" \ "pushl $1f\n\t" \  "iret\n" \ "1:\tmovl $0x17,%%eax\n\t" \ "movw %%ax,%%ds\n\t" \"movw %%ax,%%es\n\t" \"movw %%ax,%%fs\n\t" \"movw %%ax,%%gs" \:::"ax")

ps:

如果是用户态进入系统态一般用int或者是sysenter。返回的话就用iret。

2、加载文件系统

os通过setup((void *) &drive_info)实现文件系统的加载。当然加载文件系统可以放在模式切换前,也可以放在切换后,这一块问题不大。加载文件系统的意义在于,有了fs之后,os可以简单地通过路径就可以实现数据的访问和读写了。

在linux的后期演进过程中,文件系统逐步变成了根文件系统。其实一个硬盘、ram、fd里面可以有多个文件系统。至于哪一个是根文件系统,都是os自己指定的,不同的文件系统之间也可以切换为根文件系统,这一点上没有多大难度。

3、打开串口

串口的打开,是通过open("/dev/tty0",O_RDWR,0)函数实现的,这一块比较简单,就是一个系统调用。

4、创建子进程

子进程的创建是通过pid=fork()实现。如果是子进程,返回为0;如果是父进程,返回为子进程的id号。fork只是clone一下父进程的内容,本身没有加载任何代码的东西。

5、准备加载用户代码

execve("/bin/sh",argv_rc,envp_rc)来完成用户空间的加载。这也是一个系统调用。简单来说,execve只负责系统空间的加载,但是不负责实际代码和数据的载入。这么做的好处就是在内存空间不富裕的时候,只载入必要的代码段和数据段。

6、page异常

在execve返回到用户侧的时候,就会产生异常。因为虽然execve分配了代码空间,但是没有实际载入数据,所以会产生一个缺页异常。这部分的内容是通过do_no_page来完成的,如果os发现当前的空间确实有数据,那么就会尽可能地从fs加载数据进来,当然如果本身空间就是非法的,这个时候就会rais真正的异常。

7、等待子进程结束

第一次执行sh的时候,fs只完成一部分工作,执行完就退出。这个时候父进程就会在(pid != wait(&i)进行等待。一般来说,这个sh会完成一些脚本的初始化工作,类似于linux rc文件。执行结束后,sh就退出来了。虽然后面也会执行sh,但是两者的输入参数是不一样的,差别也就在这个地方。

8、重复启动子进程、等待子进程

while (1) {if ((pid=fork())<0) {printf("Fork failed in init\r\n");continue;}if (!pid) {close(0);close(1);close(2);setsid();(void) open("/dev/tty0",O_RDWR,0);(void) dup(0);(void) dup(0);_exit(execve("/bin/sh",argv,envp));}while (1)if (pid == wait(&i))break;printf("\n\rchild %d died with code %04x\n\r",pid,i);sync();}

9、不可达的代码

在0.11中_exit(0)代码是不可达的。看待上面的循环逻辑,大家就可以明白,父进程会一直监视sh有没有退出。如果sh退出之后,那么会重启一个新的shell。所以,_exit(0)是永远也不会执行到的。

ps:

linux 0.11从哪方面讲,对自己的操作系统水平和认知提升都是大有裨益的。文件系统里面的程序,可以自己写一个简单的syscall + helloworld程序就可以了,把流程捋一遍,就知道系统的整个执行流程了。另外,清华大学陈渝老师的ucore也不错,有对应的代码https://github.com/chyyuu/ucore_os_lab,还有视频教程https://www.bilibili.com/video/av6538245/,简单的安装virtualbox虚拟机、配置一下ubuntu + qemu + gdb,就可以调试开发,非常适合用来练习,对自己认识bootloader、kernel、fs很有帮助。

文件系统非常重要,如果是bios启动的话,很有可能内核也会放在文件系统里面,即bios先读取文件系统,执行kernel,再加载完整的文件系统。后期的windows、linux,都是这么来做的。比如,windows就是ntoskrnl.exe,而linux则是bzImage,两者逻辑是一样的。

用户侧的代码通常都放在文件系统里面,简单地来看,就是读取一个文件,copy到内存,创建一个线程,分配一些时间片,开始调度和切换。用户程序访问资源都需要syscall,如果是内核来做这样事情,可以直接call function,连syscall都节省了。普通的用户侧代码如果没有syscall、没有内核调用,其实执行起来是很容易的事情,它的内存和内核也是分开来的。当然,如果使用到了动态库、libc,用户侧的代码也可以做得很复杂。

随想录(文件系统的第一个用户程序shell)相关推荐

  1. 【Shell 编程基础第一部分】Shell脚本HelloShell及简单的Shell基础

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/shell/759.html ☞ 点击订阅 ☜ ...

  2. 《Linux命令行大全》重点笔记——第一部分 学习shell

    <Linux命令行大全>重点笔记 第一部分 学习shell 第一章 shell是什么 1.3 简单命令 1.date:系统时间.日期. 2.cal:当月日历. 3.df:磁盘可用空间. 4 ...

  3. [转]第一章 Windows Shell是什么 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987862】...

    一个操作系统外壳的不错的定义是它是一个系统提供的用户界面,它允许用户执行公共的任务,如访问文件系统,导出执行程序,改变系统设置等.MS-DOS有一个Command.COM扮演着这个角色.然而Windo ...

  4. 苏嵌第一天,shell中一些基础知识

    一.常用环境变量 1.HOME变量 Linux系统中的每个用户都有一个相关的称作HOME的目录. 2.PATH变量 包含一列用冒号定界的目录的路径名字,便于可执行程序的搜索. 3.PS1变量 PS1变 ...

  5. 代码随想录算法训练营第一天 | 数组理论基础,704. 二分查找,27. 移除元素

    今日学习的文章链接如下: 代码随想录 (programmercarl.com) 代码随想录 (programmercarl.com) 704. 二分查找 自己看到题目的第一想法 拿到题目首先想办法,一 ...

  6. ssh移植 打包根文件系统的第一步

    ssh移植 1.网络配置(实现开发板与主机的通信) 配置虚拟机的ip地址与开发板的ip地址在同一网段下 设置电脑的以太网与他们同一网段 2.复制开发板的文件系统到虚拟机生成镜像 1.使用ssh工具 S ...

  7. 代码随想录算法训练营第一天|704二分查找 27移除元素

    理论基础 1.数组是存放在连续内存空间上的相同类型数据的集合 2.数组可以方便的通过下标索引的方式获取到下标下对应的数据 3.数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要 ...

  8. 代码随想录算法训练营第一天 704 二分查找、27 移除元素

    代码随想录算法Day1 | 704. 二分查找.27. 移除元素 Last edited time: April 5, 2023 11:27 AM 数据理论基础 数组是存放在连续内存空间上的相同类型数 ...

  9. 代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素。

    704. 二分查找 题目链接:704. 二分查找 - 力扣(LeetCode) 第一天代码跑题了,做出来了但是没有使用二分法,看了讲解,对于左闭右闭和左闭右开还需要多思考,并没有完全理解.那就先总结一 ...

最新文章

  1. 如何学习大数据!!我要做大数据!
  2. c语言图形界代码,求个用最简单的的代码来实现图形界面…
  3. python打开文件_喜大普奔 | 如何在Win10下利用Python打开grib文件
  4. python中字典的键必须是可以哈希的对象
  5. mysql查询显示柱形图_Grafana配置mysql展示自定义分组柱状图(Mac)
  6. 什么是CPU密集型、IO密集型?
  7. C# 列出进程以及详细信息
  8. linux头文件怎么编译,microsoft编译器怎么使用Linux头文件
  9. java leader 选举_简述ZK的fastleaderelection选举leader的算法
  10. 比人高效10倍,3分钟就能评估帕金森!这是腾讯新推出的AI医生
  11. day3-python之函数初识(二)
  12. 【鱼眼镜头6】[鱼眼畸变模型]:统一相机模型标定
  13. word下横线的线添加的方法
  14. 图扑 Web 可视化引擎在仿真分析领域的应用
  15. 游戏对战平台原理终结篇(转自)
  16. oracle解一元二次方程,第 6 章 浮点运算
  17. 1997年小学生计算机知识竞赛,2019年中小学优秀传统文化知识竞赛试题(小学组)97题附全答案...
  18. 再见了, 达叔!我用Python回顾一代喜剧大师203部作品,太经典了!
  19. Protell99中的铺铜设置
  20. 使用二维码解决固定资产管理的难题

热门文章

  1. 清水河畔论坛二手帖子爬虫
  2. 基于asp.net mvc的近乎产品开发培训课程(第四讲)
  3. uva 11396Claw Decomposotion(二分图判定)
  4. Andrid Floating Action Button
  5. Android的手机震动
  6. Lync Server 2013 安装准备工具 for Win 2008 R2
  7. 技术专题:厦门9月30日限制路由(网络尖冰),WAYOS或ROS解决方案
  8. WAP 的组成及主要特点
  9. CSS 两列布局 之 左侧适应,右侧固定 3种方式
  10. MySql 中的=操作符