地址映射与共享

来源:互联网 发布:java process 编辑:程序博客网 时间:2024/05/16 15:42
这次实验做的着实好纠结。。想来也不是说有多难,关键是给的资料太少了,尤其是移植到0.11下那一步,指导书就没几句话,我左看右看也没搞明白到底要干什么。。赵炯同志的那本书又好多要看的,总是看不进去。。不过静下心去看的话还真的能获取好多有用的信息,加上各种百度Google,也不算太难,关键是查资料实在是好费时间啊。。。由于是第一次直接在代码中对内存操作,一会儿虚拟,一会线性,一会物理的,遇到了好多问题。。所谓好记性不如烂笔头,我还是有必要总结一下。。。


先说ubuntu下的那个程序吧,上次都编的差不多了,主要是熟悉了shmget, shmat的操作就好了,广泛搜罗之后比较好的贴子就拿来了,以后可能用得着。但是编这次程序的时候居然发现了上次的bug。。。。

1、我开辟了两块共享内存,一个num用来存缓冲区中有多少产品,这样指针直接后移这么多就好了。但是producer.c中通过判断num是否为零来初始化,但是可能出现消费完的情况,这样就出现了范老师最讨厌的问题:有时候对有时候错,错了还自己偷偷知道。。因为不一定什么时候消费完,也可能一直没有出现这样的情况,所以加了个flag用来判断是否是第一次,再初始化。

2、生产者生产的时候,我是通过读取最后一个数字,然后++,在写进去的,所以如果消费完的话,是没有内容可以读取出来的,所以把受范老师影响的while循环,改成了for,直接靠循环次数来记录编号,不用再读取了。。

这样看来,上次程序就有这些问题,只不过当时是两个进程,在一个程序里,可能切换的比较频繁?总之没有发现消费者消费完的情况,纯属侥幸啊。。还是自己考虑问题不全面。。好丢人。。我还去问TA了,结果问着自己悟出来了,有时候自己在那想不如给别人讲一下自己的思路呢,讲的时候比较详细,就能缕一遍,可能自己就明白了。。不过暴露了上次的问题。。。希望不要扣分呀~~~委屈


接下来是纠结了好几天的0.11下的程序了。。看了十三章之后就清楚了许多,但具体实现还是找了好多网上的资料。首先开辟共享内存有那个现成的get_free_page()函数(这个居然上次就用到了好神奇~),但是获得的是物理地址,而我们编程时才不关心这些呢,使用的是虚拟地址,所以需要找到程序中一段空闲的线性地址,减去基地址就是虚拟地址啦~同时也要把找到的线性地址映射到get_free_page()获得的物理地址上去。。。我是在shmget函数中实现找到空闲的物理内存,在shmat函数中找到空闲的线性地址,映射好,并且返回虚拟地址供用户操作。。但是思路清晰了问题又来了,还是总结一下吧~~

1、怎么获得空闲的线性地址呢?我们的程序的线性地址中有代码段,数据段,堆栈段,也有空闲的部分,参考书最后有一个get_base的函数,看来是返回一个地址的,但里面的参数我就不明白了。。于是我查了下task_struct的结构,本想知道ldt这个数组是干嘛的,结果收获更多啊~看红色的部分就好,不解释。。。


struct task_struct {
/*----------------------- these are hardcoded - don't touch -----------------------*/
       long state;       // 进程运行状态(-1不可运行,0可运行,>0以停止)
       long counter;  // 任务运行时间片,递减到0是说明时间片用完
       long priority;  // 任务运行优先数,刚开始是counter=priority
       long signal;     // 任务的信号位图,信号值=偏移+1
       struct sigaction sigaction[32];       //信号执行属性结构,对应信号将要执行的操作和标志信息
       long blocked;  // 信号屏蔽码
/*----------------------------------- various fields--------------------------------- */
       int exit_code;  // 任务退出码,当任务结束时其父进程会读取
unsigned long start_code,end_code,end_data,brk,start_stack;
              // start_code   代码段起始的线性地址
              // end_code     代码段长度 
              // end_data      代码段长度+数据段长度
              // brk             代码段长度+数据段长度+bss段长度
              // start_stack   堆栈段起始线性地址
              // end_data      代码段长度+数据段长度    

       long pid,father,pgrp,session,leader;      
              // pid       进程号
              // father   父进程号
              // pgrp     父进程组号
              // session 会话号
              // leader 会话首领
       unsigned short uid,euid,suid;
              // uid       用户标id
              // euid     有效用户id
              // suid     保存的用户id
       unsigned short gid,egid,sgid;
              // gid       组id
              // egid     有效组id
             // sgid     保存组id
long alarm; // 报警定时值 long utime,stime,cutime,cstime,start_time; // utime 用户态运行时间 
              // stime 内核态运行时间 
              // cutime 子进程用户态运行时间 
              // cstime 子进程内核态运行时间 
              // start_time 进程开始运行时刻 
       unsigned short used_math; // 标志,是否使用了387协处理器
/* ----------------------------------file system info-------------------------------- */ 
       int tty; // 进程使用tty的子设备号,-1表示没有使用 
       unsigned short umask; //文件创建属性屏蔽码 
       struct m_inode * pwd; // 当前工作目录的i节点 
       struct m_inode * root; // 根目录的i节点 
       struct m_inode * executable; // 可执行文件的i节点 
       unsigned long close_on_exec; // 执行时关闭文件句柄位图标志 
       struct file * filp[NR_OPEN]; // 进程使用的文件
/*------------------ldt for this task 0 - zero 1 - cs 2 - ds&ss -------------------*/ 
      struct desc_struct ldt[3]; // 本任务的ldt表,0-空,1-代码段,2-数据和堆栈段
/* ---------------------------------tss for this task ---------------------------------*/ 
       struct tss_struct tss; // 本任务的tss段};


其实不用get_base也可以,直接用current->start_code获得基地址就行,我试过了,是可以的。但是我有个疑问,就是获得的基地址能不能直接加上长度呢?获得的是否就是加上这段长度的地址?答案是肯定的,所以是我考虑复杂了,直接相加就好。。。

2、后来程序运行出现了死循环,又不知道怎么退出,于是我把consumer也在后台运行了,然后打印了好多信息在文件中。。原来是sem_open的时候忘记考虑第二次open了。。于是加上了判断,看名字是否已经存在,但名字是用户态和内核态的比较,还是要用get_fs_byte来获取,然后在比较的。。因为上次在一个程序里,只open了一次,就没有这个问题。由此观之,不要以为上次运行的对的就万事大吉了啊!!情况不同了,也不能抛弃人家sem.c啊。。

3、但是,死循环依然存在。。。并且数字特别规律,就是10——0一循环,难道。。。。果然!粗心的我居然两次都正好获得了基地址加上current->brk的空闲内存,于是。。。冲突了。。。所以我又往后移了shmid*1024*4的长度,shmid都不一样,这样就不会冲突了。。。

4、由于刚开始没有出现trying to free free page的提示,后来在别人的建议下将生产者生产的数目变成了40,于是就出现了。。经过多次实验,发现生产者生产数目少的话就还出现panic,大的话就不会。。想了想,在producer.c的最后打印了输出语句,结果是这样的:生产数目少就会先把生产者运行完,即文本中输出的第一行是生产者结束,接下来是消费者的打印内容。。而生产数目多的时候,生产者进程就一直没有结束了,因为一直在等待消费才能继续生产,没有结束则没有free_page,也就不会有panic了。。。

0 0
原创粉丝点击