UNIX高级环境编程中说到getenv函数是不可重入的,因为它用的是全局的静态存储区,由于对它进行了些操作(strcpy)而没有加锁,所以当多个线程访问的时候会出现错误.
书上给了程序,如何验证呢,是个问题.所以我们可以把环境变量中的值重定向到文本文件中 ,命令如下:
env >log
获得log如下:
然后就可以模拟验证了.
程序如下:
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
#include<malloc.h>
#include<pthread.h>
#include<errno.h>
#define NUM_MAX_ENVIRONMENT 100
#define MAX 256
static char envbuf[MAX];
char **environ;
struct msg{
char name[20];
char *buf;
int buflen;
};
pthread_mutex_t env_mutex;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;
static void thread_init(void)
{
//thread attirbute
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&env_mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
void *myGetenv_r(void * msg)
{
struct msg *my_msg = (struct msg *) msg;
int i, len, olen;
pthread_once(&init_done, thread_init);
len = strlen(my_msg->name);
pthread_mutex_lock(&env_mutex);
for( i = 0; environ[i] != NULL; i++)
{
if(strncmp(environ[i], my_msg->name, len) == 0)
if(environ[i][len] == '=')
{
olen =strlen(&environ[i][len+1]);
if(olen > my_msg->buflen)
{
pthread_mutex_unlock(&env_mutex);
return (void *)ENOSPC;
}
strcpy(my_msg->buf, &environ[i][len+1]);
pthread_mutex_unlock(&env_mutex);
return (void *)my_msg;
}
}
pthread_mutex_unlock(&env_mutex);
return (void *)ENOENT;
}
void *myGetenv(void * name)
{
char *my_name;
my_name = (char *)name;
int len =strlen(my_name);
int i;
for(i = 0; environ[i] != NULL; i++)
{
if(strncmp(environ[i], my_name, len) == 0)
{
if(environ[i][len] == '=')
{
strcpy(envbuf, &environ[i][len+1]);
return envbuf;
}
}
}
return NULL;
}
void createEnviron()
{
environ =(char **) malloc(NUM_MAX_ENVIRONMENT * sizeof(char *));
int i;
for(i = 0; i < NUM_MAX_ENVIRONMENT; i++)
{
environ[i] = (char *) malloc(256);
}
char line[MAX];
int count =0;
FILE *fp = fopen("log", "r");
while( fgets(line, MAX, fp) != NULL)
{
strcpy(environ[count], line);
count++;
}
environ[count] = '\0';
fclose(fp);
}
void freeEnviron()
{
int i;
for(i = 0; i < NUM_MAX_ENVIRONMENT; i++)
{
free(environ[i]);
}
free(environ);
}
void printEnviron()
{
int i;
for(i = 0 ;environ[i] != NULL; i++)
printf("%s", environ[i]);
}
void checkResults(int err)
{
if(err != 0)
{
printf("create thread failed!\n");
}
}
int main()
{
createEnviron();
char name1[] = "PATH";
char name2[] = "LC_PAPER";
char name3[] = "JRE_HOME";
// create thread
pthread_t tid1, tid2, tid3;
int err = pthread_create(&tid1, NULL, myGetenv, (void *)name1);
checkResults(err);
err = pthread_create(&tid2, NULL, myGetenv, (void *)name2);
checkResults(err);
err = pthread_create(&tid3, NULL, myGetenv, (void *)name3);
checkResults(err);
void* tret1;
void* tret2;
void* tret3;
pthread_join(tid1, &tret1);
pthread_join(tid2, &tret2);
pthread_join(tid3, &tret3);
// char *str1 = (char *)myGetenv("PATH");
printf("%s \n", (char *)tret1);
printf("%s \n", (char *)tret2);
printf("%s \n", (char *)tret3);
struct msg message4;
strcpy(message4.name, "PATH");
message4.buflen = MAX;
message4.buf = (char *) malloc(message4.buflen);
struct msg message5;
strcpy(message5.name, "LC_PAPER");
message5.buflen = MAX;
message5.buf = (char *) malloc(message5.buflen);
struct msg message6;
strcpy(message6.name, "JRE_HOME");
message6.buflen = MAX;
message6.buf = (char *) malloc(message6.buflen);
pthread_t tid4, tid5, tid6;
err = pthread_create(&tid4, NULL, myGetenv_r, (void *)&message4);
checkResults(err);
err = pthread_create(&tid5, NULL, myGetenv_r, (void *)&message5);
checkResults(err);
err = pthread_create(&tid6, NULL, myGetenv_r, (void *)&message6);
checkResults(err);
void* tret4;
void* tret5;
void* tret6;
pthread_join(tid4, &tret4);
pthread_join(tid5, &tret5);
pthread_join(tid6, &tret6);
//char *str1 = (char *)myGetenv("PATH");
printf("%s \n", ((struct msg *)tret4)->buf);
printf("%s \n", ((struct msg *)tret5)->buf);
printf("%s \n", ((struct msg *)tret6)->buf);
struct msg * m1 = (struct msg *) tret4;
struct msg * m2 = (struct msg *) tret5;
struct msg * m3 = (struct msg *) tret6;
printf("%s \n", m1->buf);
printf("%s \n", m2->buf);
printf("%s \n", m3->buf);
printf("%s \n", message4.buf);
printf("%s \n", message5.buf);
printf("%s \n", message6.buf);
free(message4.buf);
free(message5.buf);
free(message6.buf);
freeEnviron();
return 0;
}
获得的结果如下:
前三个输出的结果一模一样,不是我们想要的结果.getenv_r采用了线程自己的存储区.所以没有出现问题.
你可能会说,为啥用互斥锁,而不用读写锁呢?因为多个线程访问是,对log中读取和strcpy的写.
但并发的增强可能并不会很大程度上改善程序性能.
1.环境列表不长.
2.对getenv的调用不频繁使用.
因此没有必要