我正在使用dlopen
动态加载库,然后使用dlclose
关闭它。我希望在dlclose
完成后释放所有库资源,但在dlclose
调用之后仍有来自库的打开文件描述符。我想知道如何确保在程序执行过程中卸载库,以便清理它的所有资源。
我的代码如下:
#include <CL/cl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dlfcn.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#define MAX_PATH_LENGTH 80
int deviceQ()
{
cl_int ret;
void * libHandle = dlopen("/usr/lib64/libOpenCL.so", RTLD_LAZY);
cl_int (* clGetPlatformIDs)(cl_uint, cl_platform_id*, cl_uint*) = dlsym(
libHandle, "clGetPlatformIDs"
);
cl_int (* clGetDeviceIDs)(cl_platform_id, cl_device_type, cl_uint, cl_device_id*, cl_uint*) =
dlsym(libHandle, "clGetDeviceIDs");
/********************** PREAMBLE **************************************/
cl_device_id device_id = NULL;
cl_platform_id platform_id = NULL;
cl_uint ret_num_devices;
cl_uint ret_num_platforms;
ret = clGetPlatformIDs(1, &platform_id, &ret_num_platforms);
if (ret != CL_SUCCESS) {
perror("Failed to get platform IDs");
} else if (ret_num_platforms != 1) {
fprintf(stderr, "Number of platforms returned is %d\n", ret_num_platforms);
exit(1);
}
printf("Read platform IDs\n");
ret = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_GPU, 1, &device_id,
&ret_num_devices);
if (ret != CL_SUCCESS) {
perror("Failed to get device IDs");
} else if (ret_num_devices != 1) {
fprintf(stderr, "Number of returned devices is %d\n", ret_num_devices);
exit(1);
}
printf("Read device IDs\n");
/********************** PREAMBLE **************************************/
/***************** RELEASE AND FREE ****************************/
dlclose(libHandle);
/***************** RELEASE AND FREE ****************************/
return 0;
}
size_t closeFileDescriptors(void ** arr) {
// step 1 - get PID
pid_t pid = getpid();
//printf("PID is %d\n", pid);
char path[MAX_PATH_LENGTH];
memset(path, '\0', MAX_PATH_LENGTH);
sprintf(path, "/proc/%d/fd", pid);
int fd;
DIR * d = opendir(path);
struct dirent *dir;
struct stat s;
char dirPath[MAX_PATH_LENGTH];
char realPath[MAX_PATH_LENGTH];
size_t index = 0;
if (d) {
while ((dir = readdir(d)) != NULL) {
if (strcmp(dir->d_name, ".") != 0 &&
strcmp(dir->d_name, "..") != 0) {
fd = atoi(dir->d_name);
if (fstat(fd, &s) != 0) {
perror("fstat failed");
}
memset(dirPath, '\0', MAX_PATH_LENGTH);
strcpy(dirPath, path);
strcat(dirPath, "/");
strcat(dirPath, dir->d_name);
#ifdef S_IFLNK
if (s.st_mode & S_IFLNK) {
#else
if (S_ISLNK(s.st_mode)) {
#endif
memset(realPath, '\0', MAX_PATH_LENGTH);
#ifdef readlink
readlink(dirPath, realPath, MAX_PATH_LENGTH);
printf("%s -> %s\n", dirPath, realPath);
#else
printf("[readlink not defined] %s\n", dirPath);
#endif
} else {
printf("Not link: %s (proceeding anyway)\n", dirPath);
//printf("Not link: %s (ignoring)\n", dirPath);
//continue;
}
if (fd > 2) {
//int fdFlags = fcntl(fd, F_GETFD);
int fdFlags = fcntl(fd, F_GETFL);
if (fdFlags == -1) {
perror("fcntl failed");
}
//off_t offset = lseek(fd, 0, SEEK_CUR);
off_t offset = 0;
if (offset == -1) {
perror("lseek failed");
}
if (arr != NULL) {
/*
arr[index] = (fileData *) malloc(sizeof (fileData));
arr[index]->flags = fdFlags;
arr[index]->offset = offset;
arr[index]->fd = fd;
strcpy(arr[index]->fdPath, realPath);*/
}
index++;
// ignore stdin, stdout, stderr
printf("Closing FD %d (flags %d, offset %zd)\n",
fd, fdFlags, offset);
close(fd);
}
}
}
closedir(d);
} else {
fprintf(stderr, "Could not open directory %s\n", path);
}
return index;
}
int main () {
deviceQ();
printf("=> Closing open file descriptors\n");
closeFileDescriptors (NULL);
deviceQ();
return 0;
}
答案 0 :(得分:7)
你的期望是错误的。当你致电dlclose(3)时,只有“插件”(实际上是共享对象)被“关闭”(实际上,可能是 munmap
- ed),而不是资源(在特别是它使用的文件描述符,可能还有堆分配的内存)。
此外,在Linux上,dlclose
正在调用插件的所谓析构函数(使用__attribute__((destructor))
声明的函数,在GCC中读取function attributes)。
如果您正在编写共享库,您可以设计它以便在dlclose
时释放某些资源(通过析构函数运行适当的终结)功能)。一般来说,这是不可能的(它应该是一个记录在案的惯例)。
虚拟内存中的地址空间(由mmap(2)等获取...等)和文件描述符(由open(2),socket(2),pipe(2)等获取的资源等。 。)是整个process的全局(和通用)。 因此,在一个共享库中获取某些资源(例如,打开一些文件描述符)并在另一个共享库中(或在主程序中)释放它是可能的(并且是合法的,如果有文件记录)。
由于资源“属于”整个过程,所以说释放图书馆获取的资源是没有意义的。
所以你的closeFileDescriptors
可能是一个很大的错误(它可能泄漏了其他一些资源)。
(IIRC,OpenCL API有一些方法来释放它的资源,例如设备,上下文,内核等......但我忘了丑陋的细节;参见{{1} },clReleaseContext
以及更多内容,包括一些特定于实现的内容。)
阅读有关garbage collection的更多信息可能会让您大开眼界。
另请阅读Drepper的论文:How To Write a Shared Library&amp; credentials(7)
如果您绝对需要尽早发布OpenCL相关资源,更合理的方法可能是启动专用于OpenCL事物的不同子进程,并使用聪明的IPC机制(例如pipe(7),{{ 3}},shm_overview(7)等...然后在您的OpenCL内容完成后终止(正确)该子进程。您利用内核正在清除已失效进程使用的所有资源这一事实(不要忘记clReleaseMemObject
...使用sem_overview(7) - 以避免waitpid(2) })。如果您不熟悉所有内容,请先阅读zombie processes。