Skip to content

Commit

Permalink
本次注释的文件为:
Browse files Browse the repository at this point in the history
    init/main.c
    kernel/Makefile
上面两个文件都已全部完成。
  • Loading branch information
karottc committed Jun 10, 2014
1 parent 62cb95b commit 88167fe
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 14 deletions.
65 changes: 54 additions & 11 deletions init/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ void main(void) /* This really IS void, no error here. */
for(;;) pause();
}

// 下面函数产生格式化信息并输出到标准输出设备stdout(1),这里是指屏幕上显示。参数'*fmt'
// 指定输出将采用的格式,具体可以看标准C语言书籍。该子程序正好是vsprintf如何使用一个
// 简单例子。该程序使用vsprintf()将格式化的字符串放入printfbuf缓冲区,然后用write()将
// 缓冲区的内容输出到标准设备(1--stdout).vsprintf()函数实现在kernel/vsprintf.c中。
static int printf(const char *fmt, ...)
{
va_list args;
Expand All @@ -213,41 +217,77 @@ static int printf(const char *fmt, ...)
return i;
}

static char * argv_rc[] = { "/bin/sh", NULL };
static char * envp_rc[] = { "HOME=/", NULL };
// 读取并执行/etc/rc文件时所使用的命令行参数和环境参数
static char * argv_rc[] = { "/bin/sh", NULL }; // 调用执行程序时参数字符串数组
static char * envp_rc[] = { "HOME=/", NULL }; // 调用执行程序时环境字符串数组

// 运行登录shell时所使用的命令行参数和环境参数
// 下面 argv[0]中的字符“-”是传递给shell程序sh的一个标志。通过识别该标志,
// sh程序会作为登录shell执行。其执行过程与在shell提示符下执行sh不一样。
static char * argv[] = { "-/bin/sh",NULL };
static char * envp[] = { "HOME=/usr/root", NULL };

