Chinaunix首页 | 论坛 | 博客
  • 博客访问: 74773
  • 博文数量: 7
  • 博客积分: 1415
  • 博客等级: 上尉
  • 技术积分: 100
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-15 22:11
文章存档

2009年(2)

2008年(4)

2007年(1)

我的朋友

分类: C/C++

2008-01-08 14:40:57

进程存储空间的布局

低地址 → 高地址
正文区初始化数据区非初始化数据区堆→ | 虚地址空间 | ←栈命令行参数和环境变量
   
    在unix/linux系统中,当产生一个新进程时,内核要为进程分配存储空间(如上图)并且进行初始化操作
过程大致如下
        内核
      invoke
        fork() // 产生一个新进程,分配存储空间
       invoke
       exec("可执行程序的路径", ......)   // 有6个不同的exec函数,初始进程环境,调用main函数
        {
               .............
               读取程序指令到正文区
               读取初始化数据,在文件中获取它的值
               非初始化数据赋初值为0 或 NULL
               .............
               启动一个特殊例程从内核加载命令行参数环境变量
               exit(main(arc, argv));   // main函数被调用,程序正式开始执行
        }
   
    环境变量是操作系统传递给进程的一组字符串信息(如:PATH 等)。它存放在存储空间的高地址区域(见布局)----命令行参数和环境变量,它们都是一组字符串。命令行参数列表由char **argv指向,字符串个数由int argc指明,他们正是main函数的两个参数。环境变量和命令行参数类似,但它是一个全局变量char **environ,它指向环境表(环境字符串的集合)并以NULL空串结尾,所以它不需要argc这样的变量来指明环境字符串的个数。
    这块区域的初始化 是由exec函数中的特殊例程(见过程)完成,内核会根据字符串数来确定它的大小,所以这片区域的大小是固定的,这样做有一定的道理,因为命令行参数是不允许修改的。然而环境变量并没有这个限制,可以修改,删除,添加环境字符串,这就会导致空间不够
,采用的做法是向堆扩充。当添加一个新的环境字符串时,不但要在堆中申请一片空间存放字符串,而且要把整张环境表移到堆中,因为新字符串的指针要添加进环境表,所以这样操作起来会很麻烦(详细分析参见----《unix环境高级编程(第二版)》第七章 进程环境)。为什么不直接把环境变量安排在堆中,而是和命令行参数安排在同一区域呢?这样的设计很让人困惑,难道只是因为两者的相同点?如果把环境变量独立开,直接初始化在堆中,你将发现这一件非常愉快的事情。本人采取这种思路实现了环境变量的操作:
    在程序中是不提倡直接用environ变量进行操作的,所以系统为我们提供了下列特定的函数。我们看看它们的原型:( 环境表中各字串string的结构是:name=value )
1.  char *getenv(const char *name);
   
    根据name读取value,返回指向value的指针。
2.  int putenv(char *str);
        如果name不存在,直接把str添加进环境表中(所以这个函数有隐患),成功返回0,否则非0。
        此时环境表需要移到堆中,而原来环境表所占的空间不能被释放,因为那不是堆空间。这不是一种浪费吗?如果环境表开始就在堆中,我们只需realloc,就不会造成这样的浪费了。更糟的是,它还需要判断是不是第一次添加,如果是,则用malloc在堆中申请空间,并把环境表移过来;如果不止一次,说明环境表已在堆中,则realloc扩展空间----哎,真是自找麻烦。
3.  int setenv(const char *name, const char *value, int rewrite);
       将name和value构成的新字符串string(需要分配新的空间,这点和putenv不同)地址添加进环境表。如果name已经存在,则根据rewrite的值来决定是不是重写。成功返回0,否则非0。
       这个函数存在和putenv有一样的麻烦。并且新增的字符串也有类似的麻烦,如果发现name存在并且要重写时,需要比较新旧字符串的长度,更短就直接覆盖旧字符串;更长则需要移到堆中,旧字符串的空间一样不能被释放,更为准确的说,所有的字符串空间都不能被释放,因为不知道字符串究竟是在初始区域还是在堆中。如果一开始就在堆中初始,这一切不都简化了吗?
4.  int unsetenv(const char *name);
       删除环境表中名为name的字符串,成功返回0,否则非0。
       只在环境表中删除指针,字符串空间没被释放。Oh my god!
   
    分析对原型的说明,如果环境变量和命令行参数一起被初始化,即浪费空间,实现起来又比较麻烦。这个时候大家自然明白了把环境变量初始在堆中的好处了吧。下面是我
模拟实现此过程的代码,(initenv函数是模拟exec中特殊启动例程初始话环境变量的过程,不过初始化在堆中进行。相应的命名添加uc_为前缀):

文件1:
080103-uc_env_init.c (initenv的实现)

#include <string.h>
#include "080103-uc_env_funtion.c"

extern char **environ;    // "the environment variant from kernel"
char **uc_environ;        // "process's environment variant"

/*    init the environment */
void initenv()
{
    char    **uc_p, **en_p;

    // alloc the space of env tab
    if ((uc_environ = (char **)malloc(
        (size_t)get_envtab_size())) == NULL)
    {
        exit(0);
    }

    // copy all the env variants into the process's area
    // and fill uc_enbiron
    en_p = environ;
    uc_p = uc_environ;
    while (*en_p != NULL)
    {
        if ((*uc_p = (char *)malloc(
            strlen(*en_p) + 1)) == NULL)
        {
            exit(0);
        }
        strcpy(*uc_p, *en_p);
        en_p++;
        uc_p++;
    }
    *uc_p == NULL;
}


文件2:
080103-uc_env_funtion.c ( getenv.setenv,putenv,unsetenv的相应实现 )

