当前位置: 首页 > web >正文

【linux V0.11】init/main.c

文章目录

  • void main(void)
  • move_to_user_mode
  • _syscall
    • fork()
  • void init(void)
  • 其他

void main(void)

#define EXT_MEM_K(∗(unsignedshort∗)0x90002//1MB以后的扩展内存大小(KB)。
#define DRIVE_NFO(∗(structdrive info∗)0x90080//硬盘参数表基址。
#define ORIG_ROOT_DEV(∗(unsignedshort∗)0x901FC//根文件系统所在设备号。struct drive_info { char dummy[32]; } drive_info;//用于存放硬盘参数表信息
void main(void)
{ROOT_DEV = ORIG_ROOT_DEV;				//根设备号 →ROOT DEVdrive_info = DRIVE_INFO;memory_end = (1<<20) + (EXT_MEM_K<<10);	//内存大小=1MB+扩展内存(KB)∗1024。memory_end &= 0xfffff000;				//内存大小页对齐,忽略不到4KB(1页)的内存数if (memory_end > 16*1024*1024)			//如果内存超过 16MB,则限制最大可用内存为 16MBmemory_end = 16*1024*1024;if (memory_end > 12*1024*1024) 			//根据总内存大小决定保留多少空间作为缓冲区buffer_memory_end = 4*1024*1024;else if (memory_end > 6*1024*1024)buffer_memory_end = 2*1024*1024;elsebuffer_memory_end = 1*1024*1024;main_memory_start = buffer_memory_end;	//主内存从缓冲区之后开始分配
#ifdef RAMDISK			//如果定义了虚拟盘,则初始化虚拟盘。此时主内存将减少main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endifmem_init(main_memory_start,memory_end);trap_init();blk_dev_init();chr_dev_init();tty_init();time_init();sched_init();buffer_init(buffer_memory_end);hd_init();floppy_init();sti();									//开中断move_to_user_mode();					//切换到用户模式if (!fork()) {init();	//创建第一个用户态进程 init,它是所有后续进程的祖先进程。}//task0(空闲进程),是唯一一个可以在没有收到信号的情况下被唤醒的任务。for(;;) pause();	//进入 idle 循环 pause()
}

move_to_user_mode

内核在初始化结束时“切换”到初始任务0。
模拟中断调用返回过程,即利用指令iret运行初始任务0。

首先设置堆栈,模拟刚 进入中断调用过程时(具有特权层切换的) 堆栈的内容布置情况。
move_to_user_mode

movl %esp,%eax	//将当前堆栈指针 esp 保存到 eax,这是用户态堆栈的地址(即内核栈当前的位置)
pushl $0x17		//首先将Task0堆栈段选择符(SS)入栈
pushl %eax		//将 eax(即用户栈指针)压入栈中
pushfl			//将当前标志寄存器压栈,保存状态
pushl $0x0f		//将Task0代码段选择符(cs)入栈。
pushl $1f		//将下面标号1的偏移地址(eip)入栈。
iret
1:	movl $0x17,%eax	//设置用户态的段寄存器,都设置为 0x17,即用户数据段选择符movw %ax,%dsmovw %ax, %esmovw %ax,%fsmovw %ax,%gs

_syscall

这些宏的作用是让用户程序通过 int $0x80 指令触发中断,进入内核态,从而调用相应的内核函数。
_syscall0处理无参数,_syscall1处理一个参数,以此类推。
用户程序不能直接访问硬件或执行特权指令 ,必须通过“系统调用”来请求内核完成任务。

