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

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

因此没有必要

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值