【Linux】进程地址空间

int main()
{
	pid_t ret = fork();
	if(ret == 0)
	{  
	    int cnt = 0;
	    while(1)
	    {
	        printf("I am child, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",\
	                getpid(), getppid(), g_val, &g_val);
	        sleep(1);
	        ++cnt;
	        if(cnt == 5)
	        {
	            g_val = 200;
	            printf("child change g_val 100 -> 200 success\n");
	        }
	    }
	}
	else if(ret > 0)
	{ 
	    while(1)
	    {
	        printf("I am father, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",\
	                getpid(), getppid(), g_val, &g_val);                                                  
	        sleep(1);
	    }
	}
	else 
	{
		perror("fork");
        return 1;
    }

	return 0;
}

上面代码运行之后,竟然出现了相同的地址所打印的值并不相同的结果,这到底是为什么呢?
【Linux】进程地址空间_第1张图片
几乎所有的语言,如果有“地址”的概念,那么这个地址一定不是物理意义上的地址,而是虚拟地址!

进程地址空间的介绍

int un_g_val;                         
int g_val = 100;    
                                          
int main(int argc, char* argv[], char* env[])                         
{                                       
	char* p = "helloworld";             
	printf("read only addr: %p\n", p);    
	printf("code addr: %p\n", main);    
	                             
	static int un_val;    
	static int val = 1;                          
	printf("init static addr: %p\n", &val);    
	printf("uninit static addr: %p\n", &un_val);    
	                                                        
	printf("init gloval addr: %p\n", &g_val);    
	printf("uninit gloval addr: %p\n", &un_g_val);    
	     
	char* p1 = (char*)malloc(16);    
	char* p2 = (char*)malloc(16);    
	char* p3 = (char*)malloc(16);
	char* p4 = (char*)malloc(16);
	
	printf("heap addr: %p\n", p1);
	printf("heap addr: %p\n", p2);                                                                      
	printf("heap addr: %p\n", p3);                                               
	printf("heap addr: %p\n", p4);
	
	printf("stack addr: %p\n", &p1);
	printf("stack addr: %p\n", &p2);
	printf("stack addr: %p\n", &p3);
	printf("stack addr: %p\n", &p4);
	
	int i = 0;
	for(i = 0; i < argc; ++i)
	{
	    printf("argv[%d]: %p\n", i, argv[i]);
	}
	for(i = 0; env[i]; ++i)
	{
	    printf("env[%d]: %p\n", i, env[i]);
	}
	
	return 0;
}

【Linux】进程地址空间_第2张图片
进程地址空间是一种内核数据结构,他至少要有对各个区域的划分。
在这里插入图片描述

虚拟地址与物理地址的映射

对于虚拟地址到物理地址的映射是通过页表来转换的。
【Linux】进程地址空间_第3张图片
地址空间和页表(用户级)是每一个进程都私有一份的。
只要保证,每一个进程的页表,映射的是物理内存的不同区域,就能保证进程之间的独立性!
这里可以回答开始提出的问题,为什么相同的地址所打印的值却不相同。
子进程相当于是对父进程各项数据拷贝得到的,所以会很相似。当父进程和子进程需要对同一块内存空间进行写入操作时,为了保证进程的独立性,会在物理内存上给子进程开辟一块空间进行写入。所谓地址相同只是虚拟地址的相同,两个进程地址通过页表的转换到物理内存中却是不同的空间。
【Linux】进程地址空间_第4张图片
页表的对应关系能够被填充,是因为不仅操作系统内部会遵守进程地址空间,编译器同样要遵守。
编译器在编译代码的时候,会按照进程地址空间的样子使程序中每一个字段都具有一个虚拟地址。
【Linux】进程地址空间_第5张图片
当程序被加载进内存后,就成为了进程,进程既然是在内存中的,即物理空间中,就会有物理地址。
而程序在为变成进程前,内部就已经存在虚拟内存地址,这些虚拟内存地址会和物理地址进行映射,这样页表的映射关系就能够实现。

为什么要有进程地址空间

主要有三点原因:

  1. 可以有效的对物理内存进行保护。
  2. 内存管理模块和进程管理模块在系统层面上可以实现解耦合。
  3. 进程地址空间和页表的存在,可以使内存分布有序化。

你可能感兴趣的:(LInux,linux,运维)