Sunday, December 13, 2009

环境变量

到底什么是环境变量, 系统管理上常常会关联上环境变量。平时用就会用。但是没有对它有一个明确的定义。这有碍工作中对它的深刻使用。

带来的疑問:
1. 环境变量中的"变量"是编程语言中的"变量"?
2. 环境变量中的“变量”是存在于kernel中的?

jessinio@jessinio-laptop:/usr/src/linux$ grep -r "putenv" *

从搜索結果可以看出, enviroment不是kernel的东西。

如果环境变量就是普通的变量, 为什么需要特殊的函数去操作, 如C语言的getenv和putenv

要想知道这一切, 应该从libc6代码开始, 下载代码后, 发现:

char *
getenv (name)
     const char *name;
{
  size_t len = strlen (name);
  char **ep;
  uint16_t name_start;

  if (__environ == NULL || name[0] == '\0')
    return NULL;

  if (name[1] == '\0')

...(下面被截掉)...

可以看到一个__environ的变量. 难道是global变量? 自己写下面的代码测试一下这个变量是否真会存在:

#include <stdlib.h>
#include <endian.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>


int main(int argc, char* argv[])
    {   
        char *env_key = "PATH";
        char *env_value = getenv(env_key);
        printf("get value by getenv function: \n\t%s\n", env_value);
        printf("get value by __environ variable\n");
        int env_len = sizeof(__environ);
        int i;
        for (i =0; i <= env_len; i++)
            {   
                printf("\t%s\n", __environ[i]);
            }
        return 0;
    }

OK, 这个变量真的会存在. "环境变量其实就是普通变量"这句话已经有一半可能是正确的了. 下面再看看__environ哪来的

如果没有#include <unistd.h> , gcc出错: get_env.c:15: error: ‘__environ’ undeclared (first use in this function)

OK, 表示这个__environ来源于这个header文件. 找这个header文件:

jessinio@jessinio-laptop:/tmp/eglibc-2.10.1/stdlib$ find  /usr/include -name unistd.h
/usr/include/linux/unistd.h
/usr/include/bits/unistd.h
/usr/include/asm-generic/unistd.h
/usr/include/sys/unistd.h
/usr/include/unistd.h
/usr/include/asm/unistd.h

看看/usr/include/unistd.h的内容:

/* NULL-terminated array of "NAME=VALUE" environment variables.  */
extern char **__environ;
#ifdef __USE_GNU
extern char **environ;
#endif

__environ为char型二元数组. 说穿了就是一个global变量。 下面做一个测试, 直接修改__environ变量的值, 测试能否传递到子进程中。代码如下:

#include <stdlib.h>
#include <sys/types.h>
#include <endian.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>


int startswith(char *str1, char *str2)
    {
        //like python string instance : 'SHELL=/bin/bash'.startswith("SHELL") is True
        size_t copy_len;
        copy_len = strlen(str2);
        // temporary variable
        // tmp_str[-1] maybe '\0' char
        char tmp_str[ copy_len ];
        strncpy(tmp_str, str1, copy_len);
        // string arrary need '\0' char at the end
        tmp_str[copy_len] = '\0';
        int retval;
        retval = strcmp(str2, tmp_str);
        return retval;
    }

int main(int argc, char* argv[])
    {
        char *env_key = "SHELL";
        char *env_value = getenv(env_key);
        printf("get value by getenv function: \n\t%s\n", env_value);
        printf("set new value by modify __environ variable\n");
        int env_len = sizeof(__environ);
        int i;
        for (i =0; i <= env_len; i++)
            {
                int retval;
                retval = startswith(__environ[i], env_key);
                if ( retval == 0)
                    {
                        // OK! modify the value
                        __environ[i] = "SHELL=/usr/bin/python";
                        //printf("\t%s\n", __environ[i]);

                    }
            }
        // create child process
        pid_t pid;
        pid = fork();
        if (pid == 0)
            {
                //printf("I am child process\n");
                execl("/usr/bin/python", "/usr/bin/python", "/tmp/get_env.py", (char *)0);
            }

        if (pid != 0)
            {
                //printf("I am parent process\n");
                sleep(2);
                waitpid(pid);
            }
        return 0;
    }

get_env.py的代码:
import os
print os.environ['SHELL']

输出結果为:
jessinio@jessinio-laptop:/tmp$ gcc get_env.c
jessinio@jessinio-laptop:/tmp$ ./a.out
get value by getenv function:
    /bin/bash
set new value by modify __environ variable
/usr/bin/python

可见, 不通过setenv这种的stdlib函数照样可以修改环境变量并传递到子进程。 即: 环境变量其实就是普通变量, 程序的全局变量


现在变量可以存在了, 还有一个问题: fork函数到底到了什么, 哪些数据是保留给子进程了. fork函数无疑是kernel的东西, 位于kernel/fork.c

下部就下次写。

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.