Linux内核设计的艺术-用户进程与内存管理、缓冲区和多进程操作文件
2014-03-13 10:09
253 查看
1、用户进程与内存管理
父子进程共享同一页面,如果这个页面设置为可写状态,那么两个进程同时写一个页面会造成混乱,所以此时页表表项后三位设置为101,U/S=1,
R/W=0,P=1。对应代码如下:
代码路径:mm/memory.c
如果父子进程都要写页面,该怎么办呢?
假设进程A是父进程,B是子进程。进程A要往共享页面写数据,因为该页面为只读,会产生页写保护,页写保护为进程A申请新页面,并且引用计数递
减1,并让进程A的页表中指向原页面的页表项改为指向新页面,并将其使用的属性从“只读”改变为“可读可写”,一切准备就绪后,将原页面中的内容复制到新页面中。具体结果如下图:
进程A执行一段时间后,就该轮到它的子进程-进程B执行了。进程B仍然使用着原页面。假设也要在原页面总进程写操作,但是现在原页面的属性仍然
是“只读”的,这一点在进程A创建B时就是这样设置的,一直都没有改变过。所以在这种情况下,又需要进程页写保护,由于原页面的引用计数已经被消减为1了 ,所以现在就要将院页面的属性设置为“可读可写”。如下图:
页写保护,代码路径:mm/memory.c
进程切换,一是时间片到了,切换,且只有在3特权级下才能切换,0特权级下不能切换;二是等待硬盘读盘时,要切换到其他进程。
代码路径:kernel/sched.c
缺页中断是因为页表项中P位为0。
创建子进程时,不能用父进程的时间片复制给子进程的时间片,因为父进程的时间片不断减少,应该用优先级赋值给时间片。
p->counter=p->priority;
另外我加载shell进程的eip可能是就是shell进程的逻辑偏移,而不是原来我分析的0。
2、缓冲区和多进程操作文件
之所以需要缓冲区,是因为有了缓冲区后,数据交换快。你可能会问,进程内存空间和硬盘交换数据,中间加了一个缓冲区,由于缓冲区也是内存,只是徒劳增加了一个中介,其实则不然,快的原因是因为缓冲区共享。如果A把硬盘数据读到了缓冲区,那么不会立即释放,此时B也可以共享缓冲区,也就加快了速度。
那么问题就变成了如何让数据在缓冲区中停留的时间尽可能长?
首先通过建立hash表,getblk时候首先查询hash表中是否要申请的缓冲块。如果没有,那么在申请一个b_count为0缓冲块,而且申请后要放入链表的最后,这样做,下次申请时会从头申请,这样就使刚申请的缓冲块尽可能多的不被马上重新申请。
b_update针对进程方向,它的作用是,告诉内核,只要缓冲块中b_update字段被设置为1,缓冲块的数据已经是数据块中最新的,就可以放心地支持进程共享缓冲块的数据。
b_dirt是针对硬盘方向的。只要缓冲块的b_dirt字段被设置为1,就是告诉内核,这个缓冲块中的内容已经被进程方向的数据改写了,最终需要同步到硬盘上。
(1)进程的本意是要读文件中这个数据块到进程空间。(2)申请一个缓冲块与硬盘数据块绑定,但硬盘数据并没有同步更新到缓冲块,b_update为0。(3)此时进程开始读取缓冲块的数据,那就都是垃圾数据。
(1)进程的本意是把进程空间一部分数据写到已有数据的硬盘数据块。(2)申请一个缓冲块与硬盘数据块绑定,但硬盘数据并没有同步更新到缓冲块,b_update为0。(3)将进程空间的一部分数据写到缓冲块中,但是后一部分是垃圾数据,当同步到外部硬盘时,垃圾数据也被写到硬盘数据块中了。
(1)新建硬盘数据块。(2)申请一个缓冲块与硬盘数据块绑定,b_update设置为1,缓冲块的数据全部清零。(3)此时新建的不可能读,只有写,将进程空间一部分数据写到缓冲块,其余部分为0,同步到新建硬盘数据块中,没有问题。
(1)缓冲块和硬盘数据块数据一致了,b_update为1。(2)往缓冲块中写入新的数据,此时b_dirt为1,但b_update还应该为1,因为此时并不妨碍进程读(虽然和硬盘上的数据不一致,但迟早是要一致,因为b_dirt为1)和写缓冲块。
i_dirt=1表示i节点已经被更新,后来同步i节点的时候i_dirt设置为0,b_dirt=1。
i_update没有用,因为这些文件管理信息在硬盘上都是以数据块的形式存在的,它们以块的形式载入缓冲区,也就是会用b_update。
s_dirt没有用,因为进程全部从super_block[8]中读取信息,并没有往表项中写入数据。
b_count表示缓冲块被进程占用的数量,新建为1,释放减1,共享加1。
i_count表示inode_table[32]对应节点的引用数量。
b_lock表示不能在缓冲块数据同步到硬盘块的同时,还在向这个缓冲块中新的数据。
b_wait在缓冲块被加锁的过程中,而且无论有多少进程申请到了这个缓冲块,都不能立即操作该缓冲块,都要挂起,并切换到其他进程去执行。这就需要记录有哪些进程因为等待这个缓冲块的解锁而被挂起了,这就需要b_wait。同步到硬盘块后,首先解锁,然后唤醒等待队列。
i_lock、i_wait、s_lock、s_wait同b_lock、b_wait一样。
request如果申请不到请求项,那么利用waiting挂起,end_request中wake_up,那么等待请求项的进程被唤醒。
getblk函数如下:
如果找到了b_count为0,如果b_lock和b_dirt均为0,那就是在好不过了,如果不为0,优先选择b_lock为1的,其次再选择b_dirt为1的,因为b_lock为1,说明缓冲块正在跟硬盘交互数据,交互完了,最终轮到当前进程使用。而b_dirt为1,说明在建立新的绑定关系之前,肯定需要把数据同步到硬盘,同步的时候肯定要加锁-b_lock为1.所以,选择b_lock为1比选择b_dirt为1的,少等待由b_dirt为1到b_lock为1的时间。
父子进程共享同一页面,如果这个页面设置为可写状态,那么两个进程同时写一个页面会造成混乱,所以此时页表表项后三位设置为101,U/S=1,
R/W=0,P=1。对应代码如下:
代码路径:mm/memory.c
int copy_page_tables(unsigned long from,unsigned long to,long size) { ... for( ; size-->0 ; from_dir++,to_dir++) { if (1 & *to_dir) panic("copy_page_tables: already exist"); if (!(1 & *from_dir)) continue; from_page_table = (unsigned long *) (0xfffff000 & *from_dir); if (!(to_page_table = (unsigned long *) get_free_page())) return -1; /* Out of memory, see freeing */ *to_dir = ((unsigned long) to_page_table) | 7; nr = (from==0)?0xA0:1024; for ( ; nr-- > 0 ; from_page_table++,to_page_table++) { this_page = *from_page_table; if (!(1 & this_page)) continue; this_page &= ~2;//页表项中的R/W位被设置为0,~2为101 *to_page_table = this_page; if (this_page > LOW_MEM) { *from_page_table = this_page; this_page -= LOW_MEM; this_page >>= 12; mem_map[this_page]++;//父子进程共享,引用计数记录在mem_map中,累加为2 ... }
如果父子进程都要写页面,该怎么办呢?
假设进程A是父进程,B是子进程。进程A要往共享页面写数据,因为该页面为只读,会产生页写保护,页写保护为进程A申请新页面,并且引用计数递
减1,并让进程A的页表中指向原页面的页表项改为指向新页面,并将其使用的属性从“只读”改变为“可读可写”,一切准备就绪后,将原页面中的内容复制到新页面中。具体结果如下图:
进程A执行一段时间后,就该轮到它的子进程-进程B执行了。进程B仍然使用着原页面。假设也要在原页面总进程写操作,但是现在原页面的属性仍然
是“只读”的,这一点在进程A创建B时就是这样设置的,一直都没有改变过。所以在这种情况下,又需要进程页写保护,由于原页面的引用计数已经被消减为1了 ,所以现在就要将院页面的属性设置为“可读可写”。如下图:
页写保护,代码路径:mm/memory.c
void un_wp_page(unsigned long * table_entry) { unsigned long old_page,new_page; old_page = 0xfffff000 & *table_entry; if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {//发现原页面引用计数为1,不用共享了 *table_entry |= 2;//010,R/W位设置为1,可读可写 invalidate(); return; } if (!(new_page=get_free_page()))//申请到新页面 oom(); if (old_page >= LOW_MEM) mem_map[MAP_NR(old_page)]--;//页面引用计数递减1 *table_entry = new_page | 7;//111,标志着新页面可读可写了 invalidate(); copy_page(old_page,new_page);//将原页面内容复制给进程A新申请的页面 }
进程切换,一是时间片到了,切换,且只有在3特权级下才能切换,0特权级下不能切换;二是等待硬盘读盘时,要切换到其他进程。
代码路径:kernel/sched.c
void do_timer(long cpl) { ... if ((--current->counter)>0) return;//判断时间片是否消减为0 current->counter=0; if (!cpl) return;//只有在3特权级下才能切换,0特权级下不能切换 schedule(); }
缺页中断是因为页表项中P位为0。
创建子进程时,不能用父进程的时间片复制给子进程的时间片,因为父进程的时间片不断减少,应该用优先级赋值给时间片。
p->counter=p->priority;
另外我加载shell进程的eip可能是就是shell进程的逻辑偏移,而不是原来我分析的0。
2、缓冲区和多进程操作文件
之所以需要缓冲区,是因为有了缓冲区后,数据交换快。你可能会问,进程内存空间和硬盘交换数据,中间加了一个缓冲区,由于缓冲区也是内存,只是徒劳增加了一个中介,其实则不然,快的原因是因为缓冲区共享。如果A把硬盘数据读到了缓冲区,那么不会立即释放,此时B也可以共享缓冲区,也就加快了速度。
那么问题就变成了如何让数据在缓冲区中停留的时间尽可能长?
首先通过建立hash表,getblk时候首先查询hash表中是否要申请的缓冲块。如果没有,那么在申请一个b_count为0缓冲块,而且申请后要放入链表的最后,这样做,下次申请时会从头申请,这样就使刚申请的缓冲块尽可能多的不被马上重新申请。
b_update针对进程方向,它的作用是,告诉内核,只要缓冲块中b_update字段被设置为1,缓冲块的数据已经是数据块中最新的,就可以放心地支持进程共享缓冲块的数据。
b_dirt是针对硬盘方向的。只要缓冲块的b_dirt字段被设置为1,就是告诉内核,这个缓冲块中的内容已经被进程方向的数据改写了,最终需要同步到硬盘上。
(1)进程的本意是要读文件中这个数据块到进程空间。(2)申请一个缓冲块与硬盘数据块绑定,但硬盘数据并没有同步更新到缓冲块,b_update为0。(3)此时进程开始读取缓冲块的数据,那就都是垃圾数据。
(1)进程的本意是把进程空间一部分数据写到已有数据的硬盘数据块。(2)申请一个缓冲块与硬盘数据块绑定,但硬盘数据并没有同步更新到缓冲块,b_update为0。(3)将进程空间的一部分数据写到缓冲块中,但是后一部分是垃圾数据,当同步到外部硬盘时,垃圾数据也被写到硬盘数据块中了。
(1)新建硬盘数据块。(2)申请一个缓冲块与硬盘数据块绑定,b_update设置为1,缓冲块的数据全部清零。(3)此时新建的不可能读,只有写,将进程空间一部分数据写到缓冲块,其余部分为0,同步到新建硬盘数据块中,没有问题。
(1)缓冲块和硬盘数据块数据一致了,b_update为1。(2)往缓冲块中写入新的数据,此时b_dirt为1,但b_update还应该为1,因为此时并不妨碍进程读(虽然和硬盘上的数据不一致,但迟早是要一致,因为b_dirt为1)和写缓冲块。
i_dirt=1表示i节点已经被更新,后来同步i节点的时候i_dirt设置为0,b_dirt=1。
i_update没有用,因为这些文件管理信息在硬盘上都是以数据块的形式存在的,它们以块的形式载入缓冲区,也就是会用b_update。
s_dirt没有用,因为进程全部从super_block[8]中读取信息,并没有往表项中写入数据。
b_count表示缓冲块被进程占用的数量,新建为1,释放减1,共享加1。
i_count表示inode_table[32]对应节点的引用数量。
b_lock表示不能在缓冲块数据同步到硬盘块的同时,还在向这个缓冲块中新的数据。
b_wait在缓冲块被加锁的过程中,而且无论有多少进程申请到了这个缓冲块,都不能立即操作该缓冲块,都要挂起,并切换到其他进程去执行。这就需要记录有哪些进程因为等待这个缓冲块的解锁而被挂起了,这就需要b_wait。同步到硬盘块后,首先解锁,然后唤醒等待队列。
i_lock、i_wait、s_lock、s_wait同b_lock、b_wait一样。
request如果申请不到请求项,那么利用waiting挂起,end_request中wake_up,那么等待请求项的进程被唤醒。
getblk函数如下:
#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock) struct buffer_head * getblk(int dev,int block) { struct buffer_head * tmp, * bh; repeat: if ((bh = get_hash_table(dev,block))) return bh; tmp = free_list; do { if (tmp->b_count) continue; if (!bh || BADNESS(tmp)<BADNESS(bh)) {//优先选择b_lock为1 bh = tmp; if (!BADNESS(tmp)) break; } /* and repeat until we find something good */ } while ((tmp = tmp->b_next_free) != free_list); if (!bh) {//最终也没有找到b_count为0的缓冲块 sleep_on(&buffer_wait);//当前进程只好挂起 goto repeat; } }
如果找到了b_count为0,如果b_lock和b_dirt均为0,那就是在好不过了,如果不为0,优先选择b_lock为1的,其次再选择b_dirt为1的,因为b_lock为1,说明缓冲块正在跟硬盘交互数据,交互完了,最终轮到当前进程使用。而b_dirt为1,说明在建立新的绑定关系之前,肯定需要把数据同步到硬盘,同步的时候肯定要加锁-b_lock为1.所以,选择b_lock为1比选择b_dirt为1的,少等待由b_dirt为1到b_lock为1的时间。
相关文章推荐
- Linux内核设计的艺术-多进程操作文件的综合实例
- Unix高级编程:malloc内存管理、缓冲机制、mmap内存映射到进程、系统调用文件操作
- 2011年11月28日——进程管理,内存管理,文件系统,系统管理,网络操作概述
- linux 进程管理,内存管理,文件系统,系统管理,网络操作概述
- Linux内核设计的艺术-文件操作
- shell 操作文件来创建多个用户
- 修改用户进程可打开文件数限制
- Jsp : tag 文件操作数据库 (用户注册程序)
- 无法保存对hosts权限所作的更改 拒绝访问(权限,防止Windows主机文件、进程、注册表项进行操作和更改)
- 按名称kill一批进程或rm一批文件(请谨慎操作)
- go语言笔记——go是有虚拟机runtime的,不然谁来做GC呢,总不会让用户自己来new和delete进行内存管理吧,还有反射!Go 的 runtime 嵌入到了每一个可执行文件当中
- Linux用户和组的操作(一) 用户文件/etc/passwd
- 删除oracle用户、表空间、及物理文件操作过程
- 0220自学Linux_逻辑理解用户进程权限相关+理解文件内各字段(passwd,shadow,group)
- jetty->请求的操作无法在使用用户映射区域打开的文件上执行
- 打开操作,以及如果没有默认格式,弹出打开方式让用户自己选择何种程序打开文件
- 进程分配用户空间或是文件或者设备文件空间映射函数分析(二)
- WinForm中使用XML文件存储用户配置及操作本地Config配置文件
- Linux内核设计的艺术-进程1的创建及执行b
- 第2章 用户、文件操作与联机帮助:编写who命令