**好的,OK,上次我们说到了加载根文件系统,我们可以根据根inode找到所有的文件。
void init(void) {
setup((void *) &drive_info);
(void) open("/dev/tty0",O_RDWR,0);
(void) dup(0);
(void) dup(0);
}
我们可以看到下一行就是一个open函数,之前说过open函数使用系统调用l打开文件/dev/tty0,之后后面还有两个dup。
open函数会触发0x80中断,通过调用sys_open这个系统调用函数。
open.c
struct file file_table[64] = {0};
int sys_open(const char * filename,int flag,int mode) {
struct m_inode * inode;
struct file * f;
int i,fd;
mode &= 0777 & ~current->umask;
for(fd=0 ; fd<20; fd++)
if (!current->filp[fd])
break;
if (fd>=20)
return -EINVAL;
current->close_on_exec &= ~(1<<fd);
f=0+file_table;
for (i=0 ; i<64 ; i++,f++)
if (!f->f_count) break;
if (i>=64)
return -EINVAL;
(current->filp[fd]=f)->f_count++;
i = open_namei(filename,flag,mode,&inode);
if (S_ISCHR(inode->i_mode))
if (MAJOR(inode->i_zone[0])==4) {
if (current->leader && current->tty<0) {
current->tty = MINOR(inode->i_zone[0]);
tty_table[current->tty].pgrp = current->pgrp;
}
} else if (MAJOR(inode->i_zone[0])==5)
if (current->tty<0) {
iput(inode);
current->filp[fd]=NULL;
f->f_count=0;
return -EPERM;
}
if (S_ISBLK(inode->i_mode))
check_disk_change(inode->i_zone[0]);
f->f_mode = inode->i_mode;
f->f_flags = flag;
f->f_count = 1;
f->f_inode = inode;
f->f_pos = 0;
return (fd);
}
- 在进程描述符数组filp中找到一个空闲项。在进程的数据结构task_struct里面有一个filp数组的字段,这个就是文件描述符数组,空闲的地方索引值fd。
- 在系统文件表file_table里面找到一个空闲项。
- 将进程的文件描述符数组项和系统的文件表项对应起来。
- 根据文件名从文件系统中找到这个文件。
- 填充file数据,就是初始化这个f,包括刚刚找到的inode值,最后返回给上层文件描述符fd的值,也就是0.
open函数返回的0号fd,作为标准输入设备,接下来的dup为1号fd赋值,作为标准输出设备,接下来的dup为2号fd赋值,作为标准错误输出设备,这就是常说的stdin.stdout,stderr。
int sys_dup(unsigned int fildes) {
return dupfd(fildes,0);
}
// fd 是要复制的文件描述符
// arg 是指定新文件描述符的最小数值
static int dupfd(unsigned int fd, unsigned int arg) {
...
while (arg < 20)
if (current->filp[arg])
arg++;
else
break;
...
(current->filp[arg] = current->filp[fd])->f_count++;
return arg;
}
dup就是从进程的filp中找到下一个空闲项,然后把要复制的文件描述符fd的信息,复制到这里。
**进程1比进程0多了与外设交互的能力,进程1创建之后通过setup加载根文件系统,open打开tty0设备文件,使得进程1具备了与外设交互的能力。
好的,继续往下看下去。
void init(void) {
...
if (!(pid=fork())) {
close(0);
open("/etc/rc",O_RDONLY,0);
execve("/bin/sh",argv_rc,envp_rc);
_exit(2);
}
...
}
- fork一个新的子进程,就是进程2.
- 在进程2里面关闭0号文件描述符
- 只读形式打开rc文件
- 执行sh文件
除此之外,进程1fork进程2会复制一份filp数组,这样进程2和进程1一样有了与外设交互的能力。
进程0复制进程1页表复制只有160项,也就是640K,而之后的复制都是1024项,也就是4M空间。
close()函数
int sys_close(unsigned int fd) {
...
current->filp[fd] = NULL;
...
}
open()函数
void init(void) {
...
if (!(pid=fork())) {
...
open("/etc/rc",O_RDONLY,0);
...
}
...
}
这里的open函数打开的/etc/rc文件正好占据了0号文件描述符的位置。