// 在main()中已经进行了系统初始化,包括内存管理、各种硬件设备和驱动程序。init()函数
// 运行在任务0第1次创建的子进程(任务1)中。它首先对第一个将要执行的程序(shell)的环境
// 进行初始化,然后以登录shell方式加载该程序并执行。
void init(void)
{
int pid,i;

setup((void *) &drive_info);
// setup()是一个系统调用。用于读取硬盘参数包括分区表信息并加载虚拟盘(若存在的话)
// 和安装根文件系统设备。该函数用25行上的宏定义,对应函数是sys_setup(),在块设备
// 子目录kernel/blk_drv/hd.c中。
setup((void *) &drive_info); // drive_info结构是2个硬盘参数表
// 下面以读写访问方式打开设备"/dev/tty0",它对应终端控制台。由于这是第一次打开文件
// 操作,因此产生的文件句柄号(文件描述符)肯定是0。该句柄是UNIX类操作系统默认的
// 控制台标准输入句柄stdin。这里再把它以读和写的方式别人打开是为了复制产生标准输出(写)
// 句柄stdout和标准出错输出句柄stderr。函数前面的"(void)"前缀用于表示强制函数无需返回值。
(void) open("/dev/tty0",O_RDWR,0);
(void) dup(0);
(void) dup(0);
(void) dup(0); // 复制句柄,产生句柄1号——stdout标准输出设备
(void) dup(0); // 复制句柄,产生句柄2号——stderr标准出错输出设备
// 打印缓冲区块数和总字节数,每块1024字节,以及主内存区空闲内存字节数
printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
NR_BUFFERS*BLOCK_SIZE);
printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);
// 下面fork()用于创建一个子进程(任务2)。对于被创建的子进程,fork()将返回0值,对于
// 原进程(父进程)则返回子进程的进程号pid。该子进程关闭了句柄0(stdin)、以只读方式打开
// /etc/rc文件,并使用execve()函数将进程自身替换成/bin/sh程序(即shell程序),然后
// 执行/bin/sh程序。然后执行/bin/sh程序。所携带的参数和环境变量分别由argv_rc和envp_rc
// 数组给出。关闭句柄0并立即打开/etc/rc文件的作用是把标准输入stdin重定向到/etc/rc文件。
// 这样shell程序/bin/sh就可以运行rc文件中的命令。由于这里的sh的运行方式是非交互的,
// 因此在执行完rc命令后就会立刻退出,进程2也随之结束。
// _exit()退出时出错码1 - 操作未许可;2 - 文件或目录不存在。
if (!(pid=fork())) {
close(0);
if (open("/etc/rc",O_RDONLY,0))
_exit(1);
execve("/bin/sh",argv_rc,envp_rc);
_exit(2);
_exit(1); // 如果打开文件失败,则退出(lib/_exit.c)
execve("/bin/sh",argv_rc,envp_rc); // 替换成/bin/sh程序并执行
_exit(2); // 若execve()执行失败则退出。
}
// 下面还是父进程(1)执行语句。wait()等待子进程停止或终止,返回值应是子进程的进程号(pid).
// 这三句的作用是父进程等待子进程的结束。&i是存放返回状态信息的位置。如果wait()返回值
// 不等于子进程号,则继续等待。
if (pid>0)
while (pid != wait(&i))
/* nothing */;
// 如果执行到这里,说明刚创建的子进程的执行已停止或终止了。下面循环中首先再创建
// 一个子进程,如果出错,则显示“初始化程序创建子进程失败”信息并继续执行。对于所
// 创建的子进程将关闭所有以前还遗留的句柄(stdin, stdout, stderr),新创建一个会话
// 并设置进程组号,然后重新打开/dev/tty0作为stdin,并复制成stdout和sdterr.再次
// 执行系统解释程序/bin/sh。但这次执行所选用的参数和环境数组另选了一套。然后父
// 进程再次运行wait()等待。如果子进程又停止了执行,则在标准输出上显示出错信息
// “子进程pid挺直了运行,返回码是i”,然后继续重试下去....,形成一个“大”循环。
// 此外,wait()的另外一个功能是处理孤儿进程。如果一个进程的父进程先终止了,那么
// 这个进程的父进程就会被设置为这里的init进程(进程1),并由init进程负责释放一个
// 已终止进程的任务数据结构等资源。
while (1) {
if ((pid=fork())<0) {
printf("Fork failed in init\r\n");
continue;
}
if (!pid) {
if (!pid) { // 新的子进程
close(0);close(1);close(2);
setsid();
setsid(); // 创建一新的会话期
(void) open("/dev/tty0",O_RDWR,0);
(void) dup(0);
(void) dup(0);
Expand All @@ -257,7 +297,10 @@ void init(void)
if (pid == wait(&i))
break;
printf("\n\rchild %d died with code %04x\n\r",pid,i);
sync();
sync(); // 同步操作,刷新缓冲区。
}
// _exit()和exit()都用于正常终止一个函数。但_exit()直接是一个sys_exit系统调用,
// 而exit()则通常是普通函数库中的一个函数。它会先执行一些清除操作,例如调用
// 执行各终止处理程序、关闭所有标准IO等,然后调用sys_exit。
_exit(0); /* NOTE! _exit, not exit() */
}
35 changes: 32 additions & 3 deletions kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,33 @@
# unless it's something special (ie not a .c file).
#

# GNU的二进制文件处理程序,用于创建、修改以及从归档文件中抽取文件
AR =ar
AS =as
LD =ld
AS =as # GNU的汇编程序
LD =ld # GNU的链接程序
# 链接程序所有的参数,-s 输出文件中省略所有符号信息。-x 删除所有局部符号。
LDFLAGS =-s -x
CC =gcc -mcpu=i386
CC =gcc -mcpu=i386 # GNU C语言编译器
# C 编译程序选项。-Wall 显示所有的警告信息:-0 优化选项,优化代码长度和执行时间;
# -fstrength-reduce 优化循环代码,排除重复变量;-fomit-frame-pointer省略保存不
# 必要的框架指针;-fcombine-regs合并寄存器,减少寄存器类的使用;-finline-function
# 将所有简单短小的函数代码嵌入调用程序中;-mstring-insns Linus自己添加的优化选项,
# 以后不再使用;-nostdinc -L../include不使用默认路径中的包含文件,而使用这里指定
# 目录中的。
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer \
-finline-functions -nostdinc -I../include
# C前处理选项。-E只运行C前处理,对所有指定的C程序进行预处理并将处理结果输出到标准
# 输出设备或指定的输出文件中.
CPP =gcc -E -nostdinc -I../include

# 下面的规则指示make利用下面的命令将所有的.c文件编译生成.s汇编程序。该规则的命令
# 指使gcc采用CFLAGS所指定的选项对C代码编译后不进行汇编就停止(-S),从而产生与输入的
# 各个C文件对应的汇编代码文件。默认情况下所产生的汇编程序文件名是原C文件名去掉.c而
# 加上.s的后缀。-o表示其后是输出文件的名称。其中$*.s(或$@)是自动目标变量,$<代表第
# 一个先决条件,这里即是符合条件*.c的文件。
# 下面这3个不同规则分别用于不同的操作要求。若目标是.s文件,而源文件是.c文件则会使
# 用第一个规则;若目录是.o,而原文件是.s,则使用第2个规则;若目录是.o文件而原文件
# 是c文件,则可直接使用第3个规则。
.c.s:
$(CC) $(CFLAGS) \
-S -o $*.s $<
Expand All @@ -28,17 +46,28 @@ OBJS = sched.o system_call.o traps.o asm.o fork.o \
panic.o printk.o vsprintf.o sys.o exit.o \
signal.o mktime.o

# 在有了先决条件OBJS后使用下面的命令连接成目标kernel.o
# 选项'-r' 用于指示生成可重定位的输出,即产生可以作为链接器ld输入的目标文件。
kernel.o: $(OBJS)
$(LD) -r -o kernel.o $(OBJS)
sync

# 下面规则用于清理工作。当执行'make clean'时,就会执行上面的命令,去除所有编译
# 链接生成的文件。'rm'是文件删除命令,选项-f含义是忽略不存在的文件并且不显示删除信息。
clean:
rm -f core *.o *.a tmp_make keyboard.s
for i in *.c;do rm -f `basename $$i .c`.s;done
(cd chr_drv; make clean)
(cd blk_drv; make clean)
(cd math; make clean)

# 下面的目标或规则用于检查各文件之间的依赖关系。方法如下:
# 使用字符串编辑程序sed对makefile文件(这里即是自己)进行处理,输出为删除Makefile
# 文件中'### Dependencies'行后面的所有航,并生成tmp_make临时文件。然后对kernel/目录下
# 的每一个C文件执行gcc预处理操作,-M标志告诉预处理程序输出描述符每个目标文件相关性的
# 规则,并且这些规则符合make语法。对于每一个源文件,预处理程序输出一个make规则,其结果
# 形式是相应源程序文件的目标文件名加上其依赖关系——该源文件中包含的所有头文件列表。把
# 预处理结果都添加到临时文件tmp_make中,然后将该临时文件复制成新的Makefile文件。
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
Expand Down

0 comments on commit 88167fe

Please sign in to comment.