此代码取自Richard Stevens编写的UNIX环境中的高级编程第3版。这是如何制作getenv()
的可重入版本的示例。这里只是为了学习目的。
/* Copyright (c) W.R.Stevens */
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
extern char **environ;
pthread_mutex_t env_mutex;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;
static void
thread_init(void)
{
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);
}
int
getenv_r(const char *name, char *buf, int buflen)
{
int i, len, olen;
pthread_once(&init_done, thread_init);
len = strlen(name);
pthread_mutex_lock(&env_mutex);
for (i = 0; environ[i] != NULL; i++) {
if ((strncmp(name, environ[i], len) == 0) &&
(environ[i][len] == '=')) {
olen = strlen(&environ[i][len+1]);
if (olen >= buflen) {
pthread_mutex_unlock(&env_mutex);
return(ENOSPC);
}
strcpy(buf, &environ[i][len+1]);
pthread_mutex_unlock(&env_mutex);
return(0);
}
}
pthread_mutex_unlock(&env_mutex);
return(ENOENT);
}
此代码易于理解。但我有个问题。我们永远不会调用pthread_mutex_destroy()
,这意味着退出时可能会出现内存泄漏(我想平台之间可能会有所不同)。
首先想到的是人们可以使用PTHREAD_MUTEX_INITIALIZER
。需要pthread_mutex_init()
电话吗?如果不是,则无需致电pthread_mutex_destroy()
。但是,互斥锁将是非递归的。
可以编写一个简单的C ++类来破坏析构函数中的互斥锁。但是,它不适合只有C编译器的人(由于一个函数,使用C ++编译器似乎是胡说八道。)
我想到的另一件事是特定于编译器的扩展,例如GCC中的__attribute__((destructor))
(并且希望是clang)。但是,它是不可携带的。
是否可以避免内存泄漏?如果是,如何在C中完成?
更新
正如David Butenhof编写的“使用POSIX线程编程”中所示,我们永远不需要销毁PTHREAD_MUTEX_INITIALIZER
变体。具有其他属性的互斥锁怎么样?
答案 0 :(得分:5)
在进程终止时仍然存在的资源不是内存泄漏,尽管有一些天真的工具将它们分类。内存泄漏是程序在其生命周期内的资源需求不可逆转且无限制的增长,与实际工作集不成比例。
每个POSIX(这是您获得POSIX线程的地方),所有进程本地资源在程序终止时不再存在。没有必要明确地销毁/释放它们,在某些情况下,与你的一样,你无法安全地销毁/释放它们而你不应该尝试。
答案 1 :(得分:1)
没有内存泄漏,因为pthread_mutex_t
变量存在于用户内存中,在进程退出时,所有用户分配的内存都被回收。当某些东西分配堆内存时会发生内存泄漏,比如strdup。
然后你不清理它。
如果分配如
pthread_mutex_t *foo;
....
foo =malloc(sizeof(pthread_mutex_t);
然后没有释放free会造成内存泄漏 - 如果foo被分配给更多的新内存。但是,在进程终止所有内存时,操作系统将回收所请求的进程,包括堆。史蒂文斯在关于过程的章节中解释了这一点。该代码不泄漏。