地址映射与共享
来源:互联网 发布:java process 编辑:程序博客网 时间:2024/05/16 15:42
先说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了。。。
- 地址映射与共享
- 地址映射与共享
- 地址映射与共享
- 地址映射与共享
- 操作系统原理与实践8-地址映射与共享
- 哈工大操作系统实验 5 地址映射与共享
- 哈工大操作系统实验 5 地址映射与共享
- GPU 共享内存地址映射方式
- 进程通信之内存地址映射与共享,同时如何在Linux0.11下实现共享内存
- stm32之重映射与地址映射
- Linux地址映射(1)--线性映射与非线性映射
- Linux地址映射--线性映射与非线性映射
- Linux地址映射(1)--线性映射与非线性映射
- Linux地址映射(1)--线性映射与非线性映射
- Linux地址映射(1)--线性映射与非线性映射
- 把其它地址的共享目录映射到本地
- tomcat ip地址访问项目 映射共享目录
- tomcat ip地址访问项目 映射共享目录
- java中如何将文本流保存在字节数组中
- CSS布局的一个例子
- Remove Duplicates from Sorted List II
- 苹果发布iOS 8.2 和 Xcode 6.2 Beta版
- sqlite3创建数据库--mac终端界面
- 地址映射与共享
- JavaScript数组常用操作技巧汇总
- wget报File name too long
- oralce逗号分割变多行 Oracle中REGEXP_SUBSTR函数
- fmdb 数据库升级1-----增加表字段
- Hadoop RPC实现NIO通信范例
- 【实例】C++调用webservice接口
- android的架构图
- listjson