Linux环境编程之进程(二):程序的存储空间布局

引言:

一个写好的程序一般要存放在存储器中,那么程序中的代码、数据等各部分,是如何有规律的存放在存储器中的呢?

(一)

一个存储的程序可分为五部分:正文段、初始化数据段、非初始化数据段、栈、堆。其典型的存储安排如下图:

正文段:这是由CPU执行的机器指令的部分。通常,正文段是可共享的,所以即使是频繁执行的程序在存储器中也只需要一个副本,另外正文段常常是只读的,以防止程序由于意外而修改其自身的指令。

初始化数据段:通常称为数据段,它包含了程序中需要明确地赋初值的变量。

非初始化数据段:通常称为bss段。在程序开始执行之前,内核将此段中的数据初始化为0或空指针。

栈:自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次调用函数是,其返回地址以及调用者的环境信息都存放在栈中。然后,最近被调用的函数在栈上为其自动和临时变量分配存储空间。

堆:通常在堆中进行动态存储分配。

Linux中使用size命令可以查看文件的正文段、数据段和bss段的长度。

(二)

存储器分配:一般用于存储空间动态分配的函数有malloc/calloc/realloc。

1、malloc:分配指定字节数的存储区。此存储区中的初始化值不确定。

2、calloc:为指定数量的对象分配存储空间。该空间中的每一位都初始化为0。

3、realloc:更改以前分配区的长度(增加或减少)。

动态内存管理的优势:能够动态的满足对内存空间的需求。

(三)

共享库:共享库使得可执行文件中不再需要包含公用的库例程,而只需要在所有进程都可引用的存储区中维护这种库例程的一个副本。程序第一次执行或第一次调用某个库函数时,用动态链接方法将程序与共享库函数相链接。这减少了每个可执行文件的长度,但增加了一些运行时间开销。共享库的另一个优点是可以用库函数的新版本代替老版本,而不需要对使用该库的程序重新连接编辑。

Linux环境编程之进程(二):程序的存储空间布局,布布扣,bubuko.com

时间: 2024-10-12 17:01:35

Linux环境编程之进程(二):程序的存储空间布局的相关文章

Linux环境编程之进程(一):main函数调用、进程终止以及命令行参数和环境表

(一)main函数调用 main函数作为程序运行时的入口函数,它是如何被调用的呢?首先必须清楚一点,main函数也是一个函数,它只有被调用才能够执行.其实,在执行可执行程序时,在调用main函数之前,内核会先调用一个特殊的启动例程,将此启动例程作为可执行程序的起始地址.启动例程是如何作为可执行程序的起始地址的?这是由链接编译器设置的,而链接编译器则是由C编译器(如gcc编译器)调用的.启动例程作为可执行程序的起始地址主要做哪些工作呢?启动例程从内核取得命令行参数和环境变量值,以此来为main函数

Linux环境编程之进程(七):守护进程

守护进程也是一种进程,它由如下特性: 1.生存期较长,在系统自举时启动,仅在系统关闭时终止. 2.没有控制终端,在后台运行. 系统中有很多守护进程,它们执行日常事务活动.如日志进程syslogd.web服务器httpd.邮件服务器sendmail和数据块服务器mysqld等.大多数守护进程都是以超级用户(用户ID为0)特权运行.没有一个守护进程具有控制终端,其终端设置为问号(?),终端前台进程组ID设置为-1.内核守护进程以无控制终端方式启动.用户层守护进程缺少控制终端可能是守护进程调用了set

Linux环境编程之信号(二):不可靠信号、中断的系统调用、可重入函数

(一)不可靠信号 对前面说的信号,是不可靠的,不可靠指的是信号可能会丢失:一个信号发生了,但进程却可能一直不知道这一点.另外,进程对信号的控制能力有限,只能捕捉信号或忽略它.有时用户希望通知内核阻塞一个信号:不要忽略它,在其发生时记住它,然后在进程做好准备时再通知它.这种阻塞信号的能力并不具备. 之前的版本中村咋一个问题:在进程每次接到信号对其进行处理时,随即将该信号动作复位为默认值.另一个问题是,在进程不希望某种信号发生时,它不能关闭该信号.进程能做的一切就是忽略该信号. (二)中断的系统调用

Linux环境编程之进程(四):创建新进程、执行程序和进程终止

引言: 对于每个进程,都有一个非负整数表示的唯一进程ID.虽然进程的ID是唯一的,但却是可重用的.系统中有一些专用的进程.如ID为0的进程通常是调度进程,也成交换进程或系统进程(它是内核进程).进程ID为1通常是init进程,它是一个普通的用户进程.一些与进程ID有关的函数: #include <unistd.h> pid_t getpid(void);   //返回值:调用进程的进程ID pit_t getppid(void); //返回值:调用进程的父进程ID uid_t getuid(v

Linux环境编程之进程(五):竞争条件以及exec函数

(一) 当多个进程企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,就认为它们发生了竞争关系.避免竞争的条件,给出apue上的一个代码吧: #include "apue.h" static void charatatime(char *); int main(void) { pid_t pid; TELL_WAIT(); /*set things up for TELL_XXX & WAIT_XXX*/ if((pid == fork()) < 0){ e

Linux环境编程之进程(三):函数间跳转

引言:在编写程序时,经常在函数内部使用goto语句来跳转,从而进行出错处理,那么如果想要在函数之间进行跳转该怎么做呢?使用setjmp和longjmp函数. 给出示例程序: #include <stdio.h> #include <stdlib.h> #include <setjmp.h> static void f1(int, int, int, int); static void f2(void); static jmp_buf jmpbuffer; static

Linux环境编程之进程(六):进程组

进程组 每个进程除了有一个进程ID之外,还属于一个进程组.进程组是一个或多个进程的集合.每个进程组有一个唯一的进程组ID.进程组ID类似于进程ID--它是一个整数,并可存放在pid_t数据类型中.函数getpgrp返回调用进程的进程组ID. 每个进程组都可以有一个组长进程.组长进程的标识是,其进程组ID等于其进程ID.组长进程可以创建一个进程组,创建该组中的进程,然后终止.只要在某个进程组中有一个进程存在,则进程组就存在,这与其组长进程是否终止无关.从进程组创建开始到其中最后一个进程离开为止的时

Linux环境编程之同步(二):条件变量

MainActivity如下: package cn.testprogressdialog; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.os.

LINUX环境编程之进程控制(上)

一.进程标识 每个进程都有一个非负整型表示的唯一进程ID.虽然该id是唯一的,但是进程ID是可复用的.当一个进程终止后,其进程id就成为复用的候选者. 系统中有一些专用进程,但是具体细节随实现而不同.ID为0的进程通常是调度进程,常常被称为交换进程.该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被成为系统进程.进程ID 1通常是init进程,在自举过程结束时有内核调用.init进程绝不会终止,他是一个普通用户的用户进程(与交换进程不同,他不是内核中的系统进程),但是它以超级用户权限运行