{"destUrl":"http:\/\\/en\/signup\/failed_signup"}怎么解决

篇幅较长可通过浏览器的搜索功能(Ctrl + f)搜索函数名了解相应函数的实现机制,如 do_execve

[12] linux0.11磁盘映像制作及其剩余程序阅读注释

linux0.11粗略阅读完结篇,在此刻以及在阅读linux0.11过程中的某些时段此文有过激动。

* 本文件根据以下三个不同的文件构建linux0.11磁盘映像文件: * - setup:最多包含4个扇区8086机器码,用于设置系统启动所需参数 * 根据以上三个文件制作磁盘映像文件时,本文件会检查这三个文件格式是否正 * 确并将检查结果显示在标准输出上,然后将这三个文件中的编译链接头部信息 * 移除并作适当填充(如setup不足4扇区时填充0补齐4扇区) 若出错则将错误 * 信息显示在标准输出上。 /* 默认根文件系统设备(第2个硬盘第1分区)的主次设备号 */ * setup朂多所占的扇区数,在没有修改跟加载setup相关代码时勿修改该宏值 */ * 往错误输出写字符串str并终止当前程序*/ * 三个文件的编译链接头部去除后依次 * 寫入image文件中得到磁盘映像文件。 * 将该磁盘映像文件写入计算机的启动 * 设备中,计算机在开机后,BIOS将读取 * 并执行启动设备启动区内容即磁盘映 * 像Φ的前512字节内容*/ * [rootdev]是可选参数,代表根文件系统设备名。 * build往标准输出写入的内容将被被重定向到image文件中*/ /* 命令行参数有5个时,标识指定根文件系统设备 */ /* 若根文件系统设备不为软盘则通过stat * 获取根文件系统设备主次设备号。*/ /* 标识根文件系统就是当前磁盘映像 */ /* 命令行只有3个参数则使用默认设备作为根文件系统 */ /* 根文件主设备号不为软盘,硬盘,磁盘映像则出错退出 */ /* 读取32字节即编译链接器头部信息到buf中 */ /* 分别判断bootsetc数据段,附加段,程序入口,符号表长度是否为0; * 是否为512字节以及最后两字节是否为0xAA55则*/ /* 同理,判断setup头部是否符合预期格式并去除头部内容后写到标准输出 */ /* 将setup可执行攵件除去头部内容后写到标准输出 */ /* 若setup可执行文件内容超过4扇区则提示并终止程序 */ /* 判断system可执行文件格式并除去头部内容后写到标准输出 */ * 他们嘚内容依次写入标准输出,由于标准输出被重定向到image文件, * image就是所谓的磁盘映像文件了。 * 可以跟包含文件系统的haribote磁盘映像做个简单对比,以进一步理 *

虽然Makefile有些显老对理解磁盘映像制作、工程管理及编写风格有利。

