问题
main 函数 (默认进程入口)
int main(int argc, char* argv[], char* env[])
- argc - 命令行参数个数
- argv[] - 命令行参数数组
- env[] - 环境变量数组 (最后一个元素为 NULL)
什么是环境变量?
环境变量是进程运行过程中可能用到的 "键值对" (NAME = VALUE)
进程拥有一个环境表 (environment list),环境表包含了环境变量
环境表用于记录系统中相对固定的共享信息 (不特定于具体进程)
进程之间的环境表相对独立 (环境表可在父子进程之间传递)
环境表的构成
全局指针 environ 指向当前进程的环境表,环境表是一个字符串数组,数组中的每一个字符串都是一个环境变量,数组的最后最后一项为 NULL,表示环境表的结束
环境变量初探
parent.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#define EXE "child.out"
int create_process(char* path, char* args[], char* env[])
{
int ret = fork();
if( ret == 0 )
{
execve(path, args, env);
}
return ret;
}
int main()
{
char path[] = EXE;
char arg1[] = "hello";
char arg2[] = "world";
char* args[] = {path, arg1, arg2, NULL};
printf("%d begin\n", getpid());
printf("%d child = %d\n", getpid(), create_process(EXE, args, args));
printf("%d end\n", getpid());
return 0;
}
child.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char* argv[], char* env[])
{
int i = 0;
sleep(1);
printf("process parameter:\n");
for(i=0; i<argc; i++)
printf("exec = %d, %s\n",
getpid(), argv[i]);
printf("environment list:\n");
i = 0;
while( env[i] )
printf("exec = %d, %s\n",
getpid(), env[i++]);
return 0;
}
parent.c 中会创建子进程,并在子进程中通过 execve(...) 函数运行 child.out 程序,传递给子进程 进程参数 和 环境变量,传递给子进程的环境变量和进程参数的值是一样的
程序运行结果如下图所示:
通过打印可以看出,子进程的进程参数和环境变量的值是一样的
我们直接在命令行中运行 chlid.out 程序
在命令行中执行 child.out 程序, 打印出来的环境变量是父进程命令行传递过来的,所以 child.out 这个进程的环境变量和命令行的环境变量值是相同的
深入理解环境变量
对于进程来说,环境变量是一种特殊的参数
环境变量相对于 启动参数 较稳定 (系统定义 且 各个进程共享)
环境变量遵守固定规范 (如:键值对,变量名大写)
环境变量 和 启动参数 存储于同一内存区域 (私有)
环境变量读写接口
头文件: #include <stdlib.h>
读: char* getenv(const char* name);
返回 name 环境变量的值,如果不存在,返回 NULL
写: int putenv(char* string);
设置 / 改变 环境变量 (NAME=VALUE),string 不能是栈上定义的字符串
环境表入口: extern char** environ;
环境变量读写接口初探
parent.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#define EXE "child.out"
int create_process(char* path, char* args[], char* env[])
{
int ret = fork();
if( ret == 0 )
{
execve(path, args, env);
}
return ret;
}
int main()
{
char path[] = EXE;
char arg1[] = "hello";
char arg2[] = "world";
char* args[] = {path, arg1, arg2, NULL};
printf("%d begin\n", getpid());
printf("%d child = %d\n", getpid(), create_process(EXE, args, args));
printf("%d end\n", getpid());
return 0;
}
child.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char* argv[], char* env[])
{
int i = 0;
sleep(1);
printf("process parameter:\n");
for(i=0; i<argc; i++)
printf("exec = %d, %s\n",
getpid(), argv[i]);
printf("environment list:\n");
i = 0;
while( env[i] )
printf("exec = %d, %s\n",
getpid(), env[i++]);
printf("original:\n");
printf("%s=%s\n", "TEST1", getenv("TEST1"));
printf("%s=%s\n", "TEST2", getenv("TEST2"));
printf("%s=%s\n", "TEST3", getenv("TEST3"));
putenv("TEST1");
putenv("TEST2=NEW-VALUE");
putenv("TEST3=CREATE NEW");
printf("new:\n");
printf("%s=%s\n", "TEST1", getenv("TEST1"));
printf("%s=%s\n", "TEST2", getenv("TEST2"));
printf("%s=%s\n", "TEST3", getenv("TEST3"));
i = 0;
extern char** environ;
printf("changed:\n");
while( environ[i] )
printf("exec = %d, %s\n",
getpid(), environ[i++]);
return 0;
}
程序运行结果如下图所示:
parent.out 这个进程创建出一个子进程,并通过 execve(...) 函数,执行 child.out 程序并传递进程参数和环境变量
因为父进程中传递的进程参数和环境变量是一样的,所以 child.out 第 15 行和第 23 行打印出的进程参数和环境变量是一样的
因为子进程当前并不存在名为 TEST1、TEST2、TEST3 的环境变量名,所以第 28 行 - 第 30 行的 getenv(...) 函数返回 NULL
第 32 行和第 34 行,通过 putenv(...) 函数,改变环境变量的值;第 32 行,有环境变量名 TEST1,但是没有环境变量值,这样会清除名为 TEST1 的环境变量;第 33 行和第 34 行会新增 TEST2=NEW-VALUE 和 TEST3=CREATE NEW 的环境变量
第 38 行和第 40 行,我们重新打印名为 TEST1、TEST2、TEST3 的环境变量值,发现新增了 TEST2、TEST3 环境变量
第 48 行 - 50 行,我们通过全局指针 environ,来打印当前进程存在的所有环境变量
编程练习
编写应用程序,通过命令行参数读写环境变量
选项定义
环境变量编程练习
main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
typedef int (*OptCall)(char* name, char* value);
typedef struct
{
int opt;
OptCall handler;
} CallHandler;
static int A_Handler(char* name, char* value)
{
extern char** environ;
int ret = 0;
int i = 0;
while(environ[i])
{
printf("%s\n", environ[i++]);
}
return ret;
}
static int R_Handler(char* name, char* value)
{
int ret = 0;
if(name == NULL)
{
printf("Environ name is NULL!\n");
ret = -1;
}
else
{
printf("%s=%s\n", name, getenv(name));
}
return ret;
}
static int W_Handler(char* name, char* value)
{
int ret = 0;
if((name == NULL) || (value == NULL))
{
printf("Environ name or Environ value is NULL!\n");
ret = -1;
}
else
{
char* str = (char*)malloc(sizeof(char) * (strlen(name) + strlen(value) + 2));
if(str)
{
strcpy(str, name);
strcat(str, "=");
strcat(str, value);
if(putenv(str) == 0)
{
printf("New Environ: %s\n", str);
}
else
{
printf("Error on writing new environ value\n");
ret = -1;
}
// free(str); 不能调用 free
}
else
{
printf("malloc str failed!\n");
ret = -1;
}
}
return ret;
}
static int T_Handler(char* name, char* value)
{
return W_Handler(name, value) || A_Handler(name, value);
}
CallHandler g_handler[] =
{
{'a', A_Handler},
{'r', R_Handler},
{'w', W_Handler},
{'t', T_Handler},
};
int g_handler_size = sizeof(g_handler) / sizeof(*g_handler);
int ExecuteOptHandler(int opt, char* name, char* value)
{
int ret = -1;
for(int i = 0; i < g_handler_size; i++)
{
if(opt == g_handler[i].opt)
{
ret = g_handler[i].handler(name, value);
}
}
return ret;
}
int main(int argc, char* argv[])
{
int c = 0;
int opt = 0;
char* name = NULL;
char* value = NULL;
while((c = getopt(argc, argv, "arwtn:v:")) != -1)
{
switch(c)
{
case 'a':
case 'r':
case 'w':
case 't':
opt = c;
break;
case 'n':
name = optarg;
break;
case 'v':
value = optarg;
break;
default:
exit(-1);
}
}
ExecuteOptHandler(opt, name, value);
return 0;
}
我们使用 getopt(...) 函数来匹配命令行参数,根据 opt 来匹配具体的处理函数
第 74 行,不能调用 free(str) 是因为 putenv(str) 会让环境表中的某一项指向 str,并不会再拷贝一份 str,如果我们调用了 free(str),str 存储的内容可能随时被改变
程序运行结果如下图所示:
程序正常正确的运行了