8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
概述
上一篇涉及到了main函数在fork任务1之前的一些行为,由于任务1执行的init函数涉及内容较多,因此将其内容移动
到本篇来继续分析。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66void (void)
{
int pid,i;
// 该函数是在25 行上的宏定义的,对应函数是sys_setup(),在kernel/blk_drv/hd.c。
setup((void *) &drive_info);
(void) open("/dev/tty0",O_RDWR,0);// 用读写访问方式打开设备“/dev/tty0”,
// 这里对应终端控制台。
// 返回的句柄号0 -- stdin 标准输入设备。
(void) dup(0);// 复制句柄,产生句柄1 号-- stdout 标准输出设备。
(void) dup(0);// 复制句柄,产生句柄2 号-- stderr 标准出错输出设备。
printf("%d buffers = %d bytes buffer spacenr",NR_BUFFERS,
NR_BUFFERS*BLOCK_SIZE);// 打印缓冲区块数和总字节数,每块1024 字节。
printf("Free mem: %d bytesnr",memory_end-main_memory_start);//空闲内存字节数。
// 下面fork()用于创建一个子进程(子任务)。对于被创建的子进程,fork()将返回0 值,
// 对于原(父进程)将返回子进程的进程号。所以if (!(pid=fork())) {...} 内是子进程执行的内容。
// 该子进程关闭了句柄0(stdin),以只读方式打开/etc/rc 文件,并执行/bin/sh 程序,所带参数和
// 环境变量分别由argv_rc 和envp_rc 数组给出。参见后面的描述。
if (!(pid=fork())) {
close(0);
if (open("/etc/rc",O_RDONLY,0))
_exit(1);// 如果打开文件失败,则退出(/lib/_exit.c)。
execve("/bin/sh",argv_rc,envp_rc);// 装入/bin/sh 程序并执行。(/lib/execve.c)
_exit(2);// 若execve()执行失败则退出(出错码2,“文件或目录不存在”)。
}
// 下面是父进程执行的语句。wait()是等待子进程停止或终止,其返回值应是子进程的
// 进程号(pid)。这三句的作用是父进程等待子进程的结束。&i 是存放返回状态信息的
// 位置。如果wait()返回值不等于子进程号,则继续等待。
if (pid>0)
while (pid != wait(&i))
{/* nothing */;}
// --
// 如果执行到这里,说明刚创建的子进程的执行已停止或终止了。下面循环中首先再创建
// 一个子进程,如果出错,则显示“初始化程序创建子进程失败”的信息并继续执行。对
// 于所创建的子进程关闭所有以前还遗留的句柄(stdin, stdout, stderr),新创建一个
// 会话并设置进程组号,然后重新打开/dev/tty0 作为stdin,并复制成stdout 和stderr。
// 再次执行系统解释程序/bin/sh。但这次执行所选用的参数和环境数组另选了一套(见上面)。
// 然后父进程再次运行wait()等待。如果子进程又停止了执行,则在标准输出上显示出错信息
//“子进程pid 停止了运行,返回码是i”,
// 然后继续重试下去…,形成“大”死循环。
while (1) {
if ((pid=fork())<0) {
printf("Fork failed in initrn");
continue;
}
if (!pid) {
close(0);close(1);close(2);
setsid();
(void) open("/dev/tty0",O_RDWR,0);
(void) dup(0);
(void) dup(0);
_exit(execve("/bin/sh",argv,envp));
}
while (1)
if (pid == wait(&i))
break;
printf("nrchild %d died with code %04xnr",pid,i);
sync();
}
_exit(0);/* NOTE! _exit, not exit() */
}
setup
该函数对应函数是sys_setup(),在kernel/blk_drv/hd.c。代码不在这里贴了。讲一下这个函数的内容:在全局变量hd数组中设置每个硬盘的起始扇区号和总的扇区数。
读取第一个硬盘的第一个扇区,获取其中的分区表信息。
rd_load函数执行。加载(创建)RAMDISK(kernel/blk_drv/ramdisk.c,71)。
mount_root函数执行。// 安装根文件系统(fs/super.c,242)。
mount_root
由于对Linux中文件系统的挂载有一个深入的接触,这里来进一步分析一下mount_root函数的调用过程吧。
在分析函数之前,先来明确一下Linux中文件系统的一些概念:super_block。超级块。超级块用来表示一个文件系统,其内容主要包括了该文件系统的inode数目,逻辑块数,
inode位图所占用的数据块数,逻辑块位图所占用的的数据块数,第一个数据块等等。那么什么优势inode呢?
inode。一般我们就称之为inode,不翻译。 一个inode通常来说代表了一个文件,当然也可以代表一个目录。其主要
内容包括文件的类型和属性,用户id,文件大小,修改时间,指向该inode的链接数以及最重要的,文件的数据块标志数组,
包括7个直接,1个间接和两个双重间接。
对于上述两个数据结构,在硬盘中必然都有弃数据结构,同时,其在内存中的数据结构相比有硬盘数据结构都有了扩充。
因此Linux0.11中,分别定义了d_inode,m_inode,d_super_block和super_block四种数据结构来分别表示。
对于目录,我们还需要了解数据结构dir_entry。该数据结构表示了一个目录。
以上数据结构的定义位于Linux0.11源代码的include/linux/fs.h文件中。
总结,Linux文件系统中,我们将位于磁盘上的文件系统超级块数据读取到内存中并表示为super_block。我们通过super block
中的inode位图可以知道哪些inode还可以使用,哪些逻辑块还可以使用,这样我们在新建文件的时候就可以通过查询super block
来找到可用的inode和可用的数据块,并修改super block中的位图信息。并在合适的时候将超级块的修改信息回写到硬盘中
接下来我们来列举mount_root函数所做的事情初始化file_table全局数组。该数组大小为64,说明Linux0.11同时只能打开64个文件。
初始化超级块数组。超级块数组大小为8,说明Linux0.11最多只能挂载8个文件系统。
从根设备上读取超级块信息。
读取根设备的第一个inode到内存中。
设置该inode为mount点和根目录挂载点。
设置当前进程的工作目录和根节点为第一个inode。
进行一些文件系统数据的计算,并打印到控制台输出。
open
系统调用open将/dev/tty0打开设置为标准输入0,使用dup系统调用将其赋值为
标准输出1,和标准输出2。
fork及子程序执行
init函数在调用fork之后,在任务1中使用wait系统调用进行等待,在子任务中执行以下操作:关闭标准输入0.
以只读方式打开/etc/rc.
使用execve系统调用执行/bin/sh程序。
最后一步
init进程执行到这里的时候,就进入了循环,这就是Linux系统的常态,打开一个/bin/sh程序,开始执行,
当执行的sh程序推出之后,同步文件系统,再打开一个shell程序,如此往复。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19while (1) {
if ((pid=fork())<0) {
printf("Fork failed in initrn");
continue;
}
if (!pid) {
close(0);close(1);close(2);
setsid();
(void) open("/dev/tty0",O_RDWR,0);
(void) dup(0);
(void) dup(0);
_exit(execve("/bin/sh",argv,envp));
}
while (1)
if (pid == wait(&i))
break;
printf("nrchild %d died with code %04xnr",pid,i);
sync();
}
总结
本文对init进程的行为进行了一个较为深入的分析,但是还存在以下一些系统调用需要进行进一步的分析:fork
execve
open
close
这些系统调用中后续需要对fork和execve进行分析。