linux0.11的进程1的创建和执行

1. 进程0创建进程1

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
--- init --- main.c --- sched_init()
 |                   |- hd_init()
 |                   |- fork() --- int 0x80//系统调度中断
 |                   |- init() --- setup() --- int 0x80
 |                   |- pause() --- int 0x80
 |
 |- kernel --- sched.c --- sched_init() --- set_system_gate(0x80,&system_call)//注册中断
 |          |           |- sys_pause() --- schedule()
 |          |           |- schedule() --- switch_to()
 |          |           |- sleep_on() --- *p = current//p=&bh->b_wait
 |          |           |              |- schedule()
 |          |           |- wake_up() --- (**p).state=0//p=&bh->b_wait,进程1就绪
 |          |
 |          |- sched.h --- switch_to()//进程1切换到进程0后在sys_pause中循环
 |          |
 |          |- system_call.s --- system_call() --- sys_call_table(,%eax,4)
 |          |                 |- sys_fork() --- find_empty_process()//找到空闲任务结构task[nr]
 |          |                 |              |- copy_process()//复制父进程任务结构和页表
 |          |                 |- hd_interrupt() --- do_hd()
 |          |
 |          |- fork.c --- find_empty_process() --- 返回进程号nr,
 |          |          |                        |  并且找到最小的pid值赋值给last_pid
 |          |          |- copy_process() --- p=get_free_page()//开辟新的一页内存用于存放子进程任务结构
 |          |          |                  |- *p=*current//复制父进程任务结构给子进程
 |          |          |                  |- p->tss.eip=eip
 |          |          |                  |- p->tss.eax=0//这是子进程执行时,fork函数的返回值
 |          |          |                  |- copy_mem(p)//建立页表映射,并复制父进程页表
 |          |          |                  |- f->f_count++
 |          |          |                  |- set_tss_desc()//将task[nr]的tss和ldt地址填入GDT
 |          |          |                  |- set_ldt_desc()
 |          |          |                  |- p->state = TASK_RUNNING//子进程改为就绪态
 |          |          |- copy_mem() --- set_base(p->ldt[1]...)//子进程ldt,数据段和代码段基地址
 |          |                         |- set_base(p->ldt[2]...)//赋值为0x4000000*nr
 |          |                         |- copy_page_tables()//建立页表映射,并复制父进程页表
 |          |
 |          |- blk_drv --- hd.c --- sys_setup() --- hd_info[2]赋值
 |                      |        |               |- hd[0]和hd[5]赋值
 |                      |        |               |- bh=bread()
 |                      |        |               |- 判断硬盘信息有效
 |                      |        |               |- 根据硬盘中的分区信息设置hd[1]~hd[4]
 |                      |        |               |- brelse(bh)
 |                      |        |               |- rd_load()//用软盘格式化虚拟盘
 |                      |        |               |- mount_root()//加载根文件系统
 |                      |        |- hd_init() --- blk_dev[3].request_fn = do_hd_request
 |                      |        |             |- set_intr_gate(0x2E,&hd_interrupt);
 |                      |        |- do_hd_request() --- INIT_REQUEST//CURRENT==NULL时返回
 |                      |        |                   |- hd_out(...,&read_intr) --- 控制硬盘开始读写,
 |                      |        |                                              |  完成后引发中断
 |                      |        |                                              |- do_hd=read
 |                      |        |- read_intr() --- port_read(...CURRENT->buffer)
 |                      |                        |  //CURRENT=blk_dev[3].current_request
 |                      |                        |  //CURRENT->buffer=bh->b_data
 |                      |                        |- end_request(1)//读取完成后执行到这里
 |                      |                        |- do_hd_request()
 |                      |
 |                      |- ll_rw_block.c --- ll_rw_block() --- major=MAJOR(bh->b_dev)
 |                      |                 |                 |- make_request(major,bh)
 |                      |                 |- make_request() --- lock_buffer(bh)
 |                      |                 |                  |- 找到空闲请求req
 |                      |                 |                  |- req->buffer=bh->b_data
 |                      |                 |                  |- req->next=NULL
 |                      |                 |                  |- add_request(blk_dev[major],req)
 |                      |                 |- add_request() --- blk_dev[3]->current_request=req
 |                      |                                   |- blk_dev[3]->request_fn
 |                      |
 |                      |- blk.h --- end_request() --- CURRENT->bh->b_uptodate = 1
 |                      |         |                  |- unlock_buffer(CURRENT->bh)
 |                      |         |                  |- CURRENT = CURRENT->next//CURRENT=NULL
 |                      |         |- unlock_buffer() --- bh->b_lock=0
 |                      |                             |- wake_up(&bh->b_wait)
 |                      |
 |                      |- ramdisk.c --- rd_load() --- bh=breada()
 |                                                  |- 拷贝bh->b_data到s//s为超级块
 |                                                  |- brelse(bh)
 |                                                  |- 计算虚拟块数
 |                                                  |- 将软盘文件系统复制到虚拟盘
 |                                                  |- ROOT_DEV=0x0101//虚拟盘设置为根设备
 |
 |- include --- linux --- sys_call_table[] --- sys_fork()
 |                                          |- sys_pause()
 |                                          |- sys_setup()
 |
 |- mm --- memery.c --- copy_page_tables() --- from和to分别为父进程和子进程线性地址
 |                                          |- from_dir=(from>>20) & 0xffc //取出页目录偏移,然后乘上4
 |                                          |- to_dir=(to>>20) & 0xffc     //等于在页目录中实际的地址
 |                                          |- size = (size+0x3fffff)) >> 22
 |                                          |  //一个页表管理4MB内存,">>22"相当于"/4MB"
 |                                          |  //也就是将要拷贝的字节数转为要拷贝的页表数
 |                                          |- for( ; size-->0 ; from_dir++,to_dir++) {
 |                                          |      from_page_table=0xfffff000 & *from_dir;
 |                                          |      //取出父进程页表地址
 |                                          |      to_page_table=get_free_page();
 |                                          |      //申请一个页面作为子进程页表
 |                                          |      *to_dir = to_page_table | 7;
 |                                          |      //将该页面放入子进程的页目录表
 |                                          |      nr = (from==0)?0xA0:1024;
 |                                          |      for(;nr-->0;from_page_table++,to_page_table++) {
 |                                          |          this_page = *from_page_table;
 |                                          |          //取出父进程的页地址
 |                                          |          this_page &= ~2;//设置为只读
 |                                          |          *to_page_table = this_page;
 |                                          |          //放入子进程页表
 |                                          |          处理mem_map[]
 |                                          |      }
 |                                          |  }
 |                                          |- invalidate()//刷新CR3页高速缓存
 |
 |- fs --- buffer.c --- bread() --- bh=getblk()
        |            |           |- ll_rw_block(bh)
        |            |           |- wait_on_buffer(bh)
        |            |           |- if(bh->b_uptodate)//返回bh
        |            |- getblk() --- get_hash_table()
        |            |            |- 遍历free_list,找到空闲bh
        |            |            |- remove_from_queues(bh)
        |            |            |- bh->b_dev=dev
        |            |            |  bh->b_blocknr=block
        |            |            |- insert_into_queues(bh)
        |            |- get_hash_table() --- find_buffer()
        |            |- find_buffer()
        |            |- remove_from_queues(bh)
        |            |- insert_into_queues(bh) --- hash(...) = bh
        |            |- wait_on_buffer() --- sleep_on(&bh->b_wait)
        |                                    //等待读盘完成b_wait=NULL
        |
        |- super.c --- mount_root() --- 初始化super_block[8]
                    |                |- p=read_super(ROOT_DEV)//读取超级块
                    |                |- mi=iget(ROOT_DEV,ROOT_INO)//读取根节点
                    |                |- p->s_isup = p->s_imount = mi//挂接i节点
                    |                |- current->pwd = mi
                    |                |- current->root = mi
                    |- read_super() --- 从super_block[8]中申请一项
                    |                |- s->s_dev = dev
                    |                |- lock_super(s)
                    |                |- bh = bread(dev,1)
                    |                |- 拷贝bh->b_data到s//s前半部分被填充
                    |                |- s->s_imap[i]=bread()
                    |                |- s->s_zmap[i]=bread()
                    |- iget() --- empty = get_empty_inode()
                    |          |- inode=empty
                    |          |- inode->i_dev = dev
                    |          |- inode->i_num = nr
                    |          |- read_inode(inode)
                    |- read_inode() --- sb=get_super(inode->i_dev)
                                     |- bh=bread(inode->i_dev,block)
                                     |- 拷贝bh->b_data到inode//inode前半部分被填充

  

