1.之所以选择这么低的版本学习,答案是简单,高版本的代码量太大,对于我这样的初学者来说,就是瞎子摸象不会有什么感觉。开始吧!
2首先你需要在一个地方下载源码:OldLinux
3.分析:
1 /* 2 * linux/init/main.c 3 * 4 * (C) 1991 Linus Torvalds 5 */ 6 7 #define __LIBRARY__ //在unistd.h中,使用了#ifndef __LIBRARY__ 8 #include <unistd.h> //包含unitsd.h 9 #include <time.h> // 结构体的定义,包含tm(结构体),时间函数定义 10 11 /* 12 * we need this inline - forking from kernel space will result 13 * in NO COPY ON WRITE (!!!), until an execve is executed. This 14 * is no problem, but for the stack. This is handled by not letting 15 * main() use the stack at all after fork(). Thus, no function 16 * calls - which means inline code for fork too, as otherwise we 17 * would use the stack upon exit from 'fork()'. 18 * 19 * Actually only pause and fork are needed inline, so that there 20 * won't be any messing with the stack from main(), but we define 21 * some others too. 22 */ 23 /*--翻译--*/ //这个地方与boot目录下的bootsect.s,setup.s,head.s有关 24 /* 25 * 我们需要下面这些内嵌语句 - 从内核空间创建进程(forking)将导致没有 26 * 写时复制(COPY ON WRITE)!!! 直到一个执行execve 调用。这对堆栈可 27 * 能带来问题。处理的方法是在fork()调用之后不让main()使用任何堆栈。 28 * 因此就不能有函数调用 - 这意味着fork 也要使用内嵌的代码,否则我 29 * 们在从fork()退出时就要使用堆栈了。实际上只有pause 和fork 需要使用 30 * 内嵌方式,以保证从main()中不会弄乱堆栈,但是我们同时还定义了其它 31 * 一些函数 32 */ 33 static inline _syscall0(int,fork) 34 static inline _syscall0(int,pause) 35 static inline _syscall1(int,setup,void *,BIOS) 36 static inline _syscall0(int,sync) //系统调用 37 38 #include <linux/tty.h> //tty头文件,定义了有关tty_io,串行通信方面的参数,常数 39 //所谓“串行通信”是指外设和计算机间使用一根数据信号线 40 //数据在一根数据信号线上按位进行传输,每一位都占据一个固定的时间长度 41 #include <linux/sched.h> //调度程序头文件,定义了任务结构和task_struct,第1一个初始任务的数据 42 //还有一些以宏的形式定义的有关描述参数设置和获取的嵌入式汇编函数程序 43 #include <linux/head.h> //head头文件,定义了段描述的简单结构,和集合选择符常量 44 #include <asm/system.h> //系统头文件,以宏的形式定义了许多有关设置或修改 45 //描述符/中断门等的嵌入式汇编子程序 46 #include <asm/io.h>//io头文件,以宏的嵌入汇编程序形式定义对io端口操作的函数 47 48 #include <stddef.h>//标准定义头文件,定义了NULL,offsetof(TYPE,MEMBER) 49 #include <stdarg.h>//标准参数头文件,以宏的形式定义变量参数列表,主要说明一个类型(va_list) 50 // 和三个宏(va_list,va_arg和va_end),vsprintf vprintf , vfprintf 51 #include <unistd.h> 52 #include <fcntl.h> //文件控制头文件,用于文件及其描述符的操作控制常数符号的定义 53 #include <sys/types.h> //类型头文件,定义了基本的系统数据类型 54 55 #include <linux/fs.h> //文件系统头文件,定义文件表结构(file,buffer_head,m_inode 等) 56 57 static char printbuf[1024]; 58 59 extern int vsprintf(); 60 extern void init(void); 61 extern void blk_dev_init(void); //块设备初始化 62 extern void chr_dev_init(void); //字符设备初始化 63 extern void hd_init(void); //硬盘初始化程序 64 extern void floppy_init(void); //软盘初始化程序 65 extern void mem_init(long start, long end); //内存管理程序初始化 66 extern long rd_init(long mem_start, int length); //虚拟盘初始化 67 extern long kernel_mktime(struct tm * tm); //建立内核时间 68 extern long startup_time; //内核启动时间(开机时间)(秒) 69 70 /* 71 * This is set up by the setup-routine at boot-time 72 //一下数据是由setup.s程序在引导时间设置的 73 */ 74 #define EXT_MEM_K (*(unsigned short *)0x90002) //1m以后的拓展内存大小 75 #define DRIVE_INFO (*(struct drive_info *)0x90080) //硬盘参数表基地址 76 #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) //根文件系统所在设备号 77 78 /* 79 * Yeah, yeah, it's ugly, but I cannot find how to do this correctly 80 * and this seems to work. I anybody has more info on the real-time 81 * clock I'd be interested. Most of this was trial and error, and some 82 * bios-listing reading. Urghh. 83 */ 84 85 #define CMOS_READ(addr) ({ //这段宏读取cmos实时时钟信息 86 outb_p(0x80|addr,0x70); //0x70是些端口号,0x80|addr是要读取CMOS内存地址 87 inb_p(0x71); //0x71是读端口号 88 }) 89 90 #define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) //将BSD码转换数字 91 92 static void time_init(void) //读取cmos中的信息,初始化全局变量startup_time 93 { 94 struct tm time; 95 96 do { 97 time.tm_sec = CMOS_READ(0); 98 time.tm_min = CMOS_READ(2); 99 time.tm_hour = CMOS_READ(4); 100 time.tm_mday = CMOS_READ(7); 101 time.tm_mon = CMOS_READ(8); 102 time.tm_year = CMOS_READ(9); 103 } while (time.tm_sec != CMOS_READ(0)); 104 BCD_TO_BIN(time.tm_sec); 105 BCD_TO_BIN(time.tm_min); 106 BCD_TO_BIN(time.tm_hour); 107 BCD_TO_BIN(time.tm_mday); 108 BCD_TO_BIN(time.tm_mon); 109 BCD_TO_BIN(time.tm_year); 110 time.tm_mon--; //months since january - [0,11] 111 startup_time = kernel_mktime(&time); 112 } 113 114 static long memory_end = 0; // 机器具有的内存(字节数) 115 static long buffer_memory_end = 0; //高速缓存末端地址 116 static long main_memory_start = 0; //主内存(将用于分页)开始的位置 117 118 struct drive_info { char dummy[32]; } drive_info; //用于存放硬盘信息 119 120 void main(void) /* This really IS void, no error here. */ 121 { /* The startup routine assumes (well, ...) this */ 122 //此时中断仍然是关着,在必要的设置完成之后 123 //打开中断 124 /* 125 * Interrupts are still disabled. Do necessary setups, then 126 * enable them 127 */ 128 // 下面这段代码用于保存 129 // 根设备号 -- ROOT_DEV; 高速缓存末端地址 -- buffer_memory_end 130 // 机器内存数 -- memory_end;主内存开始地址 -- main_memory_start 131 ROOT_DEV = ORIG_ROOT_DEV; 132 drive_info = DRIVE_INFO; 133 memory_end = (1<<20) + (EXT_MEM_K<<10); //内存大小=1Mb字节+扩展内存(k) * 1024字节 134 memory_end &= 0xfffff000; //忽略不到4kb(1页)的内存数 135 if (memory_end > 16*1024*1024) //如果内存超过16Mb,则16Mb计 136 memory_end = 16*1024*1024; 137 if (memory_end > 12*1024*1024) //如果内存>12Mb,则设置缓冲末端4Mb 138 buffer_memory_end = 4*1024*1024; 139 else if (memory_end > 6*1024*1024) //否则如果内存 > 6Mb,则设置缓冲末端=2Mb 140 buffer_memory_end = 2*1024*1024; 141 else //否则设置缓冲区末端=1Mb 142 buffer_memory_end = 1*1024*1024; 143 main_memory_start = buffer_memory_end; //主内存(用于分页使用)起始位置=缓冲区末端 144 #ifdef RAMDISK 145 main_memory_start += rd_init(main_memory_start, RAMDISK*1024); 146 #endif 147 mem_init(main_memory_start,memory_end); 148 trap_init(); //陷阱门(硬件中断向量)初始化 149 blk_dev_init(); //块设备初始化 150 chr_dev_init(); //字符设备初始化 151 tty_init();//tty初始化 152 time_init();//设置开机启动时间,startup_time 153 sched_init(); //调度程序初始化 154 buffer_init(buffer_memory_end); //缓冲区初始化,建立内存链表 155 hd_init(); //硬盘初始化 156 floppy_init(); //软盘初始化 157 sti(); //设置完成,开启中断 158 move_to_user_mode(); //移到用户模式 159 if (!fork()) { /* we count on this going ok */ 160 init(); 161 } 162 /* 163 * NOTE!! For any other task 'pause()' would mean we have to get a 164 * signal to awaken, but task0 is the sole exception (see 'schedule()') 165 * as task 0 gets activated at every idle moment (when no other tasks 166 * can run). For task0 'pause()' just means we go check if some other 167 * task can run, and if not we return here. 168 */ 169 /* 170 * 注意!! 对于任何其它的任务,'pause()'将意味着我们必须等待收到一个信号才会返 171 * 回就绪运行态,但任务0(task0)是唯一的意外情况(参见'schedule()'),因为任务0 在 172 * 任何空闲时间里都会被激活(当没有其它任务在运行时),因此对于任务0'pause()'仅意味着 173 * 我们返回来查看是否有其它任务可以运行,如果没有的话我们就回到这里,一直循环执行'pause()'。 174 */ 175 for(;;) pause(); 176 } 177 178 static int printf(const char *fmt, ...)//使用变长参数,调用write系统调用 179 { 180 va_list args; 181 int i; 182 183 va_start(args, fmt); 184 write(1,printbuf,i=vsprintf(printbuf, fmt, args)); 185 va_end(args); 186 return i; 187 } 188 189 static char * argv_rc[] = { "/bin/sh", NULL }; //调用执行程序时参数的字符串数组 190 static char * envp_rc[] = { "HOME=/", NULL };//调用执行程序时的环境字符串数组 191 192 static char * argv[] = { "-/bin/sh",NULL }; 193 static char * envp[] = { "HOME=/usr/root", NULL }; 194 195 void init(void) 196 { 197 int pid,i; 198 199 setup((void *) &drive_info); //读取硬盘信息 200 (void) open("/dev/tty0",O_RDWR,0);//用读写访问方式打开设备"/dev/tty0" 201 (void) dup(0); //复制句柄,产生句柄1号--stdout标准输出设备 202 (void) dup(0); //复制句柄,产生句柄2号--stderr标准出错输出设备 203 //输出一些信息 204 printf("%d buffers = %d bytes buffer space ",NR_BUFFERS, 205 NR_BUFFERS*BLOCK_SIZE); 206 printf("Free mem: %d bytes ",memory_end-main_memory_start); 207 //下面的代码打开/etc/rc,然后执行/bin/sh,但是这里开辟了 208 //两个线程 209 if (!(pid=fork())) { 210 close(0); 211 if (open("/etc/rc",O_RDONLY,0)) 212 _exit(1); 213 execve("/bin/sh",argv_rc,envp_rc); 214 _exit(2); 215 } 216 if (pid>0) 217 while (pid != wait(&i)) 218 /* nothing */; 219 /* 220 * 如果执行到这里,说明刚创建的子进程的执行已停止或终止了。 221 * 下面循环中首先再创建一个子进程.如果出错,则显示“初始化 222 * 程序创建子进程失败”的信息并继续执行。对于所创建的子进 223 * 程关闭所有以前还遗留的句柄(stdin, stdout, stderr),新创 224 * 建一个会话并设置进程组号,然后重新打开/dev/tty0 作为stdin, 225 * 并复制成stdout 和stderr。再次执行系统解释程序/bin/sh。但 226 * 这次执行所选用的参数和环境数组另选了一套。然后父进程再次 227 * 运行wait()等待。如果子进程又停止了执行,则在标准输出上显 228 * 示出错信息“子进程pid 停止了运行,返回码是i”,然后继续重 229 * 试下去…,形成“大”死循环 230 * 231 */ 232 while (1) { 233 if ((pid=fork())<0) { 234 printf("Fork failed in init "); 235 continue; 236 } 237 if (!pid) { 238 close(0);close(1);close(2); 239 setsid(); 240 (void) open("/dev/tty0",O_RDWR,0); 241 (void) dup(0); 242 (void) dup(0); 243 _exit(execve("/bin/sh",argv,envp)); 244 } 245 while (1) 246 if (pid == wait(&i)) 247 break; 248 printf(" child %d died with code %04x ",pid,i); 249 sync(); 250 } 251 _exit(0); /* NOTE! _exit, not exit() */ 252 }
4.参考