进程通信之内存地址映射与共享,同时如何在Linux0.11下实现共享内存
2012-10-27 05:38
597 查看
Mr.Peach这一周经历了许多磨难,所以才在deadline前2天写了这篇博文,关于进程通信时,共享内存的相关理解。
这一切都要从哈工大的第五次操作系统实验说起,它的任务就是将第四次的消费者生产者问题改造改造,进程间的通信方式从文件转为内存。
内存管理有分段式管理,页式管理,也有段式地址,段基址+段偏移得线性地址,线性地址可以得到页目录号,页号,页内偏移地址,页号加上页偏移得物理地址。Linux就是这么解析一个逻辑地址的。
那站在unix系统角度来看内存共享,就要从它的系统调用说起了,和以前一样,我们需要10个sizeof(int)大小的区域来作为 producer 和 consumer 两个进程的共享空间让他们进行情感交流。按常理,就有以下几步了:
打开共享内存区域:相应调用是 id = shmget(KEY, BUFFER_SIZE, IPC_CREAT); 这里 shmget会创建一块新的IPC对象的共享内存,KEY为唯一标定IPC对象的标记,这样在不同进程间,可以通过唯一的KEY来指定相同的共享内存块,创建空间BUFFER_SIZE即为 10个int变量的大小,创建方式为IPC_CREAT,无则创建,有则打开。与以前的文件共享方式无异。id
为共享内存标识符,我们可以通过它来完成以后的映射。
KEY的生成,可以通过调用 ftok() 实现。 like: key_t KEY = ftok("/", 'A'); 第一个参数是该进程有权限访问的目录节点,第二个参数是一个介于0-255的数,可以简单用ascii来代替。通过这两个参数,能生成唯一一个KEY用于标记共享内存区域。
将共享内存区映射到本进程内存空间,任何想用共享内存区域的进程都需要进行映射。 使用 head = shmat("id", NULL, 0); 来进行映射。通过id确定哪块儿共享内存,NULL意味针对共享内存首址,我们希望系统能够自动给出,因此我们不指定,给null,0意味对于此空间,进程可读可写。head 是 一个 int* ,映射成功后,head即是共享内存的首址,通过它加上偏移,就能方便只有的读写共享内存区域。such as: head[3]=4;// 共享内存的第四个位置值为4.
这几个函数代替原先文本共享时,文件读写的几个函数即可。关于生产,消费的流程,还是以前的思路。挺简单无聊的。。。。。
另外需要注意的是, 运行有共享内存的进程时,需要有root权限,可通过 sudo ./producer & sudo ./consumer.......不然会报segmentation fault
至于Linux0.11下共享内存系统调用的实现。需要理解下。
以前实现信号量时,我们限定了可用信号量的上限,这里也是这样的,共享内存区域相当于一个页面,我们得限定上限有几个空闲页面可作为共享内存区域。
对于每个共享内存区域,都有一个唯一的上面谈到的key,有是否可用的信号,当然还有它的物理地址了,毕竟它需要将共享的东西放在真实地址上,所以要给出实际物理地址的首地址。至于共享区域的长度限制,大家如果看书的话,知道凡是申请共享内存区域,系统都会给出4KB的空间来,不管你实际申请多少。因为那是页面的大小哇,怎么能随便变呢。因此,没有变量记录长度限制。
这样,就可以在unistd.h中确定shm_t结构体的具体内容了。
现在聊聊怎么在kernel中实现shm的两个调用 shmget() shmat(). 直观的,需要一个物理页,需要一个线性地址,然后把他们映射起来。之后把段偏移地址作为shmat的返回值就行了。
首先需要一个空白物理页,这个物理空白页的地址是存在 sem_t->addr中的。以为最后映射时使用。可以
其次需要一个线性地址,这里从数据段中偷一块儿出来。
+ di。
做线性地址与物理页的映射。
所以shmget基本就是初始化 shm_t 结构体的,shmat就是做各种地址相关操作的。 按上面思路实现就好了。debug时可以通过多打印地址来跟踪。
有时我希望与我交流更多的是各式各样的人和地域风情的事儿,而不是coding,但也仅是有时。
这一切都要从哈工大的第五次操作系统实验说起,它的任务就是将第四次的消费者生产者问题改造改造,进程间的通信方式从文件转为内存。
内存管理有分段式管理,页式管理,也有段式地址,段基址+段偏移得线性地址,线性地址可以得到页目录号,页号,页内偏移地址,页号加上页偏移得物理地址。Linux就是这么解析一个逻辑地址的。
那站在unix系统角度来看内存共享,就要从它的系统调用说起了,和以前一样,我们需要10个sizeof(int)大小的区域来作为 producer 和 consumer 两个进程的共享空间让他们进行情感交流。按常理,就有以下几步了:
打开共享内存区域:相应调用是 id = shmget(KEY, BUFFER_SIZE, IPC_CREAT); 这里 shmget会创建一块新的IPC对象的共享内存,KEY为唯一标定IPC对象的标记,这样在不同进程间,可以通过唯一的KEY来指定相同的共享内存块,创建空间BUFFER_SIZE即为 10个int变量的大小,创建方式为IPC_CREAT,无则创建,有则打开。与以前的文件共享方式无异。id
为共享内存标识符,我们可以通过它来完成以后的映射。
KEY的生成,可以通过调用 ftok() 实现。 like: key_t KEY = ftok("/", 'A'); 第一个参数是该进程有权限访问的目录节点,第二个参数是一个介于0-255的数,可以简单用ascii来代替。通过这两个参数,能生成唯一一个KEY用于标记共享内存区域。
将共享内存区映射到本进程内存空间,任何想用共享内存区域的进程都需要进行映射。 使用 head = shmat("id", NULL, 0); 来进行映射。通过id确定哪块儿共享内存,NULL意味针对共享内存首址,我们希望系统能够自动给出,因此我们不指定,给null,0意味对于此空间,进程可读可写。head 是 一个 int* ,映射成功后,head即是共享内存的首址,通过它加上偏移,就能方便只有的读写共享内存区域。such as: head[3]=4;// 共享内存的第四个位置值为4.
这几个函数代替原先文本共享时,文件读写的几个函数即可。关于生产,消费的流程,还是以前的思路。挺简单无聊的。。。。。
另外需要注意的是, 运行有共享内存的进程时,需要有root权限,可通过 sudo ./producer & sudo ./consumer.......不然会报segmentation fault
至于Linux0.11下共享内存系统调用的实现。需要理解下。
以前实现信号量时,我们限定了可用信号量的上限,这里也是这样的,共享内存区域相当于一个页面,我们得限定上限有几个空闲页面可作为共享内存区域。
对于每个共享内存区域,都有一个唯一的上面谈到的key,有是否可用的信号,当然还有它的物理地址了,毕竟它需要将共享的东西放在真实地址上,所以要给出实际物理地址的首地址。至于共享区域的长度限制,大家如果看书的话,知道凡是申请共享内存区域,系统都会给出4KB的空间来,不管你实际申请多少。因为那是页面的大小哇,怎么能随便变呢。因此,没有变量记录长度限制。
这样,就可以在unistd.h中确定shm_t结构体的具体内容了。
现在聊聊怎么在kernel中实现shm的两个调用 shmget() shmat(). 直观的,需要一个物理页,需要一个线性地址,然后把他们映射起来。之后把段偏移地址作为shmat的返回值就行了。
首先需要一个空白物理页,这个物理空白页的地址是存在 sem_t->addr中的。以为最后映射时使用。可以
temp = get_free_page(); shm[i]->addr = temp;
其次需要一个线性地址,这里从数据段中偷一块儿出来。
static unsigned long change_ldt(unsigned long text_size,unsigned long * page) { /*其中text_size是代码段长度,从可执行文件的头部取出,page为参数和环境页*/ unsigned long code_limit,data_limit,code_base,data_base; int i; code_limit = text_size+PAGE_SIZE -1; code_limit &= 0xFFFFF000; //code_limit为代码段限长=text_size对应的页数(向上取整) data_limit = 0x4000000; //数据段限长64MB code_base = get_base(current->ldt[1]); data_base = code_base; //数据段基址=代码段基址 set_base(current->ldt[1],code_base); set_limit(current->ldt[1],code_limit); set_base(current->ldt[2],data_base); set_limit(current->ldt[2],data_limit); __asm__("pushl $0x17\n\tpop %%fs":: ); data_base += data_limit; //从数据段的末尾开始 for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) { //向前处理 data_base -= PAGE_SIZE; //一次处理一页 if (page[i]) put_page(page[i],data_base); //建立线性地址到物理页的映射 } return data_limit; //返回段界限 }这是修改段标识的函数,可以看出来,数据段与代码段基址相同,其次数据段长度是 0x4000000. 我们可以从下往上,抽取一部分地址作为共享内存区域的线性地址空间,一个页面大小是一个 PAGE_SIZE, 如果我们在unistd中定义了MAX个共享内存区域,那可以将 di = 0x4000000 - MAX*PAGE_SIZE 作为段偏移地址。而基址是 data_base = get_base(current->ldt[2],那共享内存区域的线性地址就是 data_base
+ di。
做线性地址与物理页的映射。
put_page(shm[i]->addr, data_base+di);//将空白物理页映射到线性地址。把di返回。进程就能通过di来读写共享内存区域了。
所以shmget基本就是初始化 shm_t 结构体的,shmat就是做各种地址相关操作的。 按上面思路实现就好了。debug时可以通过多打印地址来跟踪。
有时我希望与我交流更多的是各式各样的人和地域风情的事儿,而不是coding,但也仅是有时。
相关文章推荐
- Linux进程通信---共享内存 代码实现
- linux下的多进程通信(IPC)原理及实现方案(管道、队列、信号量、共享内存)
- UNIX环境高级编程学习之第十五章进程间通信 - 两个进程通过映射普通文件实现共享内存通信
- Linux\Unix IPC进程通信实例分析(一):共享内存通信---文件映射mmap方式
- Linux下共享内存通信实现A进程死循环输出A后被C进程处理输出C
- linux下通过共享内存在进程之间实现通信(system V)
- linux下进程间共享内存通信的问题
- Linux进程间的通信--内存共享
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped
- Linux 进程通信 内存映射技术
- linux 进程通信之共享内存
- window中进程间如何通信vc++技术 (共享内存)
- window中进程间如何通信vc++技术 (共享内存)
- window中进程间如何通信vc++技术 (共享内存)
- Linux C程序练习(4)进程通信之信号量、共享内存
- linux进程通信-共享内存
- linux 进程通信之 共享内存
- Linux进程通信-共享内存
- .net4.0多进程间共享内存实现通信(VB.Net)
- Linux 基于IPC机制实现进程间的共享内存处理