dlclose不会关闭库打开文件句柄

时间:2015-06-19 19:28:32

标签: c dlopen

我正在使用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;
}

1 个答案:

答案 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