linux 进程内存基础
一、进程虚拟空间
linux所有的进程都运行在自己的虚拟地址空间中,每个进程都有自己的内存地址。在32位系统中,4GB的进程地址空间被分为用户空间和内核空间两个部分。用户空间占用0-3GB,内核空间范围是3-4GB。
每个进程都会占用如下的数据段:
1. 代码段
用于保存可执行文件的操作指令和程序定义的常量(const,字符常量等)。为了防止代码在运行时候被其他进程修改,代码段只读。多进程也可以共享代码段。
2. 数据段
数据段紧挨着代码段,分为初始化数据段和未初始化数据段(BSS)。此段中存放初始化的全局变量和静态变量,未初始化的全局变量则位于bss段。
3. 堆栈段
堆中存放的是malloc或new分配的内存空间。堆的位置与数据段相邻。
栈中存放的是函数中创建的临时变量,传递给函数的参数也将被保存在栈中。、
具体分析见下例子:
#include <stdio.h> #include <malloc.h> #include <unistd.h> int bss_var; int data_var0 = 1; int no_data_init = 8; int main( int argc, char** argv ) { printf ("below are addresses of types of process's mem\n"); printf ("Text location\n"); printf ("\tAddress of main( Code Segment):%p\n", main); printf ("--------------------\n"); int stack_var0 = 3; printf ("Stack Location:\n"); printf ("\tInitial end of stack:%p\n", &stack_var0 ); int stack_var1 = 3; printf ("\tnew end of stack:%p\n", &stack_var1 ); printf ("--------------------\n"); printf ("Data Location.\n"); static int data_var1 = 4; printf ("\tAddress of data_var( Data Segment ):%p\n", &data_var0 ); printf ("\tNew end of data_var( Data Segment ):%p\n", &data_var1 ); printf ("\tAnother global initial data_var( Data Segment ):%p\n", &no_data_init ); printf ("--------------------\n"); printf ("BSS Location:\n"); printf ("\tAddress of bss_var: %p\n", &bss_var ); printf ("--------------------\n"); char *b = sbrk( ( ptrdiff_t )0 ); printf ("Heap Location:\n"); printf ("\tInitial end of heap:%p\n", b ); return 0; }
below are addresses of types of process's mem Text location Address of main( Code Segment):0x80484ac -------------------- Stack Location: Initial end of stack:0xbfff7de8 new end of stack:0xbfff7de4 -------------------- Data Location. Address of data_var( Data Segment ):0x804a028 New end of data_var( Data Segment ):0x804a030 Another global initial data_var( Data Segment ):0x804a02c -------------------- BSS Location: Address of bss_var: 0x804a038 -------------------- Heap Location: Initial end of heap:0x89fe000
二、进程生成
进程的产生有两种方式,一个是调用系统函数fork():
fork的返回值如果小于0,则创建进程失败,如果返回值为0,则为子进程,否则为父进程。
另外一个为vfork(),返回值同fork。
ps: fork()和vfork()的区别主要在,fork会复制父进程的所拥有的资源(如进程环境,内存,堆栈等)。vfork函数则不会复制父进程的相关资源,父子进程共享地址空间。子进程对虚拟空间的修改,都是在修改父进程的虚拟空间。
#include <stdio.h> #include <unistd.h> #include <sys/types.h> int g_var = 0; int g_var_vfork = 10; int main() { pid_t pid; pid_t pid_vfork; int var = 1; int var_vfork = 11; printf ("--------------------\n"); printf ("fork process start\n"); printf ("process id: %ld\n", ( long )getpid() ); printf ("before execute the fork system call, g_var=%d var=%d\n", g_var, var ); if ( ( pid = fork() ) < 0 ) { perror( "Cannot create a new process.\n" ); return 1; } else if ( pid == 0 ) { g_var++; var++; printf ("process id : %ld, g_var=%d var=%d\n", ( long )getpid(), g_var, var ); _exit( 0 ); } printf ("process id :%ld, g_var=%d var=%d\n", ( long )getpid(), g_var, var ); printf ("fork process stop.\n"); printf ("--------------------\n"); printf ("vfork process start\n"); printf ("process id :%ld\n", ( long )getpid() ); printf ("before vfork system call, g_var_vfork=%d var_vfork=%d\n", g_var_vfork, var_vfork ); if ( ( pid_vfork = vfork() ) < 0 ) { perror( "Cannot create a new process.\n" ); return 1; } else if ( pid_vfork == 0 ) { g_var_vfork++; var_vfork++; printf ("process id :%ld, g_var_vfork=%d, var_vfork=%d\n", ( long )getpid(), g_var_vfork, var_vfork ); _exit( 0 ); } printf ("process id :%ld, g_var_vfork=%d, var_vfork=%d\n", ( long )getpid(), g_var_vfork, var_vfork ); printf ("vfork process stop.\n"); return 0; }
-------------------- fork process start process id: 2496 before execute the fork system call, g_var=0 var=1 process id :2496, g_var=0 var=1 fork process stop. -------------------- vfork process start process id :2496 before vfork system call, g_var_vfork=10 var_vfork=11 process id : 2497, g_var=1 var=2 process id :2498, g_var_vfork=11, var_vfork=12 process id :2496, g_var_vfork=11, var_vfork=12 vfork process stop.
三、exec函数组
exec函数族用于对新产生进程的数据段,代码段,堆栈段进行替换。进程调用exec函数族中的函数后,其代码段会被替换成新的代码段。同时,获取新程序的数据段和堆栈段,进程的进程号保持不变。