自己实现的getenv和getenv_r,验证线程是否可重入,即是否线程安全

来源:互联网 发布:淘宝网聚划算天天特价 编辑:程序博客网 时间:2024/06/04 19:54

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 256static 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的调用不频繁使用.

因此没有必要

0 0