1.linux系统调用之write源码解析(基于linux0.11)
2.lbochsè¿è¡Win11
3.如何从官网获取各个版本Linux内核的码下源码
4.Linux0.11main函数启动初始化(二)
5.linux0.11源码分析-fork进程
linux系统调用之write源码解析(基于linux0.11)
Linux系统的write函数在底层操作上与read函数有相似之处。本文主要关注一般文件的码下写操作,我们首先从入口函数开始解析。码下
进入file_write函数,码下它的码下核心逻辑是根据文件inode中的信息,确定要写入的码下配源码笔记后端业务硬盘位置,即块号。码下如果目标块已存在,码下就直接返回块号;若不存在,码下则需要创建新的码下块。这个过程涉及到bmap函数,码下它负责根据文件系统状态为新块申请空间并标记为已使用。码下
创建新块的码下过程涉及到文件系统的超级块,通过检查当前块的码下使用情况,申请一个空闲块,码下并更新超级块以标记其为已使用。旅游机票程序源码接着,超级块信息会被写回到硬盘,同时返回新建的块号。
回到file_write,处理完块的逻辑后,由于是新创建的块,其内容默认为0。这时,bread函数会读取新块的内容,这部分逻辑可以参考read函数的分析。读取后,用户数据会被写入buffer,同时标记为待写回(脏)状态。重要的是,数据实际上并未立即写入硬盘,绿色引导页源码而是先存储在缓存中。系统会通过后台线程定期将缓存中的内容刷新到硬盘。
lbochsè¿è¡Win
ãä¸è½½Linux0.ç³»ç»å ï¼æ¬é¾æ¥çä¸è½½å å·²å å«Bochs软件ã
ç¹å»æå¼é¾æ¥http://oldlinux.org/Linux.old/bochs/linux-0.-devel-.zip
2ã解ålinux-0.-devel-.zipï¼ç®å½ç»æå¦ä¸å¾æ示ï¼
è¿éçæ£ä½¿ç¨å°çæ件为ï¼Bochs-2.1.1.exe ãbochsrc-hd.bxrcãbootimage-0.ãbootimage-0.-hdãhdc-0..img
3ãç¹å»Bochs-2.1.1.exeè¿è¡å®è£ ã
4ãå®è£ å®æ¯åï¼å°æælinux-0.-devel-ä¸çæ件ï¼æ·è´å°åæå®è£ bochsä¸çæ ¹ç®å½ãåç®å½å¦ä¸ï¼
æ·è´è¿åç®å½ä¸ºï¼
5ãæ·è´å®æåï¼è¿è¡bochsrc-hd.bxrcï¼è¥å®è£ æåï¼æå¼å³è½è§å°å¦ä¸çé¢ï¼
è¿å·²ç»æ¯xPC模æå¨ä¸ï¼æ建å®æçLinuxæä½ç³»ç»ã建ç«linuxæä½å¹³å°å½ä»¤ï¼å°±è½æä½Linuxãç±äºå¦ä¹ æä½ç³»ç»éè¦ï¼æ们éè¦å¨Linuxä¸ï¼ç¼è¯æä½ç³»ç»ä»¥åå®æç®åçæºç ä¿®æ¹ï¼ä»èè¾¾å°ç解Linuxå æ ¸çè½åãæ¥ä¸æ¥ï¼ä¸»è¦æ¼ç¤ºLinuxå æ ¸æºç çä¿®æ¹ï¼ä»¥åå æ ¸çç¼è¯è¿ç¨ãç±äºè¯¥Linuxçæ¬çåå ï¼æ们è¿éè¦å¯¹ä»£ç ååºè°æ´ï¼æ¹è½æåç¼è¯ã
å æ ¸ç¼è¯è¿ç¨ï¼
1ãè¿å ¥/usr/src/linux ç®å½ä¸ï¼é®å ¥å½ä»¤ cd ../src/linux ï¼å¦ä¸å¾æ示ï¼
2ãè¿è¡ç¼è¯ï¼é®å ¥å½ä»¤ make clean å makeåã
è¥åºç°å¦ä¸æ示é误ï¼gcc-cc1: Invalid option "string-insns"ãè¿æ¯éå°Linuxç®å½ä¸çMakefileæ件ä¸ç-mstring-insnsåæ°å»æãé¤æ¤ä¹å¤ï¼æ¯ä¸ªåç®å½fsï¼libï¼kernel以åkernelç®å½ä¸çä¸ä¸ªé©±å¨ç¨åºåç®å½(math, blk_drv, chr_drv)çMakefikeå½ä¸ç-mstring-insnsåæ°å»é¤æãæ¤å¤åªæ¼ç¤ºæ ¹ç®å½ä¸çMakefileæ件修æ¹ã
3ãä¾æ¬¡ä¿®æ¹ï¼/linux/Makefileã/linux/fs/Makefileã/linux/lib/Makefileã
/linux/kernel/Makefileã/linux/kernel/math/Makefileã/linux/kernel/blk_drv/Makefileã/linux/kernel/chr_drv/Makefileæ件ãlinuxç®å½ä¸ï¼é®å ¥ï¼vi Makefileãå¦ä¸å¾æ示ï¼
æ¾å°æ件ä¸ç-mstring-insns åæ°ï¼å¹¶å å»ä¿åã
4ãå°/usr/local/binä¸æ¾å°æ件arï¼å¹¶æar æ¹å为gar,é®å ¥å½ä»¤ï¼mv ar garãå¦ä¸å¾æ示ï¼
5ãåå°linux ç®å½ä¸ï¼éæ°é®å ¥make clean以å makeï¼å®æå æ ¸çç¼è¯é¾æ¥ã
åºç°ä¸å¾æ示ï¼å³è¡¨é¢ç¨åºç¼è¯æåãå®æç¼è¯åï¼ä¾¿å¯ä»¥å¼å§ä¿®æ¹å æ ¸æºç ï¼ä»èè¾¾å°æ©å±æä½ç³»ç»åè½ä»¥åå¦ä¹ æä½ç³»ç»çç®çãè¿éï¼æ们ç®åä¿®æ¹ä¸ï¼linuxæä½ç³»ç»å¼æºå¯å¨é¡¹bootsect.sæºç ï¼è®©å¼æºåè¾åºä¸ªæ§åçå符串ã
6ãè¿å ¥/usr/src/linux/bootç®å½ï¼ä¿®æ¹bootsect.sæºç ï¼ä¿å并éåºã
! Print some inane message
mov ah,#0x ! read cursor pos
xor bh,bh
int 0x
mov cx,#
mov bx,#0x ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x ! write string, move cursor
int 0x
msg1:
.byte ,
.ascii "Loading system ...this is create by demon"
.byte ,,,
.org
7ãä¿®æ¹å®æåï¼å¨linuxç®å½ä¸ï¼éæ°é®å ¥makeå½ä»¤ï¼å®æç¼è¯ã
8ãmakeæååçæå¼å¯¼å¯å¨æ 象æ件Imageãè¥éè¦è¾åºè¿ä¸ªImageæ件, å¯ä»¥é¦å å¤ä»½bootimage-0.-hdæ件, ç¶å使ç¨ä¸é¢å½ä»¤å°±ä¼æbootimage-0.-hdæ¿æ¢ææ°çå¼å¯¼å¯å¨æ件ï¼é®å ¥å½ä»¤ï¼dd bs= if=Image of=/dev/fd0ãå¦ä¸å¾æ示ï¼
9ç¹å»resetéå¯Linuxæä½ç³»ç»ãå¦ä¸å¾æ示ï¼
如何从官网获取各个版本Linux内核的源码
访问网址 https://www.kernel.org
在页面上找到HTTP协议旁的"Location"链接,点击它或直接访问 https://www.kernel.org/pub
浏览器将展示pub/目录下的所有文件。在此页面上,找到"linux"并点击,接着点击"kernel"即可浏览到各个版本的Linux内核源码。
特别地,pub/linux/kernel目录下还包含一个名为"Historic"的子目录,这里收藏了如linux-0.和linux-0.等早期版本的源码。
Linux0.main函数启动初始化(二)
在Linux0.的初始化过程中,main函数起到关键的启动作用。首先,sti指令被调用,这表示中断功能的肇源码头位置开启,模块初始化工作至此完成。
接着,move_to_user_mode()函数被执行,这标志着程序从内核模式切换至用户模式,即执行任务0,并通过iret指令实现切换。iret指令前,数据被压入堆栈,执行后,数据根据标志位被弹入相应的寄存器。这里需要理解GDT、LDT、GDTR、LDTR和保护模式寻址方式,具体可以参考相关教程。qt moc文件源码
在切换到用户模式后,所有的进程ss0寄存器被初始化为0x,esp0则设置为PAGE_SIZE+(long)p。理解这一操作对后续学习至关重要。而cs选择子则分别为0x和0x0f,分别对应ss和cs寄存器的初始化。cs选择子从ldt的第1项开始,即进程0的代码段。
进程0主要由ldt段表示,包括代码段的展开与详细内容分析。该段基址为地址0,段限长为kb,DPL为3,说明优先级为用户。
接下来,进程0回到main函数。fork函数执行后,进程0回到main函数中。
init函数在main()中已经进行系统初始化,包括内存管理和硬件设备驱动程序等。init()函数运行在任务0第一次创建的子进程中,对bin/sh程序的环境进行初始化。
setup系统调用在目录kernel/blk_drv/hd.c中实现,对应函数为sys_setup()。此函数主要设置硬盘分区结构、RAMDISK,并挂载安装文件系统。
在执行/bin/sh后,shell程序启动,至此,Linux0.的初始化过程完成。用户可通过控制台输入命令与内核交互。printf函数产生格式化信息并输出至标准输出设备stdout,即屏幕显示。接下来,将详细分析进程管理。
linux0.源码分析-fork进程
在操作系统中,Linux0.源码中的fork函数执行流程分为启动和系统调用两个阶段。启动阶段首先在init/main.c中执行init用于启动shell,让用户执行命令。
在include/unistd.h中定义了宏,表示将__NR_fork的值复制给eax寄存器,并将_res与eax绑定。使用int 0x中断后,系统调用函数system_call被调用,从sys_call_table中找到对应的函数执行。fork函数执行时,操作系统会在内核栈里保存相关寄存器,准备中断返回。
接着,操作系统通过int调用system_call,在kernel/system_call.s中执行call _sys_call_table(,%eax,4)指令。内核栈中,因为是段内跳转,所以cs不需要入栈。ip指向call指令的下一句代码。执行call指令进入系统调用表。
在includ/linux/sys.h中,系统调用表是一个数组,根据eax即系统函数编号找到对应的函数执行。对于fork,__NR_fork值2被放入eax寄存器,%eax * 4找到sys_fork。执行sys_fork后,调用find_empty_process函数找到可用的进程号,并放入eax寄存器返回。
接着,系统调用执行copy_process函数建立新进程结构体并复制数据。新进程的ip出栈,执行完copy_process后,系统调用返回,内核栈状态改变。此阶段最后通过iret指令弹出寄存器,恢复中断前状态。
总结,fork函数通过复制当前进程结构体、处理信号并初始化新进程,实现父进程与子进程的创建与共享。子进程返回值为0,父进程返回新子进程的pid。通过fork函数的执行,操作系统能够高效地创建进程,实现多任务处理。