随想录(文件系统的第一个用户程序shell)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱: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)相关推荐
- 【Shell 编程基础第一部分】Shell脚本HelloShell及简单的Shell基础
本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/shell/759.html ☞ 点击订阅 ☜ ...
- 《Linux命令行大全》重点笔记——第一部分 学习shell
<Linux命令行大全>重点笔记 第一部分 学习shell 第一章 shell是什么 1.3 简单命令 1.date:系统时间.日期. 2.cal:当月日历. 3.df:磁盘可用空间. 4 ...
- [转]第一章 Windows Shell是什么 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987862】...
一个操作系统外壳的不错的定义是它是一个系统提供的用户界面,它允许用户执行公共的任务,如访问文件系统,导出执行程序,改变系统设置等.MS-DOS有一个Command.COM扮演着这个角色.然而Windo ...
- 苏嵌第一天,shell中一些基础知识
一.常用环境变量 1.HOME变量 Linux系统中的每个用户都有一个相关的称作HOME的目录. 2.PATH变量 包含一列用冒号定界的目录的路径名字,便于可执行程序的搜索. 3.PS1变量 PS1变 ...
- 代码随想录算法训练营第一天 | 数组理论基础,704. 二分查找,27. 移除元素
今日学习的文章链接如下: 代码随想录 (programmercarl.com) 代码随想录 (programmercarl.com) 704. 二分查找 自己看到题目的第一想法 拿到题目首先想办法,一 ...
- ssh移植 打包根文件系统的第一步
ssh移植 1.网络配置(实现开发板与主机的通信) 配置虚拟机的ip地址与开发板的ip地址在同一网段下 设置电脑的以太网与他们同一网段 2.复制开发板的文件系统到虚拟机生成镜像 1.使用ssh工具 S ...
- 代码随想录算法训练营第一天|704二分查找 27移除元素
理论基础 1.数组是存放在连续内存空间上的相同类型数据的集合 2.数组可以方便的通过下标索引的方式获取到下标下对应的数据 3.数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要 ...
- 代码随想录算法训练营第一天 704 二分查找、27 移除元素
代码随想录算法Day1 | 704. 二分查找.27. 移除元素 Last edited time: April 5, 2023 11:27 AM 数据理论基础 数组是存放在连续内存空间上的相同类型数 ...
- 代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素。
704. 二分查找 题目链接:704. 二分查找 - 力扣(LeetCode) 第一天代码跑题了,做出来了但是没有使用二分法,看了讲解,对于左闭右闭和左闭右开还需要多思考,并没有完全理解.那就先总结一 ...
最新文章
- 如何学习大数据!!我要做大数据!
- c语言图形界代码,求个用最简单的的代码来实现图形界面…
- python打开文件_喜大普奔 | 如何在Win10下利用Python打开grib文件
- python中字典的键必须是可以哈希的对象
- mysql查询显示柱形图_Grafana配置mysql展示自定义分组柱状图(Mac)
- 什么是CPU密集型、IO密集型?
- C# 列出进程以及详细信息
- linux头文件怎么编译,microsoft编译器怎么使用Linux头文件
- java leader 选举_简述ZK的fastleaderelection选举leader的算法
- 比人高效10倍,3分钟就能评估帕金森!这是腾讯新推出的AI医生
- day3-python之函数初识(二)
- 【鱼眼镜头6】[鱼眼畸变模型]:统一相机模型标定
- word下横线的线添加的方法
- 图扑 Web 可视化引擎在仿真分析领域的应用
- 游戏对战平台原理终结篇(转自)
- oracle解一元二次方程,第 6 章 浮点运算
- 1997年小学生计算机知识竞赛,2019年中小学优秀传统文化知识竞赛试题(小学组)97题附全答案...
- 再见了, 达叔!我用Python回顾一代喜剧大师203部作品,太经典了!
- Protell99中的铺铜设置
- 使用二维码解决固定资产管理的难题