//内核实现的系统调用符号常数,用作系统调用函数表中的索引值
#define __NR_setup	0	/* used only by init, to get system going */
#define __NR_exit	1
#define __NR_fork	2
#define __NR_read	3
...#define _syscall0(type,name)
type name(void)
{long __res;__asm__ volatile ("int $0x80"	//调用系统中断0x80: "=a" (__res)				//返回值==>eax( res)。: "0" (__NR_##name));		//输入为系统中断调用号 NR nameif (__res >= 0)return (type) __res;		//如果返回值>=0,则直接返回该值errno = -__res;					//否则置出错号,并返回-1return -1;
}

当用户态执行 int $0x80 后,CPU 会跳转到内核中的中断处理入口(_system_call)。Linux 内核根据sys_call_table和传入的系统调用号(索引)找到对应的处理函数。

fork()

static inline _syscall0(int,fork)
fork
首先调用C函数find_empty_process(),取得一个进程号pid。若返回负数则说明目前任务数组已
#满。然后调用copy_process()复制进程。

_sys_fork:call _find_empty_process	//查找空进程槽testl %eax,%eaxjs 1fpush %gspushl %esipushl %edipushl %ebppushl %eaxcall _copy_process			//调用copy_process()复制进程addl $20,%esp
1:	ret

具体内容以后再看。

void init(void)

init()函数运行在任务0创建的子进程(任务1)中。它首先对第一个要执行的程序(shell)的环境进行初始化,然后加载该程序并执行之。

static char * argv_rc[] = { "/bin/sh", NULL };
static char * envp_rc[] = { "HOME=/", NULL };
static char * argv[] = { "-/bin/sh",NULL };
static char * envp[] = { "HOME=/usr/root", NULL };void init(void)
{int pid,i;//sys_setup,读取硬盘参数包括分区表信息并建立虚拟盘和安装根文件系统设备。setup((void *) &drive_info);//用读写访问方式打开设备“/dev/tty0”,这里对应终端控制台。返回的句柄0号———stdin标准输入设备(void) open("/dev/tty0",O_RDWR,0);(void) dup(0);	//复制句柄,产生句柄1号———stdout标准输出设备。(void) dup(0);	//复制句柄,产生句柄2号———stderr标准出错输出设备。printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,NR_BUFFERS*BLOCK_SIZE);//打印缓冲区块数和总字节数,每块1024字节printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);////空闲内存字节数//fork()用于创建一个子进程(子任务)。//对于被创建的子进程,fork()将返回0值,对于原(父进程)将返回子进程的进程号。if (!(pid=fork())) {//子进程执行的内容close(0);//该子进程关闭了句柄0(stdin)if (open("/etc/rc",O_RDONLY,0))//以只读方式打开/etc/rc文件_exit(1);execve("/bin/sh",argv_rc,envp_rc);//执行/bin/sh程序_exit(2);}//wait()是等待子进程停止或终止,其返回值应是子进程的进程号(pid)。//父进程等待子进程的结束。//&i是存放返回状态信息的位置。如果wait()返回值不等于子进程号,则继续等待。if (pid>0)while (pid != wait(&i));//如果执行到这里,说明刚创建的子进程的执行已停止或终止了。while (1) {//先再创建一个子进程,如果出错,打印信息后继续fork()。if ((pid=fork())<0) {printf("Fork failed in init\r\n");continue;}if (!pid) {close(0);close(1);close(2);//关闭所有以前还遗留的句柄(stdin,stdout,stderr)//新创建一个会话并设置进程组号,然后重新打开/dev/tty0作为stdin,并复制成stdout和stderr。setsid();(void) open("/dev/tty0",O_RDWR,0);(void) dup(0);(void) dup(0);_exit(execve("/bin/sh",argv,envp));//再次执行系统解释程序,但是参数环境与上次不一样}//父进程再次运行wait()等待while (1)if (pid == wait(&i))break;//如果子进程又停止了执行,打印信息,继续重试printf("\n\rchild %d died with code %04x\n\r",pid,i);sync();}_exit(0);	/* NOTE! _exit, not exit() */
}

其他

main.c还有关于时间的初始化一些内容,ez

http://www.xdnf.cn/news/15571.html

相关文章:

  • JAVA青企码协会模式系统源码支持微信公众号+微信小程序+H5+APP
  • Spring MVC 执行流程详解:一次请求经历了什么?
  • 基于铸造机床的Canopen转Profinet协议转换网关应用研究
  • 涨停板池,跌停板池,炸板池,次新股池,强势股池数据接口
  • Python命令行计算2的22次方方法
  • 轻松管理多个Go版本:g工具安装与使用
  • keeplived双击热备配置
  • Spring Security 实践及源码学习
  • 如何轻松将音乐从安卓设备传输到安卓设备
  • 504网关超时可能是哪些原因导致?
  • 短剧小程序的「技术革命」:从「粗放生长」到「精准运营」
  • Docker镜像导入、导出操作指南
  • 工业喷涂机器人的革新:艾利特协作机器人引领人机交互新纪元
  • Zookeeper入门安装与使用详解
  • PyTorch 数据加载实战:从 CSV 到图像的全流程解析
  • OpenCV 对数变换函数logTransform()
  • 手提式干粉灭火器检查工作,如何做到可执行、可追溯、可管理?
  • 基于深度学习的LSTM、GRU对大数据交通流量分析与预测的研究
  • 06-C语言:第06天笔记
  • 通过 1Panel MCP 自动部署静态网站
  • Flink Watermark原理与实战
  • Python 中 sys 库的全面解析与实战应用​
  • Agentic AI 的威胁与缓解措施
  • 【编程】-环形缓冲区
  • Basilisk库教程(二)
  • TimSort 类:论Arrays.sort的稳定性
  • Axios 和 Promise 区别对比
  • 小智完整MCP交互流程(以调节音量为例)
  • 网络基础10--ACL与包过滤
  • 从浏览器到服务器:TCP 段的网络传输之旅