linux中main进程,linux-0.11内核main函数执行过程分析——2

66b52468c121889b900d4956032f1009.png

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进行分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * linux/init/main.c * * (C) 1991 Linus Torvalds */ #define __LIBRARY__ #include <unistd.h> #include <time.h> /* * we need this inline - forking from kernel space will result * in NO COPY ON WRITE (!!!), until an execve is executed. This * is no problem, but for the stack. This is handled by not letting * main() use the stack at all after fork(). Thus, no function * calls - which means inline code for fork too, as otherwise we * would use the stack upon exit from 'fork()'. * * Actually only pause and fork are needed inline, so that there * won't be any messing with the stack from main(), but we define * some others too. */ static inline _syscall0(int,fork) static inline _syscall0(int,pause) static inline _syscall1(int,setup,void *,BIOS) static inline _syscall0(int,sync) #include <linux/tty.h> #include <linux/sched.h> #include <linux/head.h> #include <asm/system.h> #include <asm/io.h> #include <stddef.h> #include <stdarg.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <linux/fs.h> static char printbuf[1024]; extern int vsprintf(); extern void init(void); extern void blk_dev_init(void); extern void chr_dev_init(void); extern void hd_init(void); extern void floppy_init(void); extern void mem_init(long start, long end); extern long rd_init(long mem_start, int length); extern long kernel_mktime(struct tm * tm); extern long startup_time; /* * This is set up by the setup-routine at boot-time */ #define EXT_MEM_K (*(unsigned short *)0x90002) #define DRIVE_INFO (*(struct drive_info *)0x90080) #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) /* * Yeah, yeah, it's ugly, but I cannot find how to do this correctly * and this seems to work. I anybody has more info on the real-time * clock I'd be interested. Most of this was trial and error, and some * bios-listing reading. Urghh. */ #define CMOS_READ(addr) ({ \ outb_p(0x80|addr,0x70); \ inb_p(0x71); \ }) #define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) static void time_init(void) { struct tm time; do { time.tm_sec = CMOS_READ(0); time.tm_min = CMOS_READ(2); time.tm_hour = CMOS_READ(4); time.tm_mday = CMOS_READ(7); time.tm_mon = CMOS_READ(8); time.tm_year = CMOS_READ(9); } while (time.tm_sec != CMOS_READ(0)); BCD_TO_BIN(time.tm_sec); BCD_TO_BIN(time.tm_min); BCD_TO_BIN(time.tm_hour); BCD_TO_BIN(time.tm_mday); BCD_TO_BIN(time.tm_mon); BCD_TO_BIN(time.tm_year); time.tm_mon--; startup_time = kernel_mktime(&time); } static long memory_end = 0; static long buffer_memory_end = 0; static long main_memory_start = 0; struct drive_info { char dummy[32]; } drive_info; void main(void) /* This really IS void, no error here. */ { /* The startup routine assumes (well, ...) this */ /* * Interrupts are still disabled. Do necessary setups, then * enable them */ ROOT_DEV = ORIG_ROOT_DEV; drive_info = DRIVE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); memory_end &= 0xfffff000; if (memory_end > 16*1024*1024) memory_end = 16*1024*1024; if (memory_end > 12*1024*1024) buffer_memory_end = 4*1024*1024; else if (memory_end > 6*1024*1024) buffer_memory_end = 2*1024*1024; else buffer_memory_end = 1*1024*1024; main_memory_start = buffer_memory_end; #ifdef RAMDISK main_memory_start += rd_init(main_memory_start, RAMDISK*1024); #endif mem_init(main_memory_start,memory_end); trap_init(); blk_dev_init(); chr_dev_init(); tty_init(); time_init(); sched_init(); buffer_init(buffer_memory_end); hd_init(); floppy_init(); sti(); move_to_user_mode(); if (!fork()) { /* we count on this going ok */ init(); } /* * NOTE!! For any other task 'pause()' would mean we have to get a * signal to awaken, but task0 is the sole exception (see 'schedule()') * as task 0 gets activated at every idle moment (when no other tasks * can run). For task0 'pause()' just means we go check if some other * task can run, and if not we return here. */ for(;;) pause(); } static int printf(const char *fmt, ...) { va_list args; int i; va_start(args, fmt); write(1,printbuf,i=vsprintf(printbuf, fmt, args)); va_end(args); return i; } static char * argv_rc[] = { "/bin/sh", NULL }; static char * envp_rc[] = { "HOME=/", NULL }; static char * argv[] = { "-/bin/sh",NULL }; static char * envp[] = { "HOME=/usr/root", NULL }; void init(void) { int pid,i; setup((void *) &drive_info); (void) open("/dev/tty0",O_RDWR,0); (void) dup(0); (void) dup(0); 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); if (!(pid=fork())) { close(0); if (open("/etc/rc",O_RDONLY,0)) _exit(1); execve("/bin/sh",argv_rc,envp_rc); _exit(2); } if (pid>0) while (pid != wait(&i)) /* nothing */; while (1) { if ((pid=fork())<0) { printf("Fork failed in init\r\n"); 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("\n\rchild %d died with code %04x\n\r",pid,i); sync(); } _exit(0); /* NOTE! _exit, not exit() */ }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值