函数putenv
不是线程安全函数,所以我想如果我在调用putenv
之前调用pthread_mutex_lock
,我可以通过这种方式putenv
"线程安全"吗?
我试过了,但是当我运行它时,出现了分段错误。
这是代码:
#include "apue.h"
#include <pthread.h>
pthread_mutex_t envlock = PTHREAD_MUTEX_INITIALIZER;
void thread_func(void*arg){
pthread_mutex_lock(&envlock);
char env[100];
sprintf(env,"hhh=%s",(char*)arg);
putenv(env);
pthread_mutex_unlock(&envlock);
return;
}
int main(){
pthread_t thread0, thread1, thread2;
void *shit;
int err;
char name0[]="thread0";
err=pthread_create(&thread0,NULL,thread_func,(void*)name0);
if(err!=0)
exit(-1);
char name1[]="thread1";
err=pthread_create(&thread1,NULL,thread_func,(void*)name1);
if(err!=0)
exit(-1);
char name2[]="thread2";
err=pthread_create(&thread2,NULL,thread_func,(void*)name2);
if(err!=0)
exit(-1);
pthread_join(thread0,&shit);
pthread_join(thread1,&shit);
pthread_join(thread2,&shit);
char *hhh=getenv("hhh");
printf("hhh is =%s",hhh);
return 0;
}
putenv
在较新版本的glibc中是可重入的。问题是putenv
不会复制提供给它的字符串,因此您不能将其基于堆栈。尝试将char env[100]
保存在函数结束时不会被破坏的地方。
putenv(( 函数不需要是可重入的,并且 glibc 2.0不是,但glibc 2.1版本是。
。
从 2.1.2 版开始,glibc 实现符合 SUSv2: 使用给定 putenv(( 的指针字符串。 特别是这个字符串 成为环境的一部分;稍后更改它将更改 环境。 (因此,使用 自动变量作为参数,然后从调用返回 函数,而字符串仍然是环境的一部分。
通常,使用锁定机制保护函数不会使其自动重入。 重入意味着,一个函数可以在调用自身的过程中再次重新输入,而不会对它管理的活动调用的内部数据造成任何风险。 为此,函数必须仅在其堆栈帧上运行,或者作为参数(在堆栈中或可存储寄存器中(提供指针,指向它必须对其执行操作的任何外部数据对象。 这使得重入成为可能。
现在,我将解释一种使用非重入函数(使用您提出的锁定机制(不适用的场景:
假设您有一个正在执行的函数f()
,然后接收到信号(或中断(,信号处理程序只需要调用f()
。 只要您锁定了函数条目,信号处理程序将在进入函数f()
时被锁定,使处理程序永远不会返回,因此主程序无法继续执行其f()
以打开锁。 在 (大多数( 情况下 (大多数( 你使用相同的堆栈来处理中断 而不是中断函数 (嗯, 我知道 FreeBSD 对中断处理程序使用不同的上下文, 但不知道这是否适用于用户模式进程(, 在处理程序返回之前没有机会解锁锁, 但它不能返回处理程序正在等待锁解锁。 此重入情况不由您的例程处理。
如何避免这个问题。 只需避免在共享区域中间时调用此处理程序的中断(那么,为什么要锁定它? 因此,中断是在函数调用之后处理的。
当然,如果您需要从多个线程(每个线程都有自己的堆栈(调用它,那么您毕竟需要锁。
结论:
锁只是避免了f()
的重新进入,但不会使其可重入。