进程0创建进程1
1. fork()函数执行,触发0x80中断,系统由特权级3转换为特权级0,跳转到sys_fork执行。
2. sys_fork中首先使用find_empty_process()函数找到空闲的进程号nr,和最小的pid值,然后调用copy_process()函数复制父进程任务结构到给子进程。
3. copy_process()函数先将task[nr]指向新的一页内存,然后将父进程任务结构复制到task[nr]指向的内存;然后修改task[nr]tss.eip=当前eiptss.eax=0tss.ldt = _LDT(nr);最后调用copy_mem()函数复制父进程页表到子进程。
4. copy_mem()函数中,设置新的数据段和代码段基址为nr*0x4000000,再将其填入当前进程的LDT[1]和LDT[2],然后调用copy_page_tables()函数开始复制页表。
5. copy_page_tables()函数建立的新的映射关系,并复制父进程的页表到子进程。首先申请一页内存作为子进程的页表,将页表首地址填入页目录表,然后将父进程的页表复制到子进程的页表中,最后写CR3寄存器,刷新MMU,这样父进程和子进程就共享同样的内存了。映射建立完后就可以直接使用线性地址寻址了。
6. 返回copy_process()函数,将填充好的task[nr]tssldt的地址填充到GDT中相应的位置。
7. 将进程1改为状态改为就绪态,最终中断返回,fork返回子进程pid值。
8. 死循环中执行pause(),将当前进程(进程0)改为可中断等待状态,然后调用schedule()函数。
9. schedule()函数找到task[]中就绪态的进程只有进程1,于是切换到进程1。
10. 系统自动载入tss中的寄存器值到各寄存器,其中eip为进程0调用0x80中断后地址,exa=0,代码段基址在LDT[1]中,为0x4000000,这个线性地址是和进程0共享同一段内存的,因此代码段的物理地址是相同,因此跳转到进程0的eip的位置执行,也就是system_call函数的iret的位置。
11. 同样fork函数返回,返回值为exa,也就是0,执行init()函数。

