Dlclose does not close open library file files

I dynamically load the library with dlopen and then close its dlclose . I expected all library resources to be freed after dlclose completes, but after calling dlclose , open file descriptors from the library still remain. I am wondering how to make sure that the library is unloaded in the middle of the program so that it cleans up all its resources.

My code is below:

 #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; } 
+6
source share
1 answer

Your expectation is wrong. When you call dlclose (3) , only the "plugin" (actually the shared object) is "closed" (actually it could be munmap - ed), but not the resources (in particular file descriptors and possibly a bunch of allocated memory) that He used.

In addition, on Linux, dlclose specifically calls the so-called plug-in destructor functions (those declared using __attribute__((destructor)) read attributes on the function in GCC).

If you are coding a shared library, you can create it so that some resources are released in dlclose time (by performing appropriate finalizations through the destructor functions). In general, this is not easy (and it should be a documented agreement).

Resources such as address space in virtual memory (obtained by mmap (2) , etc.) and file descriptors (obtained by open (2) , socket (2) , pipe (2) , etc ...) are global (and common) for the entire process . Thus, it would be possible (and legal, if it were documented) to get some resource (for example, open some file descriptor) in one shared library and release it in another (or in the main program).

Since the resource "belongs" to the whole process, there is no point in talking about the release of resources received by the library.

So your closeFileDescriptors is probably a big mistake (and it probably leaks some other resources).

(IIRC, the OpenCL API has some way to release its resources, such as devices, contexts, kernels, etc. But I forgot the ugly details: see clReleaseContext , clReleaseMemObject and many others, including some implementation-specific ones.)

More details on garbage collection will probably expand your mind.

Read also the Drepper document: How to write a shared library and credentials (7)

If you need to release resources related to OpenCL early, a more sensible way would be to start another OpenCL child process and use smart IPC (e.g. pipe (7) , shm_overview (7) , sem_overview (7) , etc. ), then terminate (correctly) this child process after completing work with OpenCL. You use the fact that the kernel cleans up all the resources used by the idle process (don't forget wait ... it -eg using waitpid (2) - to avoid zombie processes ). If you are not familiar with all of this, first read Advanced Linux Programming .

+6
source

Source: https://habr.com/ru/post/989427/


All Articles