父进程和父子进程的内存关系调度占不占内存

1463人阅读
----------Linux网络/并发编程(11)
#include&stdio.h&
#include&string.h&
#include&stdlib.h&
#include&unistd.h&
void main()
char str[6]=&hello&;
pid_t pid=fork();
if(pid==0)
str[0]='b';
printf(&子进程中str=%s\n&,str);
printf(&子进程中str指向的首地址:%x\n&,(unsigned int)str);
printf(&父进程中str=%s\n&,str);
printf(&父进程中str指向的首地址:%x\n&,(unsigned int)str);
子进程中str=bello
子进程中str指向的首地址:bfdbfc06
父进程中str=hello
父进程中str指向的首地址:bfdbfc06
这里就涉及到物理地址和逻辑地址(或称虚拟地址)的概念。
从逻辑地址到物理地址的映射称为地址重定向。分为:
静态重定向--在程序装入主存时已经完成了逻辑地址到物理地址和变换,在程序执行期间不会再发生改变。
动态重定向--程序执行期间完成,其实现依赖于硬件地址变换机构,如基址寄存器。
逻辑地址:CPU所生成的地址。CPU产生的逻辑地址被分为 :p (页号) 它包含每个页在物理内存中的基址,用来作为页表的索引;d
(页偏移),同基址相结合,用来确定送入内存设备的物理内存地址。
物理地址:内存单元所看到的地址。
用户程序看不见真正的物理地址。用户只生成逻辑地址,且认为进程的地址空间为0到max。物理地址范围从R+0到R+max,R为基地址,地址映射-将程序地址空间中使用的逻辑地址变换成内存中的物理地址的过程。由内存管理单元(MMU)来完成。
fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。在fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。
fork时子进程获得父进程数据空间、堆和栈的复制,所以变量的地址(当然是虚拟地址)也是一样的。
每个进程都有自己的虚拟地址空间,不同进程的相同的虚拟地址显然可以对应不同的物理地址。因此地址相同(虚拟地址)而值不同没什么奇怪。
具体过程是这样的:
fork子进程完全复制父进程的栈空间,也复制了页表,但没有复制物理页面,所以这时虚拟地址相同,物理地址也相同,但是会把父子共享的页面标记为“只读”(类似mmap的private的方式),如果父子进程一直对这个页面是同一个页面,知道其中任何一个进程要对共享的页面“写操作”,这时内核会复制一个物理页面给这个进程使用,同时修改页表。而把原来的只读页面标记为“可写”,留给另外一个进程使用。
这就是所谓的“写时复制”。正因为fork采用了这种写时复制的机制,所以fork出来子进程之后,父子进程哪个先调度呢?内核一般会先调度子进程,因为很多情况下子进程是要马上执行exec,会清空栈、堆。。这些和父进程共享的空间,加载新的代码段。。。,这就避免了“写时复制”拷贝共享页面的机会。如果父进程先调度很可能写共享页面,会产生“写时复制”的无用功。所以,一般是子进程先调度滴。
假定父进程malloc的指针指向0x, fork 后,子进程中的指针也是指向0x,但是这两个地址都是虚拟内存地址
(virtual memory),经过内存地址转换后所对应的 物理地址是不一样的。所以两个进城中的这两个地址相互之间没有任何关系。
(注1:在理解时,你可以认为fork后,这两个相同的虚拟地址指向的是不同的物理地址,这样方便理解父子进程之间的独立性)
(注2:但实际上,linux为了提高 fork 的效率,采用了 copy-on-write 技术,fork后,这两个虚拟地址实际上指向相同的物理地址(内存页),只有任何一个进程试图修改这个虚拟地址里的内容前,两个虚拟地址才会指向不同的物理地址(新的物理地址的内容从原物理地址中复制得到))
转自:http://blog.csdn.net/xy/article/details/
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:223839次
积分:7354
积分:7354
排名:第2978名
原创:536篇
转载:11篇
评论:15条
(10)(3)(4)(5)(9)(3)(5)(3)(9)(27)(38)(60)(34)(88)(3)(65)(8)(40)(31)(47)(55)为什么系统内存占用明显大于所有进程内存使用的和? - 知乎24被浏览2718分享邀请回答9添加评论分享收藏感谢收起41 条评论分享收藏感谢收起如果你对代码段、数据段、栈、堆存放哪些数据还不是很清楚,请先看我写和。
有时会出现父子进程变量的地址一样,但值不一样。看下面代码:
#include&stdio.h&
#include&string.h&
#include&stdlib.h&
#include&unistd.h&
char str[4]="asd";
pid_t pid=fork();
if(pid==0){
str[0]='b';
printf("子进程中str=%s\n",str);
printf("子进程中str指向的首地址:%x\n",(unsigned int)str);
printf("父进程中str=%s\n",str);
printf("父进程中str指向的首地址:%x\n",(unsigned int)str);
子进程中str=bsd子进程中str指向的首地址:bfc224dc父进程中str=asd父进程中str指向的首地址:bfc224dc
这里就涉及到物理地址和逻辑地址(或称虚拟地址)的概念。
从逻辑地址到物理地址的映射称为地址重定向。分为:
静态重定向--在程序装入主存时已经完成了逻辑地址到物理地址和变换,在程序执行期间不会再发生改变。
动态重定向--程序执行期间完成,其实现依赖于硬件地址变换机构,如基址寄存器。
逻辑地址:CPU所生成的地址。CPU产生的逻辑地址被分为 :p (页号) 它包含每个页在物理内存中的基址,用来作为页表的索引;d (页偏移),同基址相结合,用来确定送入内存设备的物理内存地址。
物理地址:内存单元所看到的地址。
用户程序看不见真正的物理地址。用户只生成逻辑地址,且认为进程的地址空间为0到max。物理地址范围从R+0到R+max,R为基地址,地址映射-将程序地址空间中使用的逻辑地址变换成内存中的物理地址的过程。由内存管理单元(MMU)来完成。
& fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了&写时复制&技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。在fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。&&&&&& fork之后内核会通过将子进程放在队列的前面,以让子进程先执行,以免父进程执行导致写时复制,而后子进程执行exec系统调用,因无意义的复制而造成效率的下降。
fork时子进程获得父进程数据空间、堆和栈的复制,所以变量的地址(当然是虚拟地址)也是一样的。
每个进程都有自己的虚拟地址空间,不同进程的相同的虚拟地址显然可以对应不同的物理地址。因此地址相同(虚拟地址)而值不同没什么奇怪。具体过程是这样的:fork子进程完全复制父进程的栈空间,也复制了页表,但没有复制物理页面,所以这时虚拟地址相同,物理地址也相同,但是会把父子共享的页面标记为&只读&(类似mmap的private的方式),如果父子进程一直对这个页面是同一个页面,知道其中任何一个进程要对共享的页面&写操作&,这时内核会复制一个物理页面给这个进程使用,同时修改页表。而把原来的只读页面标记为&可写&,留给另外一个进程使用。这就是所谓的&写时复制&。正因为fork采用了这种写时复制的机制,所以fork出来子进程之后,父子进程哪个先调度呢?内核一般会先调度子进程,因为很多情况下子进程是要马上执行exec,会清空栈、堆。。这些和父进程共享的空间,加载新的代码段。。。,这就避免了&写时复制&拷贝共享页面的机会。如果父进程先调度很可能写共享页面,会产生&写时复制&的无用功。所以,一般是子进程先调度滴。
子进程复制了父进程的行缓冲
#include &stdio.h&
#include &unistd.h&
int main(void)
printf("one");
printf("two/n");
首先要清楚stdin和stdout都是行缓冲,stderr是无缓冲。"one"存放在父进程的行缓冲里,子进程复制了父进行程的行缓冲,所以子进程也会打印输出"one"。
#include &stdio.h&
#include &unistd.h&
int main(void)
printf("one");
fflush(stdout);
printf("two/n");
想消除行缓冲所带来的困扰,方法如下
1、输出数据后加换行符
2、使用fflush之类的函数强制刷新
3、使用setbuf,setvbuf函数设置缓冲区大小
4、使用非缓冲的的流,如stderr.
阅读(...) 评论() &当前位置:&&技术问答>
求助:父进程被kill掉以后,子进程在任务表中所占内存是怎样释放的?
&&&&来源:&互联网& 发布时间:
&&&&本文导语:&
哪位大虾能帮下忙?!
linux中,通常父进程会等待子进程终止以后,释放子进程在任务数据结构中所占的内存以及任务数组中的指针项。但是,如果子进程还没终止,就kill掉了父进程。等子进程终止后,在任务表中...
哪位大虾能帮下忙?!
linux中,通常父进程会等待子进程终止以后,释放子进程在任务数据结构中所占的内存以及任务数组中的指针项。但是,如果子进程还没终止,就kill掉了父进程。等子进程终止后,在任务表中所占内存是如何释放的?
你的理解有误,子进程中的资源不是由创建它的父进程来释放的,而是由kernel来完成。
当一个子进程的父进程退出后,init进程就会成为该子进程的父进程,当该子进程退出是init进程会调用wait来
释放该子进程在进程表中占用的资源。
父进程被kill掉后,子进程就交给init进程来管理了
Unix环境高级编程说道
The final condition to consider is this: what happens when a process that has been inherited by init terminates? Does it become a zombie? The answer is "no," because init is written so that whenever one of its children terminates, init calls one of the wait functions to fetch the termination status. By doing this, init prevents the system from being clogged by zombies. When we say "one of init's children," we mean either a process that init generates directly (such as getty, which we describe in Section 9.2) or a process whose parent has terminated and has been subsequently inherited by init
我们学的《unix/linux高级编程》中,说如果父进程在子进程中如果提前退出的话,那么子进程
也不会变成“孤儿”,他们还受到“国家”保护,就是init(1)进程!
父亲死后, 儿子就是孤儿, 被init领养, 一切都由init负责!
您可能感兴趣的文章:
本站()旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。本站()站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。
相关文章推荐
特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!
(C)2012-,E-mail:www_169it_(请将#改为@)}

我要回帖

更多关于 linux 进程占用内存 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信