extern    char **environ;
extern    char **uc_environ;

#include <stdlib.h>
#define    SP_SIZE    sizeof(char *)    // sizeof string pointer

/*    get the sizeof environment table
 *    return the sizeof env tab
 */

int get_envtab_size()
{
    char    **p;
    int        items;    // number of variants in the table

    p = environ;
    items = 0;
    while (*p != NULL)
    {
        items++;
        p++;
    }
    items++;    // contain the space of NULL
    
    return (items * SP_SIZE);
}


/*    expend the name with '='
 *    return:pointer of the name,false NULL
 */

char *expnam(const char *name)
{
    char    *newnam;
    
    if ((newnam = (char *)malloc
        (strlen((name + 1) + 1))) == NULL)
    {
        return NULL;
    }

    strcpy(newnam, name);
    strcat(newnam, "=");
    
    return newnam;
}

/*    make a new one to expend the env table
 *    return:new uc_environ,else NULL
 */

char **new_env()
{
    char    **old_p, **new_env_p, **new_p;
    int        old_size, new_size;

    old_p = uc_environ;
    old_size = get_envtab_size();
    new_size = old_size + (1 * SP_SIZE);
    if ((new_env_p = (char **)malloc(new_size)) == NULL)
    {// failed to alloc
        return NULL;
    }
    new_p = new_env_p;
    
    // copy the old table's values to the new one
    while (*old_p != NULL)
    {
        *new_p = *old_p;
        new_p++;
        old_p++;
    }
    *new_p = *old_p;
    new_p++;
    *new_p = NULL;
    old_p = uc_environ;
    uc_environ = new_env_p;
    free(old_p);

    return uc_environ;
}

/*    get the environment variant's value
 *    return:the name relate with value's pointer,false NULL
 */

char *uc_getenv(const char *name)
{
    char    **p, *buf_nam;
    int        nam_len;    // lenth of name
    char    *vp;        // point to value

    p = uc_environ;
    if ((buf_nam = expnam(name)) == NULL)
    {// expend '=' for name
        return NULL;
    }
    nam_len = strlen(buf_nam);
    
    while(*p != NULL)
    {
        if (strncmp(*p, buf_nam, nam_len) == 0)
        {
            vp = *p + nam_len;
            free(buf_nam);
            return vp;
        }
        p++;
    }
    
    free(buf_nam);
    return NULL;
}

/*    put the string's(name=value) pointer into the table
 *    overwrite if the name exist
 *    return:0 success,else not 0
 */

int uc_putenv(char *str)
{
    char     **p;
    int        nam_len, i;

    p = uc_environ;
    nam_len = 0;
    i = 0;

    // get the lenth of name
    while (*(str + i) != '\0')
    {
        nam_len++;
        if (*(str + i) == '=')
        {
            break;
        }

        i++;
    }
    if (*(str + i) == '\0')
    {// the error string
        return 1;
    }

    // is the name exist?
    while (*p != NULL)
    {
        if(strncmp(*p, str, nam_len) == 0)
        {// exist,just overwrite
            free(*p);
            *p = str;
            return 0;
        }
        p++;
    }
    
    // isn't exist,realloc the env tab and add the str to the tail
    if ((p = new_env()) == NULL)
    {// failer to realloc
        return 1;
    }
    while (*p != NULL)
    {// move to the tail
        p++;
    }
    *p = str; // add to the tail
    
    return 0;
}

/*    set the new environment variant (name=value)
 *    if name exist and rewrite is not 0,just rewrite it
 *    else rewrite is 0, just retuen
 *    else if name is not exist,add it as a new variant
 *    return:0 success,else not 0
 */

int uc_setenv(const char *name, const char *value, int rewrite)
{
    char     **p, *buf_nam;
    int        nam_len, len_str;

    p = uc_environ;
    if ((buf_nam = expnam(name)) == NULL)
    {// expend '=' for name
        return 1;
    }
    nam_len = strlen(buf_nam);            // get the lenth of name
    len_str = nam_len + strlen(value);    // get the lenth of string

    // is the name exist?
    while (*p != NULL)
    {    
        if(strncmp(*p, buf_nam, nam_len) == 0)
        {// exist
            // don't rewrite
            if (rewrite == 0)
            {
                free(buf_nam);
                return 0;
            }

            // rewrite it
            if (strlen(*p) < len_str)
            {// out of lenth, get a new space
                *p = realloc(*p, len_str + 1);
            }
            strcpy(*p, buf_nam);
            strcat(*p, value);
            free(buf_nam);
            return 0;
        }
        p++;
    }

    // isn't exist,realloc the env tab and add the str to the tail
    if ((p = new_env()) == NULL)
    {// failer to realloc
        free(buf_nam);
        return 1;
    }
    
    // add to the tail
    while (*p != NULL)
    {
        p++;
    }
    *p = realloc(*p, len_str + 1);
    strcpy(*p, buf_nam);
    strcat(*p, value);
    
    free(buf_nam);
    return 0;
}

/*    unset the variant which relate to the name
 *    return:0 success,else not 0
 */

int uc_unsetenv(const char *name)
{
    char    **p, *save_p, *buf_nam;
    int        nam_len;

    p = uc_environ;
    if ((buf_nam = expnam(name)) == NULL)
    {// expend '=' for name
        return 1;
    }
    nam_len = strlen(buf_nam);    
    while (*p != NULL)
    {
        if (strncmp(*p, buf_nam, nam_len) == 0)
        {// find the name

            save_p = *p;
            while (*p != NULL)
            {
                *p = *(p + 1);
                p++;
            }

            free(save_p);    // delete the name
            free(buf_nam);
            return 0;
        }
        p++;
    }

    // not find
    free(buf_nam);
    return 1;
}


阅读(1790) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~