linux-0.11分析:进程初始化函数init(),第一部分setup((void *) &drive_info) ,第十二篇随笔
进程的初始化函数,init()
先看看这个函吧:
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() */
}
这个函数比较复杂,老规矩,一部分一部分来看吧
第一个部分,看看这个函数setup((void *) &drive_info)
#define DRIVE_INFO (*(struct drive_info *)0x90080)
struct drive_info { char dummy[32]; } drive_info;
init(void)
{
int pid,i;
setup((void *) &drive_info);
.......
这里setup传入就是一个drive_info
的结构体,可以看到上面宏定义的位置在0x90080,还记得setup.s
阶段存入了一个硬盘信息吗?
地址 | 字节 | 存储的信息 |
---|---|---|
0x90080 | 16 | hd0硬盘信息 |
0x90010 | 16 | hd1硬盘信息 |
static inline _syscall1(int,setup,void *,BIOS)
include文件 -> unistd.h
#define _syscall1(type,name,atype,a) \
type name(atype a) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(a))); \
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
return -1; \
}
翻译一下
int setup(void * BIOS){
ong __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_BIOS),"b" ((long)(a))); \ // NR_BIOS = 0
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
return -1; \
}
目的就是开起了int 0x80中断,去sys_call_table
下标为0的函数调用,也就是sys_setup
include文件 -> linux文件 -> sys.h
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid };
kernel文件 -> blk_drv文件 -> hd.c
int sys_setup(void * BIOS)
{
static int callable = 1;
int i,drive;
unsigned char cmos_disks;
struct partition *p;
struct buffer_head * bh;
if (!callable)
return -1;
callable = 0;
#ifndef HD_TYPE
for (drive=0 ; drive<2 ; drive++) {
hd_info[drive].cyl = *(unsigned short *) BIOS;
hd_info[drive].head = *(unsigned char *) (2+BIOS);
hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
hd_info[drive].sect = *(unsigned char *) (14+BIOS);
BIOS += 16;
}
if (hd_info[1].cyl)
NR_HD=2;
else
NR_HD=1;
#endif
for (i=0 ; i<NR_HD ; i++) {
hd[i*5].start_sect = 0;
hd[i*5].nr_sects = hd_info[i].head*
hd_info[i].sect*hd_info[i].cyl;
}
if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
if (cmos_disks & 0x0f)
NR_HD = 2;
else
NR_HD = 1;
else
NR_HD = 0;
for (i = NR_HD ; i < 2 ; i++) {
hd[i*5].start_sect = 0;
hd[i*5].nr_sects = 0;
}
for (drive=0 ; drive<NR_HD ; drive++) {
if (!(bh = bread(0x300 + drive*5,0))) {
printk("Unable to read partition table of drive %d\n\r",
drive);
panic("");
}
if (bh->b_data[510] != 0x55 || (unsigned char)
bh->b_data[511] != 0xAA) {
printk("Bad partition table on drive %d\n\r",drive);
panic("");
}
p = 0x1BE + (void *)bh->b_data;
for (i=1;i<5;i++,p++) {
hd[i+5*drive].start_sect = p->start_sect;
hd[i+5*drive].nr_sects = p->nr_sects;
}
brelse(bh);
}
if (NR_HD)
printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
rd_load();
mount_root();
return (0);
}
-
第一部分
struct hd_i_struct { int head,sect,cyl,wpcom,lzone,ctl; }; #ifdef HD_TYPE struct hd_i_struct hd_info[] = { HD_TYPE }; #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) #else struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} }; static int NR_HD = 0; #endif ..... int sys_setup(void * BIOS) { ..... for (drive=0 ; drive<2 ; drive++) { hd_info[drive].cyl = *(unsigned short *) BIOS; hd_info[drive].head = *(unsigned char *) (2+BIOS); hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); hd_info[drive].ctl = *(unsigned char *) (8+BIOS); hd_info[drive].lzone = *(unsigned short *) (12+BIOS); hd_info[drive].sect = *(unsigned char *) (14+BIOS); BIOS += 16; } }
这里就是把
0x90080和0x90010
的数据存放到hd_info
中, -
第二部分
#define MAX_HD 2 static struct hd_struct { long start_sect; long nr_sects; } hd[5*MAX_HD]={{0,0},}; ..... int sys_setup(void * BIOS) { ..... for (i=0 ; i<NR_HD ; i++) { hd[i*5].start_sect = 0; hd[i*5].nr_sects = hd_info[i].head* hd_info[i].sect*hd_info[i].cyl; } for (drive=0 ; drive<NR_HD ; drive++) { ..... p = 0x1BE + (void *)bh->b_data; for (i=1;i<5;i++,p++) { hd[i+5*drive].start_sect = p->start_sect; hd[i+5*drive].nr_sects = p->nr_sects; } brelse(bh); } ...... }
第一个循环在初始化
hd
,第二个循环在给hd赋值
目前我们已经把硬盘的基本信息存入了 hd_info[],把硬盘的分区信息存入了 hd[]
-
第三部分
rd_load(); mount_root(); return (0);
rd_load这个不重要,就暂时不看了
mount_root 直译过来就是加载根,再多说几个字是加载根文件系统,有了它之后,操作系统才能从一个根开始找到所有存储在硬盘中的文件,所以它是文件系统的基石
看看这个函数mount_root
fs文件 -> super.c
#define NR_FILE 64
#define NR_SUPER 8
struct super_block super_block[NR_SUPER];
void mount_root(void)
{
int i,free;
struct super_block * p;
struct m_inode * mi;
if (32 != sizeof (struct d_inode))
panic("bad i-node size");
for(i=0;i<NR_FILE;i++)
file_table[i].f_count=0;
if (MAJOR(ROOT_DEV) == 2) {
printk("Insert root floppy and press ENTER");
wait_for_keypress();
}
for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) {
p->s_dev = 0;
p->s_lock = 0;
p->s_wait = NULL;
}
if (!(p=read_super(ROOT_DEV)))
panic("Unable to mount root");
if (!(mi=iget(ROOT_DEV,ROOT_INO)))
panic("Unable to read root i-node");
mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */
p->s_isup = p->s_imount = mi;
current->pwd = mi;
current->root = mi;
free=0;
i=p->s_nzones;
while (-- i >= 0)
if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data))
free++;
printk("%d/%d free blocks\n\r",free,p->s_nzones);
free=0;
i=p->s_ninodes+1;
while (-- i >= 0)
if (!set_bit(i&8191,p->s_imap[i>>13]->b_data))
free++;
printk("%d/%d free inodes\n\r",free,p->s_ninodes);
}
比较长,就一部分一部分来看吧
-
第一部分
#define NR_FILE 64 struct file { unsigned short f_mode; unsigned short f_flags; unsigned short f_count; struct m_inode * f_inode; off_t f_pos; }; struct file file_table[NR_FILE]; for(i=0;i<NR_FILE;i++) file_table[i].f_count=0;
这部分很简单了,把64个
file_table
的f_count
属性初始化为0这个 file_table 表示进程所使用的文件,进程每使用一个文件,都需要记录在这里,包括文件类型、文件 inode 索引信息等,而这个 f_count 表示被引用的次数,此时还没有引用,所以设置为零。
-
第二部分
#define NR_SUPER 8 struct super_block { unsigned short s_ninodes; unsigned short s_nzones; unsigned short s_imap_blocks; unsigned short s_zmap_blocks; unsigned short s_firstdatazone; unsigned short s_log_zone_size; unsigned long s_max_size; unsigned short s_magic; /* These are only in memory */ struct buffer_head * s_imap[8]; struct buffer_head * s_zmap[8]; unsigned short s_dev; struct m_inode * s_isup; struct m_inode * s_imount; unsigned long s_time; struct task_struct * s_wait; unsigned char s_lock; unsigned char s_rd_only; unsigned char s_dirt; }; struct super_block super_block[NR_SUPER]; for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) { p->s_dev = 0; p->s_lock = 0; p->s_wait = NULL; }
这段就是把
super_block
这个结构体初始化操作这个 super_block 存在的意义是,操作系统与一个设备以文件形式进行读写访问时,就需要把这个设备的超级块信息放在这里。
-
第三部分
p=read_super(ROOT_DEV) mi=iget(ROOT_DEV,ROOT_INO) mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */ p->s_isup = p->s_imount = mi; current->pwd = mi; current->root = mi; free=0; i=p->s_nzones; free=0;
read_super 就是读取硬盘中的超级块。
然后把该 inode 设置为当前进程(也就是进程 1)的当前工作目录和根目录。
current->pwd = mi;current->root = mi;
然后记录块位图信息。最后记录 inode 位图信息。