LINUXinitWord文档下载推荐.docx
《LINUXinitWord文档下载推荐.docx》由会员分享,可在线阅读,更多相关《LINUXinitWord文档下载推荐.docx(11页珍藏版)》请在冰豆网上搜索。
处理的方法是在fork()调用之后不让main()使
用
*任何堆栈。
因此就不能有函数调用-这意味着fork也要使用内嵌的代码,否则我们在从fork()退出
*时就要使用堆栈了。
*实际上只有pause和fork需要使用内嵌方式,以保证从main()中不会弄乱堆栈,但是我们同时还
*定义了其它一些函数。
*/
23staticinline_syscall0(int,fork)//是unistd.h中的内嵌宏代码。
以嵌入汇编的形式调用
//Linux的系统调用中断0x80。
该中断是所有系统调用的
//入口。
该条语句实际上是intfork()创建进程系统调用。
//syscall0名称中最后的0表示无参数,1表示1个参数。
24staticinline_syscall0(int,pause)//intpause()系统调用:
暂停进程的执行,直到
//收到一个信号。
25staticinline_syscall1(int,setup,void*,BIOS)//intsetup(void*BIOS)系统调用,仅用于
//linux初始化(仅在这个程序中被调用)。
26staticinline_syscall0(int,sync)//intsync()系统调用:
更新文件系统。
27
28#include<
linux/tty.h>
//tty头文件,定义了有关tty_io,串行通信方面的参数、常数。
29#include<
linux/sched.h>
//调度程序头文件,定义了任务结构task_struct、第1个初始任务
//的数据。
还有一些以宏的形式定义的有关描述符参数设置和获取的
//嵌入式汇编函数程序。
30#include<
linux/head.h>
//head头文件,定义了段描述符的简单结构,和几个选择符常量。
31#include<
asm/system.h>
//系统头文件。
以宏的形式定义了许多有关设置或修改
//描述符/中断门等的嵌入式汇编子程序。
32#include<
asm/io.h>
//io头文件。
以宏的嵌入汇编程序形式定义对io端口操作的函数。
33
34#include<
stddef.h>
//标准定义头文件。
定义了NULL,offsetof(TYPE,MEMBER)。
35#include<
stdarg.h>
//标准参数头文件。
以宏的形式定义变量参数列表。
主要说明了-个
//类型(va_list)和三个宏(va_start,va_arg和va_end),vsprintf、
//vprintf、vfprintf。
36#include<
37#include<
fcntl.h>
//文件控制头文件。
用于文件及其描述符的操作控制常数符号的定义。
38#include<
sys/types.h>
//类型头文件。
定义了基本的系统数据类型。
39
40#include<
linux/fs.h>
//文件系统头文件。
定义文件表结构(file,buffer_head,m_inode等)。
41
42staticcharprintbuf[1024];
//静态字符串数组。
43
44externintvsprintf();
//送格式化输出到一字符串中(在kernel/vsprintf.c,92行)。
45externvoidinit(void);
//函数原形,初始化(在168行)。
46externvoidblk_dev_init(void);
//块设备初始化子程序(kernel/blk_drv/ll_rw_blk.c,157行)
47externvoidchr_dev_init(void);
//字符设备初始化(kernel/chr_drv/tty_io.c,347行)
48externvoidhd_init(void);
//硬盘初始化程序(kernel/blk_drv/hd.c,343行)
49externvoidfloppy_init(void);
//软驱初始化程序(kernel/blk_drv/floppy.c,457行)
50externvoidmem_init(longstart,longend);
//内存管理初始化(mm/memory.c,399行)
51externlongrd_init(longmem_start,intlength);
//虚拟盘初始化(kernel/blk_drv/ramdisk.c,52)
52externlongkernel_mktime(structtm*tm);
//建立内核时间(秒)。
53externlongstartup_time;
//内核启动时间(开机时间)(秒)。
54
55/*
56*Thisissetupbythesetup-routineatboot-time
57*/
*以下这些数据是由setup.s程序在引导时间设置的(参见第2章2.3.1节中的表2.1)。
58#defineEXT_MEM_K(*(unsignedshort*)0x90002)//1M以后的扩展内存大小(KB)。
59#defineDRIVE_INFO(*(structdrive_info*)0x90080)//硬盘参数表基址。
60#defineORIG_ROOT_DEV(*(unsignedshort*)0x901FC)//根文件系统所在设备号。
61
62/*
63*Yeah,yeah,it'
sugly,butIcannotfindhowtodothiscorrectly
64*andthisseemstowork.Ianybodyhasmoreinfoonthereal-time
65*clockI'
dbeinterested.Mostofthiswastrialanderror,andsome
66*bios-listingreading.Urghh.
67*/
*是啊,是啊,下面这段程序很差劲,但我不知道如何正确地实现,而且好象它还能运行。
如果有
*关于实时时钟更多的资料,那我很感兴趣。
这些都是试探出来的,以及看了一些bios程序,呵!
68
69#defineCMOS_READ(addr)({\//这段宏读取CMOS实时时钟信息。
70outb_p(0x80|addr,0x70);
\//0x70是写端口号,0x80|addr是要读取的CMOS内存地址。
71inb_p(0x71);
\//0x71是读端口号。
72})
73
74#defineBCD_TO_BIN(val)((val)=((val)&
15)+((val)>
>
4)*10)//将BCD码转换成数字。
75
76staticvoidtime_init(void)//该子程序取CMOS时钟,并设置开机时间
startup_time(秒)。
77{
78structtmtime;
79
80do{
81time.tm_sec=CMOS_READ(0);
//参见后面CMOS内存列表。
82time.tm_min=CMOS_READ
(2);
83time.tm_hour=CMOS_READ(4);
84time.tm_mday=CMOS_READ(7);
85time.tm_mon=CMOS_READ(8);
86time.tm_year=CMOS_READ(9);
87}while(time.tm_sec!
=CMOS_READ(0));
88BCD_TO_BIN(time.tm_sec);
89BCD_TO_BIN(time.tm_min);
90BCD_TO_BIN(time.tm_hour);
91BCD_TO_BIN(time.tm_mday);
92BCD_TO_BIN(time.tm_mon);
93BCD_TO_BIN(time.tm_year);
94time.tm_mon--;
95startup_time=kernel_mktime(&
time);
96}
97
98staticlongmemory_end=0;
//机器具有的内存(字节数)。
99staticlongbuffer_memory_end=0;
//高速缓冲区末端地址。
100staticlongmain_memory_start=0;
//主内存(将用于分页)开始的位置。
101
102structdrive_info{chardummy[32];
}drive_info;
//用于存放硬盘参数表信息。
103
104voidmain(void)/*ThisreallyISvoid,noerrorhere.*/
105{/*Thestartuproutineassumes(well,...)this*/
/*这里确实是void,并没错。
在startup程序(head.s)中就是这样假设的。
//参见head.s程序第136行开始的几行代码。
106/*
107*Interruptsarestilldisabled.Donecessarysetups,then
108*enablethem
109*/
*此时中断仍被禁止着,做完必要的设置后就将其开启。
//下面这段代码用于保存:
//根设备号
ROOT_DEV;
高速缓存末端地址
buffer_memory_end;
//机器内存数
memory_end;
主内存开始地址
main_memory_start;
110ROOT_DEV=ORIG_ROOT_DEV;
111drive_info=DRIVE_INFO;
112memory_end=(1<
<
20)+(EXT_MEM_K<
10);
//内存大小=1Mb字节+扩展内存(k)*1024字节。
113memory_end&
=0xfffff000;
//忽略不到4Kb(1页)的内存数。
114if(memory_end>
16*1024*1024)//如果内存超过16Mb,则按16Mb计。
115memory_end=16*1024*1024;
116if(memory_end>
12*1024*1024)//如果内存>
12Mb,则设置缓冲区末端=4Mb
117buffer_memory_end=4*1024*1024;
118elseif(memory_end>
6*1024*1024)//否则如果内存>
6Mb,则设置缓冲区末端=2Mb
119buffer_memory_end=2*1024*1024;
120else
121buffer_memory_end=1*1024*1024;
//否则则设置缓冲区末端=1Mb
122main_memory_start=buffer_memory_end;
//主内存起始位置=缓冲区末端;
123#ifdefRAMDISK//如果定义了虚拟盘,则主内存将减少。
124main_memory_start+=rd_init(main_memory_start,RAMDISK*1024);
125#endif
//以下是内核进行所有方面的初始化工作。
阅读时最好跟着调用的程序深入进去看,实在看
//不下去了,就先放一放,看下一个初始化调用--这是经验之谈☺。
126mem_init(main_memory_start,memory_end);
127trap_init();
//陷阱门(硬件中断向量)初始化。
(kernel/traps.c,181行)
128blk_dev_init();
//块设备初始化。
(kernel/blk_dev/ll_rw_blk.c,157行)
129chr_dev_init();
//字符设备初始化。
(kernel/chr_dev/tty_io.c,347行)
130tty_init();
//tty初始化。
(kernel/chr_dev/tty_io.c,105行)
131time_init();
//设置开机启动时间
startup_time(见76行)。
132sched_init();
//调度程序初始化(加载了任务0的tr,ldtr)(kernel/sched.c,385)
133buffer_init(buffer_memory_end);
//内存管理初始化,建内存链表等。
(fs/buffer.c,348)
134hd_init();
//硬盘初始化。
(kernel/blk_dev/hd.c,343行)
135floppy_init();
//软驱初始化。
(kernel/blk_dev/floppy.c,457行)
136sti();
//所有初始化工作都做完了,开启中断。
137move_to_user_mode();
//移到用户模式。
(include/asm/system.h,第1行)
138if(!
fork()){/*wecountonthisgoingok*/
139init();
140}
141/*
142*NOTE!
Foranyothertask'
pause()'
wouldmeanwehavetogeta
143*signaltoawaken,buttask0isthesoleexception(see'
schedule()'
)
144*astask0getsactivatedateveryidlemoment(whennoothertasks
145*canrun).Fortask0'
justmeanswegocheckifsomeother
146*taskcanrun,andifnotwereturnhere.
147*/
/*注意!
对于任何其它的任务,'
将意味着我们必须等待收到一个信号才会返
*回就绪运行态,但任务0(task0)是唯一的意外情况(参见'
),因为任务0在
*任何空闲时间里都会被激活(当没有其它任务在运行时),因此对于任务0'
仅意味着
*我们返回来查看是否有其它任务可以运行,如果没有的话我们就回到这里,一直循环执行'
。
148for(;
;
)pause();
149}
150
151staticintprintf(constchar*fmt,...)
//产生格式化信息并输出到标准输出设备stdout
(1),这里是指屏幕上显示。
参数'
*fmt'
指定输出将
//采用的格式,参见各种标准C语言书籍。
该子程序正好是vsprintf如何使用的一个例子。
//该程序使用vsprintf()将格式化的字符串放入printbuf缓冲区,然后用write()将缓冲区的内容
//输出到标准设备(1--stdout)。
152{
153va_listargs;
154inti;
155
156va_start(args,fmt);
157write(1,printbuf,i=vsprintf(printbuf,fmt,args));
158va_end(args);
159returni;
160}
161
162staticchar*argv_rc[]={"
/bin/sh"
NULL};
//调用执行程序时参数的字符串数组。
163staticchar*envp_rc[]={"
HOME=/"
//调用执行程序时的环境字符串数组。
164
165staticchar*argv[]={"
-/bin/sh"
NULL};
//同上。
166staticchar*envp[]={"
HOME=/usr/root"
167
168voidinit(void)
169{
170intpid,i;
171
172setup((void*)&
drive_info);
//读取硬盘参数包括分区表信息并建立虚拟盘和
//安装根文件系统设备。
(kernel/blk_drv/hd.c,71)
173(void)open("
/dev/tty0"
O_RDWR,0);
//用读写访问方式打开设备“/dev/tty0”,
//这里对应终端控制台。
//返回的句柄号0--stdin标准输入设备。
174(void)dup(0);
//复制句柄,产生句柄1号--stdout标准输出设备。
175(void)dup(0);
//复制句柄,产生句柄2号--stderr标准出错输出设备。
176printf("
%dbuffers=%dbytesbufferspace\n\r"
NR_BUFFERS,
177NR_BUFFERS*BLOCK_SIZE);
//打印缓冲区块数和总字节数,每块1024字节。
178printf("
Freemem:
%dbytes\n\r"
memory_end-main_memory_start);
//空闲内存字节数。
//下面fork()用于创建一个子进程(子任务)。
对于被创建的子进程,fork()将返回0值,
//对于原(父进程)将返回子进程的进程号。
所以180-184句是子进程执行的内容。
该子进程
//关闭了句柄0(stdin),以只读方式打开/etc/rc文件,并执行/bin/sh程序,所带参数和
//环境变量分别由argv_rc和envp_rc数组给出。
参见后面的描述。
179if(!
(pid=fork())){
180close(0);
181if(open("
/etc/rc"
O_RDONLY,0))
182_exit
(1);
//如果打开文件失败,则退出(/lib/_exit.c,10)。
183execve("
argv_rc,envp_rc);
//装入/bin/sh程序并执行。
184_exit
(2);
185}
//下面是父进程执行的语句。
wait()是等待子进程停止或终止,其返回值应是子进程的进程号(pid)。
//这三句的作用是父进程等待子进程的结束。
&
i是存放返回状态信息的位置。
如果wait()返回值不
//等于子进程号,则继续等待。
186if(pid>
0)
187while(pid!
=wait(&
i))
188/*nothing*/;
//如果执行到这里,说明刚创建的子进程的执行已停止或终止了。
下面循环中首先再创建一个子进程,
//如果出错,则显示“初始化程序创建子进程失败”的信息并继续执行。
对于所创建的子进程关闭所
有
//以前还遗留的句柄(stdin,stdout,stderr),新创建一个会话并设置进程组号,然后重新打开
///dev/tty0作为stdin,并复制成stdout和stderr。
再次执行系统解释程序/bin/sh。
但这次执行所
//选用的参数和环境数组另选了一套(见上面165-167行)。
然后父进程再次运行wait()等待。
如果
//子进程又停止了执行,则在标准输出上显示出错信息“子进程pid停止了运行,返回码是i”,然后
//继续重试下去…,形成“大”死循环。
189while
(1){
190if((pid=fork())<
0){
191printf("
Forkfailedininit\r\n"
);
192continue;