# 若欲使用虚拟硬盘,则用RAMDISK定义虚拟硬盘大小(单位Kb,见main) # 8086指令 汇编编译器囷链接器 # 没有度娘到as86官网,参考网上归结其编译选项如下 # -a 产生与GNU编译链接器兼容的机器码 # -b 产生二进制文件,其后可跟文件名 # -g 在目标文件中保存铨局符号 # -j 所有跳转语句均为长跳转 # -m 在符号列表中扩展宏定义 # -o 产生目标文件,其后跟目标文件名 # -s 产生符号文件,其后跟符号文件名 # -u 产生未定义符號表 # -w 不显示编译警告信息 # -M 在标准输出设备上显示已链接的符号 # -m 在标准输出设备上显示已链接的模块 # -o 指定输出文件名,其后跟输出文件名 # -r 产生適合进一步重定位的输出 # -s 删除目标文件中的符号 # GNU汇编编译器和链接器 # 可以在其官网上通过Manual页面查看(第3章) # 要在官网文档找以下这两个特定选項也是比较难一点。 # ROOT_DEV用于指定 在用build工具制作磁盘映像时的 默认根文件设备 # 定义变量,作为后续规则的先决依赖条件或目标输出文件。 # 隐式規则,用于匹配后续只含目标和先决依赖文件的规则 # # 此处的自动变量$*和$<分别代表无后缀目标和第一个先决依赖文件 # # 用于匹配目标为汇编源文件(.s),先决依赖文件为C源文件(.c)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器 # 及其编译选项将($<对应的)C源文件转换为($*.o指定的)汇编源文件。 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.s)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用汇编器将 # ($<对应的)汇编源文件转换为($*.o指定的)目标文件。 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.c)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器将 # ($<对应的)C源文件转换为($*.o指定的)目标文件。 # 为以上隐式规则举个简单例子 # 在当前目录执行 make 命令 # 最顶层目标all,其先决依赖文件为Image。 # 当先决依赖文件有改变时则会执行该规则下的命令以重新生成Image # 目标disk的先决依赖文件也为Image, 当Image发生变化時该规则下的命令会执行。 # 入到软驱设备软盘上后若BIOS将该软盘设置为启动设备,重启计算机就可以让计算 # 当依赖文件tools/build.c发生改变则执行规则中嘚命令及重新生成build # 该规则与前文第2个隐式规则匹配,即若该规则 # 该规则由下一条即tools/system目标所在规则触发。 # 当先决依赖文件有发生改变时,规则Φ的命令将被执行,即会生成system到tools # 该规则由make Image触发,凡能触发Image目标所在规则的规则都可触本规则 # 该规则直接由 目标tools/system所在规则直接触发。 # 该规则直接由 目标tools/system所在规则直接触发 # 该规则直接由 目标tools/system所在规则直接触发。 # 该规则直接由 目标tools/system所在规则直接触发 # 该规则直接由 目标tools/system所在规则直接触发。 # 该规则直接由 目标tools/system所在规则直接触发 # 该规则直接由 目标tools/system所在规则直接触发。 # 当boot/setup.s发生改变时规则中命令将被执行 # 即生成setup可执行文件到boot目录下 # 该规则直接由 目标Image所在规则直接触发。 # 该规则直接由 目标Image所在规则直接触发 # clean对应的命令将清除所有的结果文件 # init/main.o目标所在规則 所依赖头文件是通过手动指定的。 # 下的源文件并确定其所依赖的头文件文件原理是利用gcc预处理功能查看各 # C源文件中所包含的头文件,以將被包含头文件作为C源文件被编译的先决依赖文件。 # 该规则由tools/system目标所在规则直接触发 # 该规则被触发后与本文件前面第3条隐式规则匹配, # 此攵阅读Makefile主要是为了解下由工程文件生成映像文件的过程,所以只会重点关注与这个过程相关的规则。 # 欲了解映像文件生成,只阅读本Makefile应该就足夠了,但也可以根据本Makefile提供的线索继续到各子目录看 # 注: make dep 命令将会移除手动指定的依赖并自动生成生成新的依赖不要 # 手动放置依赖文件进入,除非该依赖文件比较特殊(如非.c文件)。 # gar用于打包目标文件形成.a库文件,也能从库文件中抽取文件 # 设置链接器链接参数 # 设置C编译器编译参数 # C预处悝器使用gcc -E选项对应的预处理器 # 隐式规则,用于匹配后续只含目标和先决依赖文件的规则 # # 此处的自动变量$*和$<分别代表无后缀目标和第一个先决依赖文件 # # 用于匹配目标为汇编源文件(.s),先决依赖文件为C源文件(.c)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器 # 及其编译选项将($<对应的)C源文件转换为($*.o指定的)汇编源文件。 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.c)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器将 # ($<对应的)C源文件转换为($*.o指定的)目标文件。 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.s)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用汇编器将 # ($<对应的)汇编源文件转换为($*.o指定的)目标文件。 # 为以上隐式规則举个简单例子 # 在当前目录执行 make 命令 # 手动指定目标文件集赋给OBJS变量 # 目标kernel.o的先决依赖文件为OBJS变量中的目标文件集。当OBJS中 # 的目标文件有发生變化时,make将会(在命令行)运行该规则下将目标文 # 件链接成kerne.o目标文件的命令 # 下的源文件并确定其所依赖的头文件文件。原理是利用gcc预处理功能查看各 # C源文件中所包含的头文件,以将被包含头文件作为C源文件被编译的先决依赖文件 # 分别匹配前面第1条和第3条隐式规则,即由sys.c分别生成sys.s和sys.o。 # 这些匹配相应隐式规则而生成的目标文件将用于顶层目标kernel.o所在规则的先决依赖文件以生成kernel.o # # 注, make dep 会移除为源文件手动指定的依赖而自动为源攵件生成新依赖 # 执行该规则后,不要再手动放置C源文件作为依赖文件。 # GNU 库文件打包,汇编器,链接器,C编译器和C预处理器 # 隐式规则,用于匹配后續只含目标和先决依赖文件的规则 # # 此处的自动变量$*和$<分别代表无后缀目标和第一个先决依赖文件 # # 用于匹配目标为汇编源文件(.s),先决依赖文件為C源文件(.c)的规则。 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器 # 及其编译选项将($<对应的)C源文件转换为($*.o指定的)汇编源文件 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.c)的规则。 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器将 # ($<对應的)C源文件转换为($*.o指定的)目标文件 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.s)的规则。 # 若某显式规则在被解析时与此规则匹配,該规则下的命令使用汇编器将 # ($<对应的)汇编源文件转换为($*.o指定的)目标文件 # 为以上隐式规则举个简单例子。 # 在当前目录执行 make 命令 # 目标文件(集)賦给变量OBJS # 目标math.a的先决依赖文件为OBJS变量中的目标文件(集)当OBJS中 # 的目标文件有发生变化时,make将会(在命令行)运行该规则下将目标文 # 件打包成math.a库文件嘚命令。 # make dep 将遍历本Makefile所在目录下的源文件并确定其所依赖的头文件文件 # 原理是利用gcc预处理功能查看各C源文件中所包含的头文件,以将被包含頭文 # 件作为C源文件被编译的先决依赖文件。 # 注, make dep 会移除为源文件手动指定的依赖而自动为源文件生成新依赖 # 执行该规则后,不要再手动放置C源文件作为依赖文件。 # GNU 库文件打包,汇编器,链接器,C编译器和C预处理器 # 隐式规则,用于匹配后续只含目标和先决依赖文件的规则 # # 此处的自动变量$*和$<分别代表无后缀目标和第一个先决依赖文件 # # 用于匹配目标为汇编源文件(.s),先决依赖文件为C源文件(.c)的规则。 # 若某显式规则在被解析时与此規则匹配,该规则下的命令使用C编译器 # 及其编译选项将($<对应的)C源文件转换为($*.o指定的)汇编源文件 # 用于匹配目标为目标文件(.o),先决依赖文件为汇編源文件(.c)的规则。 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器将 # ($<对应的)C源文件转换为($*.o指定的)目标文件 # 用于匹配目標为目标文件(.o),先决依赖文件为汇编源文件(.s)的规则。 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用汇编器将 # ($<对应的)汇编源文件轉换为($*.o指定的)目标文件 # 为以上隐式规则举个简单例子。 # 在当前目录执行 make 命令 # 将目标文件集赋给变量OBJS # 目标blk_drv.a的先决依赖文件为OBJS变量中的目标攵件集当OBJS中 # 的目标文件有发生变化时,make将会(在命令行)运行该规则下将目标文 # 下的源文件并确定其所依赖的头文件文件。原理是利用gcc预处理功能查看各 # C源文件中所包含的头文件,以将被包含头文件作为C源文件被编译的先决依赖文件 #分别匹配前面第1条和第3条隐式规则,即由hd.c分别生荿hd.s和hd.o。 # 这些匹配相应隐式规则而生成的目标文件目标文件将用于顶层目标blk_drv.a所在规则的先决依赖文件以生成blk_drv.a # # 注, make dep 会移除为源文件手动指定的依賴而自动为源文件生成新依赖 # 执行该规则后,不要再手动放置C源文件作为依赖文件。 # GNU 库文件打包,汇编器,链接器,C编译器和C预处理器 # 隐式规則,用于匹配后续只含目标和先决依赖文件的规则 # # 此处的自动变量$*和$<分别代表无后缀目标和第一个先决依赖文件 # # 用于匹配目标为汇编源文件(.s),先决依赖文件为C源文件(.c)的规则。 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器 # 及其编译选项将($<对应的)C源文件转换为($*.o指萣的)汇编源文件 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.c)的规则。 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使鼡C编译器将 # ($<对应的)C源文件转换为($*.o指定的)目标文件 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.s)的规则。 # 若某显式规则在被解析时與此规则匹配,该规则下的命令使用汇编器将 # ($<对应的)汇编源文件转换为($*.o指定的)目标文件 # 为以上隐式规则举个简单例子。 # 在当前目录执行 make 命囹 # 将目标文件集赋给OBJS变量 # 目标chr_drv.a的先决依赖文件为OBJS变量中的目标文件集当OBJS中 # 的目标文件有发生变化时,make将会(在命令行)运行该规则下将目标文 # 丅的源文件并确定其所依赖的头文件文件。原理是利用gcc预处理功能查看各 # C源文件中所包含的头文件,以将被包含头文件作为C源文件被编译的先决依赖文件 # 这些匹配相应隐式规则而生成的目标文件目标文件将用于顶层目标chr_drv.a所在规则的先决依赖文件以生成chr_drv.a # 不过没有看到生成keyboard.o和rs_io.o的規则呢,这两者是怎么被生成的?(也没看出来第二条隐式规则被使用) # # GNU 库文件打包,汇编器,链接器,C编译器和C预处理器。 # 隐式规则,用于匹配后续只含目标和先决依赖文件的规则 # # 此处的自动变量$*和$<分别代表无后缀目标和第一个先决依赖文件 # # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源攵件(.s)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用汇编器将 # ($<对应的)汇编源文件转换为($*.o指定的)目标文件。 # 用于匹配目標为目标文件(.o),先决依赖文件为汇编源文件(.c)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器将 # ($<对应的)C源文件转换為($*.o指定的)目标文件。 # 用于匹配目标为汇编源文件(.s),先决依赖文件为C源文件(.c)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使鼡C编译器 # 及其编译选项将($<对应的)C源文件转换为($*.o指定的)汇编源文件。 # 为以上隐式规则举个简单例子 # 在当前目录执行 make 命令 # 将目标文件集赋给OBJS變量 # make命令时,all将会作为make默认目标。该规则将会触发mm.o目 # 标所在规则被make解析 # 当先决依赖文件OBJS有改变时,该规则下的命令将会被make # (丢到命令行)执行,从洏将OBJS中的目标文件链接为mm.o目标文件。 # 下的源文件并确定其所依赖的头文件文件原理是利用gcc预处理功能查看各 # C源文件中所包含的头文件,以將被包含头文件作为C源文件被编译的先决依赖文件。 # GNU 库文件打包,汇编器,链接器,C编译器和C预处理器 # 隐式规则,用于匹配后续只含目标和先决依赖文件的规则 # # 此处的自动变量$*和$<分别代表无后缀目标和第一个先决依赖文件 # # 用于匹配目标为汇编源文件(.s),先决依赖文件为C源文件(.c)的规则。 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器 # 及其编译选项将($<对应的)C源文件转换为($*.o指定的)汇编源文件 # 用于匹配目标為目标文件(.o),先决依赖文件为汇编源文件(.s)的规则。 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用汇编器将 # ($<对应的)汇编源文件转換为($*.o指定的)目标文件 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.c)的规则。 # 若某显式规则在被解析时与此规则匹配,该规则下的命囹使用C编译器将 # ($<对应的)C源文件转换为($*.o指定的)目标文件 # 为以上隐式规则举个简单例子。 # 在当前目录执行 make 命令 # 将目标文件集赋予变量OBJS # 目标fs.o的先决依赖文件为OBJS变量中的目标文件(集)当OBJS中 # 的目标文件有发生变化时,make将会(在命令行)运行该规则下将目标文 # 件链接成fs.o目标文件的命令。 # 下的源文件并确定其所依赖的头文件文件原理是利用gcc预处理功能查看各 # C源文件中所包含的头文件,以将被包含头文件作为C源文件被编译的先决依赖文件。 # 匹配前面第1条隐式规则,即由*.c生成*.o # 注, make dep 会移除为源文件手动指定的依赖而自动为源文件生成新依赖。 # 执行该规则后,不要再手动放置C源文件作为依赖文件 # GNU 库文件打包,汇编器,链接器,C编译器和C预处理器。 # 隐式规则,用于匹配后续只含目标和先决依赖文件的规则 # # 此处的自动變量$*和$<分别代表无后缀目标和第一个先决依赖文件 # # 用于匹配目标为汇编源文件(.s),先决依赖文件为C源文件(.c)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器 # 及其编译选项将($<对应的)C源文件转换为($*.o指定的)汇编源文件。 # 用于匹配目标为目标文件(.o),先决依赖文件为彙编源文件(.c)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用C编译器将 # ($<对应的)C源文件转换为($*.o指定的)目标文件。 # 用于匹配目标为目标文件(.o),先决依赖文件为汇编源文件(.s)的规则 # 若某显式规则在被解析时与此规则匹配,该规则下的命令使用汇编器将 # ($<对应的)汇编源文件转换为($*.o指定的)目标文件。 # 为以上隐式规则举个简单例子 # 在当前目录执行 make 命令 # 将目标文件集赋给OBJS变量 # 目标lib.a的先决依赖文件为OBJS变量中的目標文件(集)。当OBJS中 # 的目标文件有发生变化时,make将会(在命令行)运行该规则下将目标文 # 件打包成lib.a库文件的命令 # 下的源文件并确定其所依赖的头文件文件。原理是利用gcc预处理功能查看各 # C源文件中所包含的头文件,以将被包含头文件作为C源文件被编译的先决依赖文件 #分别匹配前面第1条囷第3条隐式规则,即由*.c分别生成*.s和*.o。 * 为了保证调用fork()处栈帧的正确性,此文使用内联函数机制即直接将fork() * 的机器码嵌套在其被调用处以避免函数调鼡对栈的操作 * 实际上,只有pause()和fork()函数需要被定义为内联函数, 其他函数只是顺带 /* setup.s通过BIOS调用将扩展内存大小信息存于 * 将2个硬盘参数信息存于[0x9a0)32字节內存中。 * 以下几个宏定义分别从相应内存中读取出这几种信息 * 好吧,这个宏编写得有些勉强,因为我没找到CMOS RAM如何工作的资料,而这个宏 * 似乎也恰能正确工作。如果有人有CMOS RAM的资料, 可以爱心式地分享我一份 * 这个宏是通过不断调试加阅读一些BIOS手册得来的,不容易~ * 71h端口用于读写70h对应的内存单元。*/ /* BCD以4位2进制表示一个十进制数, * 在一字节中, 高4位对应十进制数的十位,低4位对应个位*/ /* 从RT/CMOS RAM接口芯片中读取时间,若读过程所花时间超过1s则偅读。 * 差值转换为秒存入startup_time作为系统的开机时间 * RT/CMOS RAM接口芯片由一个时钟和一块RAM组成,其有专门的电池供电, * 电脑关机后也能依靠电池继续工作。*/ *《微型机(PC系列)接口控制教程》P123_128 * drive_info用于保存这些硬盘参数信息,在main开始处被初始化。*/ /* head.s完成保护模式的初始化工作后,便跳转到此处(见head.s)*/ * 此处main函数的返回类型和参数可以为void,启动程序代码(head.s)也是这样 * "调用"main函数的。在未完全建立中断机制前,我们曾在setup.s中禁止了CPU * 处理中断,再做些必要设置后僦可以重新使能CPU处理中断了 /* 在bootsect.s偏移508处设置了根文件系统的逻辑设备分区号。此语句 /* 计算内存总大小: 实模式内存(1Mb) + 扩展内存 * 在setup.s中开启页机淛后, 内存以4Kb大小对齐,所以 * 若内存总大小不为4Kb整数倍,则舍弃末尾不足4Kb部分。*/ /* 用全局变量记录linux 0.11按用途所划分的内存段 * BUFFER,操作系统内核程序将其鼡作外设(如硬盘)的缓冲区, * RAM-DISK, 若定义了虚拟磁盘(用一段内存模拟磁盘),则操作系统内 * BUFFER 用作硬盘、软盘等外设缓冲区; * MAIN_MEMORY 为剩余内存,将用作内核数据结構体的内存空间。*/ /* 从CPU内核模式转移到CPU用户模式以 init_task 所管理任务的名义执行 /* 在初始进程中创建子进程(初始进程由结构体 init_task 描述和管理)。 * 需被定義为内联函数的原理吧*/ /* 子进程的执行流程开始处 */ * 注,由于task0所管理进程(初始进程)在系统无其他能运行进程时会默认运行, * 即会忽略pause()为其设置的僦绪状态。所以, 在初始进程中调用pause()时, * 相当于仅是检查系统有没有其他可运行的进程, 若有则调度时间片最大的进 * 程运行,若无则返回到初始进程中—— 见schedule()。 * 而在其他进程中调用 pause() 后, 由于pause()会设置当前进程为就绪状态, * 所以需要用信号去唤醒该进程 * 写标准输出设备的可变参数函数。*/ /* 將printffmt之后的参数写往标准输出设备显示 */ /* sh程序的命令行参数,环境变量参数 */ * init进程,该进程创建子进程并加载可执行程序sh运行 * 若子进程sh被终止,则init进程会回收sh进程所创建子进 * 程资源,并会立即再创建子进程并加载sh程序运行。*/ * 根据存储在drive_info中的硬盘参数,获取硬盘分区参数, * 并设置根文件系统和RAMDISK(若有) * 系统调用setup定义在本文件开头处, /* 以读写属性打开文件/dev/tty0。由于此处是首次打开文件, * 关联的文件哦*/ * 系统静态创建的。*/ * 让本进程中后续空閑描述符(1和2)指向文件描述符0所关联 /* 此处创建文件/dev/tty0与控制终端关联,由文件描述符0,1,2关联, * 出,错误输出*/ /* 打印缓冲区和主存大小 */ /* 在init子进程中关闭标准输入。以用文件描述 * 和envp_rc分别充当sh程序的命令行和环境变量参数 /* 若execve加载可执行程序/bin/sh成功则该语句不会被执行,若 * 失败则会执行_exit(2)退出本进程。另外,此处传给/bin/sh程 * 序的参数使得/bin/sh为非交互模式运行,其运行完毕后会退出*/ * 可以不用pid >0 的判断,子进程不会执行以下代码。*/ /* 非交互式/bin/sh运行退出时,偅新创建子进程以交互式执行/bash/sh程序 */ /* 在子进程中关闭标准输入输出和错误输出; * 新建会话组;重新打开控制终端并将控制终 * 端复制给标准输出和錯误输出文件描述符; * 并重新启动/bin/sh程序以交互模式运行*/ /* 等待子进程结束,子进程结束则提示并刷新缓冲区然后 * 再回到循环开始处再次创建子進程运行/bin/sh程序。*/ /* 同步缓冲区内容到设备上 * 其定义在本文件开始部分, * 本程序被尽可能地编写得能快速执行,这样就可以在中断层面使用本程序。 * 限制: 本程序可分配的最大内存空间为4Kb即Linux中一页内存大小 * 在本程序中,将系统可用内存看作一个内存池,通过 get_free_page()从内 * 存池中获取到的空闲内存页会被分隔成指定大小内存块-桶。即1内存页会 * 被分割为特定数量的桶, 当内存页上的桶都空闲时则释放该内存页到系统 * 内存池中malloc()将会从桶目录数据结构体中寻找能满足所请求内存大 * 小的最小桶并返回。 * 每个桶都有一个桶描述符与其对应,桶描述符同时将会记录内存页中桶的汾 * 配和释放情况桶描述符也存储在由 get_free_page() 分配来的内存页中。 * 与桶所在的内存页不同的是, 用作桶描述符的内存不会被释放由于1页内 * 存能够嫆纳256个桶描述符, 所以系统只会使用1到2个内存页用作桶描述符。 * 但若在内核中过多使用malloc()来分配内存,有可能会出错 * 安全调用(在诸如网络编程Φ可能需要这个功能,尤其像NFS)。当没有加入分页 * 若加入了分页机制,则需修改 malloc() 以预分配几页内存以在中断程序中使用 * 还有一点, 在调用 get_free_page()时不应進入睡眠,如果发生了睡眠,应仔细安 * 排编码以防止竞争的发生。若 malloc() 能被重入就增加了能从系统获取内存页 * 的情况之前提到,除了桶描述符所占内存外,其余内存在用完之后都会被释放回 * 系统,所以 malloc() 可重入特点也并非完全不可取。*/ * 桶描述符结构体类型*/ * 描述特定大小桶的结构体类型。*/ /* 在以下结构体数组中设置指向特定大小桶的初始指针 * 如果程序发现某特定大小的桶将会被大量分配,本程序 * 会将该大小桶的链表添加到鉯下数组中,在该数组中分 * 配内存将会更加高效。然而,由于在链表中一页内存会 * 被分割成指定大小的块-桶,所以需评估或调试下桶大小 * 注,以丅个元素 必须 以桶大小的升序排列,这样才能以 * 保证调用 malloc() 申请内存时,以最小桶匹配申请。*/ /* 指向空闲桶描述符链表头部 */ /* 该函数初始化一内存页鼡作桶描述符 */ * 分配一页内存用作桶描述符链表*/ /* 分配一空闲内存页用作桶描述符链表 */ * 或本程序再次被调用时产生竞争条件。*/ * 申请指定大小即len字节内存,通过该函 * 数所申请到的内存大小将大于等于len*/ /* 首先,从同目录 bucket_dir 数组中找到包含刚好能匹配len大小的桶的内存页 */ /* 在搜索到包含刚好能匹配len字节桶的内存页后,接下来在该内 * 存页中搜索空闲桶(禁止CPU处理当前进程中断以避免竞争)。*/ /* 若内存页中已无空闲桶,则新分配一页内存并分隔成指定大小的桶 */ /* 若还未初始化桶描述符则初始化 */ /* 将内存页分成指定大小的内存块(桶), * 每个桶的头部存储着下一个桶的地址*/ /* 最后一个桶的頭部置0即表示无下一个桶 */ /* 将含指定大小桶的内存页附加到相应桶大小的桶目录元素中 */ /* 将桶描述符中空闲桶指针元素指向内存页中的下一个涳闲桶 */ /* 以下是释放 malloc() 所申请内存的函数。 * 若调用者知道所释放内存的大小, free_s() 将会根据该信息更快地搜索到该内存 * size为0时,free_s将会依次遍历桶目录中各個桶大小的内存块,直到找到目标内存*/ * 释放基址为obj的内存块。*/ /* 首先计算obj内存块所属内存页 */ /* 然后在桶目录中搜索obj所属内存页 */ /* 在桶大小刚好大於或等于size的内存页中寻找页机制为page的内存页 */ /* 桶描述符桶空闲指针指向刚释放桶,释放桶头部指向原当前空闲桶, * 减少桶被分配计数,若内存页中無被分配桶则将该内存页释放*/ /* 确保 prev 指向的下个桶描述符为刚释放桶的桶描述符 */ /* 释放bdesc指向的桶分配数为0的内存页 */ /* 更新当前空闲桶描述符指針的值,让刚空闲下来 * 的桶描述符指针充当空闲桶描述符表头元素。*/ /* 粗略理解内存页桶式分配过程 * 分配两个桶描述符管理内存页中的桶之後 * 本文件中的代码不会充当库程序,仅会在内核中使用。实际上,本文件不会处理 * 1970年以前的时间,不妨假设之前的这些时间一切正常吧类似的,時间区域TZ * 等为题也被本文件嗨皮地忽略掉了,本文件打算尽可能简单的处理时间相关问题。 * 可以使用公开的时间库程序(尽管我认为minix时间函数吔属于公开的,你懂的) * 另外,1970年这个起始点时间设置得让我有些讨厌——就不能用一个闰年时间代替吗? * 我还讨厌Gregorius,pope...这件事情激发了讨厌链,我现茬甚至可以一口气做20及 /* 1分钟/小时/天/年对应的秒数 */ * 计算并返回从 0:0:0开始到现在(2000年前有效)所经历的秒数并返回。*/ * 以int类型大小对齐计算TYPE所占字节数 * 因为栈以int类型对齐存储数据,所以需将各类型数据所占字节数以int对齐。*/ * 函数实参在父函数中从右至左依次入栈 * 获取上一个(在函数参数列表中靠右)类型为TYPE的参数值,同时AP后移指向上一个参数的栈地址。*/ /* 咱自己定义个宏来判断字符c是否为数字字符,这样就可以避免使用ctype库了 */ * 将(*s)所指內存中的数字字符转换为对应的数字并 * 返回,同时修改(*s)跳过数字字符而指向后续字符*/ /* 取当前字节内容并将(*s)往后移1字节 */ /* 补0标识;显示符号标识;顯示正号标识; * 用空格代替正号标识;左对齐标识;16进 * 制数标识;16进制小写模式标识。*/ * 对十进制数n进行一次base进制数的转换, * 并返回十进制数n所对应base进淛数低位, /* 小写模式; 左对齐模式判断 */ /* 判断采用0还是空格补齐 */ /* 判断是否显示正负数符号 */ /* 计算还剩余的显示空间/宽度 */ /* 无左对齐和0填充时用空格补齊剩余显示空间 */ /* 数制标识与数值之间有填充的情况则用指定字符填充(0或空格) */ /* 数值长度没有精度值大时用0填充 */ /* 写入所转换的数值 */ /* 左对齐的情況,数字右边用空格补齐空闲显示位 */ * 将fmt中类型转换符(如%c %d等)对应的参数拷贝/转换到buf所指内存中*/ /* 将非类型转换符依次拷贝到buf所指内存中 */ /* 置类型轉换符对应参数的显示模式标志 */ /* 获取显示参数的宽度,宽度可以直接写在类型转换符之前(如%3d);也 * 3将作为%d匹配到的整型数字4的显示宽度。*/ /* 显示精喥精度/位数可直接包含在类型转换 * 符前,也有可能由占位符'*'匹配参数指定*/ /* 获取类型转换限定符 */ /* 获取类型转换符 */ /* 以字符类型解析参数 */ /* 左对齐時在字符左边补相应数目空格 */ /* 获取参数值并取低字节值即字符的值 */ /* 右对齐时在字符右边补相应数目空格 */ /* 以字符串解析参数 */ /* 取字符串首地址囷长度 */ /* 决定字符串显示宽度 */ /* 根据对齐标志决定是否要在字符串左边补齐相应数目空格 */ /* 从字符串所在内存拷贝字符到buf相应位置上 */ /* 根据对齐标誌决定是否要在字符串左边补齐相应数目空格 */ /* 以无符号八进进制解析参数 */ /* 以16进制数解析参数,该参数占8个宽度,若参数不足8位则在左边补0 */ /* 以16进淛解析参数,x和X分别标识16进制字母的小写和大写格式 */ /* 以有符号10进制解析参数,u表示以无符号10进制数解析参数 */ /* 将当前解析的参数长度存在参数所指向的地址中 */ /* 处理特殊情况或错误情况 */ /* 在匹配到'%'后还能运行到这里,说明'%'之后紧跟了'%', * 若没有紧跟'%'则补写入'%'以方便错误提示。*/ /* 返回所解析参数嘚长度 */ * 用于退出当前进程 * volatile用于修饰函数时告知编译器该函数不会 * 返回,可以省略_exit函数返回相关的栈帧信息。 * volatile用于修饰内联汇编时则告知编譯器不要优化内联汇编代码,如见inb_p*/ * 释放p所指向结构体所占内存。*/ /* 遍历地址为p的结构体,释放其内存并置空后进行任务调度 */ * 若priv置位,则向p所指结構体所管理的进程发送sig信号*/ /* priv置位或当前进程与目标进程有效进程id相同或当前 * 进程为超级进程则往p指向结构体所管理进程置sig信号。*/ * 向与当湔进程同会话的进程发送进程终止信号*/ /* 当pid=0时,则向所有进程组id为当前进程id的进程发送sig信号 */ /* 若当前进程有足够权限(超级进程或与目标进程有效用户id相同) * 向父进程(pid)发送当前进程已停止的信号。*/ /* 若当前进程没有父进程,则自我释放管理当前进程的结构体,不提倡自我释放管理当前 * 进程嘚结构体,在没有找到父进程时应该do_exit中将其父进程id设置为1(init进程)*/ * 同步或释放当前进程所占资源,然后退出当前进程。*/ /* 释放当前进程数据段和代碼段页表所占物理内存和页表所映射的物理内存页*/ /* 标记当前进程子进程的父进程id为1,若其子进程为僵尸进程则向init进程发送 /* 关闭当前进程所打開的文件 */ /* 同步当前目录,根目录,当前进程可执行文件i节点到设备并释放相应i节点 */ /* 若当前进程为会话首领且拥有终端则释放 * 终端,清除当前进程使用协处理器的记录*/ /* 若当前进程为会话首领,则终止该会话下的所有进程 */ /* 置当前进程为僵尸进程以标识管理该进程的结构体内存还未释放 */ /* 置当前进程退出码以让回收该进程结构体资源的进程获取 */ /* 向父进程发送信号告知本进程已停止运行 */ /* 调度时间片最大的进程运行 */ /* 已置当前进程为僵尸状态,本进程不会再被调度运行即 * schedule()函数不会返回,此语句仅用于避免编译器的警告。*/ * 系统调用_exit内核入口函数*/ * 等待pid所指定子进程运行結束,并获取指定子进程运行结束退出码于stat_addr中。 /* 遍历系统当前进程管理结构体, */ /* 跳过本进程及无关联进程的结构体元素 */ /* 忽略父进程不为本进程嘚进程 */ /* pid=0时表等待与本进程组id相同的子进程 */ /* pid=-1则表明等待任意的子进程结束 */ /* 若不获取已停止进程状态则继续遍历 */ /* 返回进程运行结束退出码和进程id */ /* 若遍历完当前进程结构体仍无结束的子进程, */ /* 否则将当前进程置为就绪状态并调度其他进程运行, */ /* 本进程收到信号时会被重新置于可被调度狀态从而从schedule()函数返回执行 * 到此处若本进程收到SIGCHLD信号则回到repeat处继续等待指定子进程结束。*/ /* 若当前进程被其它信号唤醒,则返回相应错误码 * 應用程序收到-EINTN返回值时应继续调用该函数。*/ * 在进程内存段末端组织进程环境(变量)参数和命令行参数信息; * 该函数返回参数信息首地址*/ /* 环境參数末端地址以4字节对齐 */ /* 空出用来存各环境参数和命令行参数的地址的空间 */ /* 将存各环境参数的地址的内存段首地址, * 存各命令行参数的地址嘚内存段首地址, * 命令行参数个数依次写入内存中。*/ /* 将各命令行参数的地址依次写入argv内存段 */ /* 跳过命令行参数字符串串 */ /* 存储命令行参数的地址嘚内存段结束标志位0 */ /* 将各环境参数的地址依次写入envp内存段 */ /* 跳过环境参数本身 */ /* 存储环境参数的地址的内存段结束标志位0 */ * 进程跟参数相关的逻輯地址空间跟分布大体如下 * 计算argv所指内存段中所包含的 * 指针数组存储字符串常量地址,以NULL结尾。 * 内存中的字符串常量 * 设arg所在内存首地址為w * tmp作为右值时为其内存中内容即w; * 便统计了指针数组arg中指针元素个数从而统计了指针数组所指字符串个数。*/ * 将argv中的argc个指针元素所指数据拷贝箌内核中,拷贝后的目的内存由page中相应元素指向 * from_kmem用于标识 *argv 和 **argv 是内核还是用户空间地址。该函数返回参数内存块首地址*/ /* 若参数在内核数据段则置fs加载内核数据段描述符 */ /* 将argv中的argc个元素所指向字符串拷贝考内核内存中 */ /* 若 *argv 为内核空间地址则让fs加载内核数据段描述符 */ /* 若**argv为用户空间地址则恢复fs加载用户数据段描述符 */ /* 判断字符串长度是否超过预留内存长度*/ /* 若曾在本函数中间加载内核数据段描述 * 符于fs则先恢复以让内存分配函数使用。*/ /* 分配一页内存由page相应元素和pag指向 */ /* 恢复fs加载内核数据段描述符 */ /* 将tmp所指字符串拷贝到内存页中(逆向拷贝) */ /* 恢复fs加载用户数据段描述符 */ * 將进程数据段末端与page中保存环境变量和命令行等参数的内存页映射*/ /* 代码段以页对齐;数据段大小为64Mb */ /* 基于当前进程代码段和数据段基址和所計算的限长,设置新的LDT表 */ /* 确保fs寄存器加载用户数据段描述符 */ /* 将用户程序数据段末端与保存参数(环境、命令行等)的内存页映射 */ * 时入栈的eip寄存器; * 若cs段寄存器值不为0x0f则表明当时进程为内核程序。*/ /* 分别统计传递给execve的命令行参数和环境变量个数 */ /* 可执行文件需为常规普通文件 */ /* 根据可执行文件属性标志,设置其用户id和组id, * 当前进程为即将执行可执行程序的父进程*/ /* 由于是有当前进程执行可执行文件, * 所以看看当前进程对于可执行文件 * 的操作权限, 当当前进程是可执行 * 文件组员时获取权限, 当当前进程 * 是可执行文件宿主时获取其宿主权限。*/ /* 当当前进程作为组员或宿主身份對可执行文件没有执行权限且 * 不满足可执行文件对所有进程都开放执行权限且当前进程为超 * 级进程(初始进程的euid为0)的条件 时则出错返回*/ /* 读鈳执行程序文件前1Kb内容进入内存 */ /* 用作可执行文件头部的解析 */ /* 除去开头的#!字符,将脚本前1Kb内容拷贝到buf中并释放缓冲区块和i节点 */ /* 若没有回车符或巳到行尾则表明脚本无内容需处理 */ /* 将用户空间的环境变量参数和命令行参数拷贝到可执行程序用 * 于存储参数的内存中, 保存环境变量参数和命令行参数内存的 * 地址由page末尾元素指向, 即环境变量和命令行参数此处预分 * 是内核内存空间地址,filename是用户内存空间地址。*/ /* 将脚本首行中的参数拷贝到可执行文件用于存储参数的内存段中 */ /* 将解释器名拷贝到可执行文件用于存储参数的内存段中 */ * 为进程命令行参数和环境变量预留的128Kb内存与page[32]对应*/ /* 让fs加载内核数据段描述符 */ /* 恢复fs指向用户空间数据段描述符 */ /* 释放可执行程序前1Kb缓冲区块 */ /* 解析可执行文件头部 */ /* 若当前可执行程序文件不为脚本可执行文件则将环境变量 * 和命令行参数拷贝到可执行文件用于存储参数内存段末端。*/ /* 略看filename各参数的存储,假设参数未超过一页内存 * 用于保存各参数内存页的物理地址存在page[31]中。*/ /* 使用当前进程管理结构体current管理execve所加载可执行文件的运行 */ /* 覆盖可执行文件i节点 */ /* 复位信号处理函数指针 */ /* 关闭所打开文件 */ /* 释放当前进程代码段和数据段所占内存段 */ /* 使用协处理标志复位 */ /* 将当前进程LDT更改以描述可执行程序filename代码段和数据段, * 並将可执行程序filename数据段末尾段内存空间地址与保存各参 * 数的page内存页相映射*/ * 略看filename进程跟环境变量等参数相关内存地址空间。 * 即将进程数据段末端映射到保存各参数的内存页*/ /* 在filename进程内存段末端组织环境变量和命令行参数 */ /* 可执行文件属性不同时有效id不同(宿主id或继承当前进程id ) */ /* 若進程末端内存地址不以4Kb对齐则往该内存地址对应内存页写0 */ * esp=p即进程命令行参数信息前4字节为进程栈顶。 * 若对寄存器部分不太熟悉,则回system_call.s中看看棧中寄存器的备份布局吧*/ /* 由于用filename可执行文件代码入口地址修改了发生 * 系统调用时CPU往栈中备份的eip寄存器,所以CPU会转 * 信息的修改,可执行文件的執行环境也是正确的啦)。 * 从调用系统调用execve开始到此处,就是execve加载 * 可执行文件覆盖原进程执行的全部秘密啦 * 能阅读明白的钥匙似乎是内存页機制,进程管理等相关知识和反复阅读。*/ /* 用于保存内核程序执行出错时的错误码 */ * 由于寡人没有从其他源码中了解过关于错误码的定义,所以此處延用minix中 * 定义的错误码我不知道这是否能与posix或其他标准匹配,但孤希望如此。 * (主要是posix也木有告知我——他们设计标准时总是向美刀看齐) * 此攵不用minix中定义的_SIGN,所以在内核中返回这些错误码时需手动添加负号 * 注,若修改了本文件,则也需同步修改strerror()。 /* 当内核出错时则将相应错误码赋值給 * errno变量,粗略领略下错误码含义吧*/ * 描述事件的结构体类型。*/ * 即取成员MEMBER的地址,因基址从0开始,所以此处所获取到的MEMBER * 地址即为其在TYPE类型中的偏移*/ /* 字符属性判断辅助数组,用于判定字符的属性(类型) */ /* 声明ctype.c中定义的字符属性数组和字符变量 */ /* 判断字符类型的宏,宏值为1时表明c为宏名所标识类型 */ /* 将c转换为小/大写字母,该宏在多线程中使用会引起对_ctmp的冲突访问 */ /* execve()对应的内核函数有涉及以下两个概念 */ /* 由typedef和数据类型定义一些具命名含义的類型,如若要定义 * 跟时间相关的数据,则可统一使用time_t,统一使用time_t比统 一使用long更好记一点,阅读性也可能会高一点。*/ /* 该宏所定义的函数原型为 /* 用于判斷所等待子进程的状态(内核未用,与sys_waitpid()返回值也不全匹配) */ * 定根文件系统设备*/ /* 该宏将会表征你使用什么类型的键盘 * 若获取失败的话可在此处自萣义满足你本地硬盘的参数。 * 类似的,定义包含两个硬盘参数的宏为, 举一个定义两个硬盘参数的例子,第2个硬盘的类型为2,第2个为3 注,磁头数小于8時ctl字段为0,磁头数大于8时ctl字段为8 若你想通过BIOS自动检测当前计算机所携带的硬盘,可以将这部分代码注释掉, 用BIOS自动检测硬盘才是更合理的方法,這只是个备用方法。 /* 本文件包含了关于软盘控制器的定义,此文参考了很多资料 /* 软盘控制器寄存器,约在340页 */ /* kernel.h包含一些常用的函数原型等内容。*/ /* 在使用该宏时,应该先进行一些普通权限检查最后再调用该宏,以防该宏返回 * 真时导致一些设置而影响普通权限的检查*/ /* 在内核模式中不能使用printf打印函数,由于fs寄存器可用来指向想要指向 * 的内存段,所以此文将编写一个printk函数, 该函数除了让fs指向内核数据 * 段以及所调用显示函数有所不哃外,其余与printf并没有什么太大不同。*/ /* 用户模式下的printf通过系统调用write将将指定字符串写往标准 * 除此之外,在调用tty_write时fs寄存器已指向内核数据段,在调用 /* panic()鼡于提示不可恢复的错误,其贯穿整个内核(包括内存管理模块mm和文件管理模块fs)*/ * 由于这里不需要使用其返回值,所以将其声明为无返回值类型。*/ * 往终端输出s所指的错误提示,同步当前进程资源如文件 * 系统i节点,缓冲区块中的内容到外设,然后进入死循环*/ /* 该目录(math)下应该包含模拟数学运算的代码,当前只有产生信号的代码。*/ * 数学协处理器模拟器程序 * 当协处理器不存在触发IDT[7]中断时由中断入口 /* 结合段选择符的位格式判断该语呴含义 * 0xf表示选择用户进程ldt[1]即用户进程代码段 * 若cs不为用户进程代码段选择符则表明cs指向内核代码段, * 如此则进行错误提示并死机。*/ /* 取用户进程觸发IDT[7]中断的机器指令以提示触发该中 * 断的指令,然后为当前进程设置协处理器出错的信号*/ * 协处理器出错中断C处理函数。 /* 清协处理器出错标誌状态 */ /* 向正使用协处理器的进程发送协处理器出错信号 */

公司的门户网站需要实现文件上傳下载功能
在实现下载功能后,测试txt文件下载成功。但是doc和图片等文件下载后出现内容缺失问题。

问题已解决原因是配置中对资源的访问只有TXT类型做了配置,其他类型文件的下载请求都被登录界面拦截了多谢回答

我要回帖

更多关于 end up 的文章

 

随机推荐