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 位图信息。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)