读取硬盘信息
1. init()函数中执行setup()系统调用,触发0x80中断,系统转换到特权级0,执行sys_setup()函数。
2. sys_setup()函数中调用bread()读取硬盘信息到缓冲区bh,硬盘设备号是0x300和0x305,从0开始读取。
3. bread()函数中,首先使用getblk()函数在缓冲区中找到空闲的缓冲块。
4. getblk()函数遍历缓冲区数据结构,找到空闲的缓冲块,然后将缓冲块bh->b_dev=dev,返回缓冲区管理结构。
5. 返回bread()函数,调用ll_rw_block()函数从硬盘中读取数据到缓冲块。其中

1
2
major=MAJOR(bh->b_dev)=MAJOR(0x300)=3
blk_dev[major].request_fn=blk_dev[3].request_fn=do_hd_request

决定了硬盘该请求是硬盘读写请求。
6. bread()函数继续执行wait_on_buffer()函数等待硬盘读取完成,完成后返回bh
7. 返回sys_setup()函数,判断刚才读取的硬盘引导区内容是否有效,然后根据硬盘中的分区信息设置虚拟分区。

挂载根文件系统
1. sys_setup()函数中,继续调用rd_load()函数,读取软盘内容到内存的虚拟盘(4-6MB)中,并将根设备设置为虚拟盘(0x0101)。然后调用mount_root()函数挂载文件系统。
2. mount_root()函数中,先初始化file_table[64],然后初始化super_block[8],然后读取根文件系统(虚拟盘)的超级块到p,调用iget()函数读取根文件系统的根节点到mi。
3. iget()函数中,先在inode_table[32]中申请一个空闲的inode节点,然后将inode->i_dev = dev; inode->i_num = nr,nr为inode号,这里为1。然后调用read_inode()函数读取该节点。
4. read_inode()函数中首先读取inode->i_dev的超级块,然后和根据超级块的信息确定inode->i_num对应的块,最后使用bread()函数读取对应inode节点。
5. 最终返回iget()函数,将p->s_isup = p->s_imount = mi完成根文件系统的挂载。
6. 中断返回,最终返回sys_setup()函数。


__EOF__

本文作者漫漫程序之路
本文链接https://www.cnblogs.com/still-smile/p/12992766.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Smah  阅读(649)  评论(1编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示