2015-06-19 10 views
6

Sto caricando dinamicamente una libreria con dlopen, quindi la chiudo con dlclose. Mi aspettavo che tutte le risorse della biblioteca fossero liberate una volta completata la dlclose, ma ci sono ancora dei descrittori di file aperti dalla libreria dopo la chiamata dlclose. Mi chiedo come verificare che una libreria sia scaricata durante l'esecuzione del programma, in modo da ripulire tutte le sue risorse.dlclose non chiude gli handle di file aperti della libreria

Il mio codice è qui sotto:

#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

Stai andando in questo modo nel modo sbagliato. Devi fare in modo che il modulo si riordini. Solo lui sa come farlo. –

+0

Perché ti preoccupi di rilasciare le cose di OpenCL? Potrebbe non essere molto importante. –

+0

@BasileStarynkevitch Perché dopo la prima chiamata 'deviceQ', l'intero programma sarà (in futuro lavoro) migrato su hardware diverso in fase di runtime, quindi ho bisogno di OpenCL per reinizializzare le sue strutture dati. – BlackSheep

risposta

6

tua aspettativa è sbagliato. Quando si chiama dlclose(3), solo il "plug-in" (oggetto effettivamente condiviso) è "chiuso" (in realtà, può esseremunmap -ed), ma non le risorse (in particolare i descrittori di file e, eventualmente, la memoria allocata nell'heap) ha Usato.

Inoltre, sulla Linux nello specifico, dlclose sta chiamando le cosiddette funzioni distruttore del plugin (quelle dichiarate con __attribute__((destructor)), leggere function attributes in GCC).

Se si esegue la codifica di una libreria condivisa, si potrebbe progettare in modo che alcuni risorse vengono rilasciate al dlclose tempo (avendo finalizations appropriate eseguire attraverso distruttore funzioni). In generale, non è facilmente possibile (e dovrebbe essere una convenzione documentata).

risorse come lo spazio di indirizzi nella memoria virtuale (ottenuto dalla mmap(2) ecc ...) e descrittori di file (ottenuto con open(2), socket(2), pipe(2) etc etc ...) sono globali (e comune) per l'intero process. Quindi sarebbe possibile (e legittimo, se documentato) acquisire una risorsa (ad esempio aprire un descrittore di file) in una libreria condivisa e rilasciarla in un'altra (o nel programma principale).

Poiché una risorsa "appartiene" all'intero processo, non ha senso parlare di rilascio delle risorse acquisite da una libreria.

Quindi il tuo closeFileDescriptors è probabilmente un grosso errore (e probabilmente perde altre risorse).

(IIRC, OpenCL API ha qualche modo per liberare le proprie risorse, ad esempio i dispositivi, i contesti, noccioli, ecc .... Ma ho dimenticato il brutto dettagli, vedi clReleaseContext, clReleaseMemObject e molti altri, tra cui alcuni implementazione quelli specifici.)

Leggere di più su garbage collection probabilmente allargherà la tua mente.

Leggi anche l'articolo di Drepper: How To Write a Shared Library & credentials(7)

Se è assolutamente necessario rilasciare le risorse OpenCL correlate presto, un modo più ragionevole potrebbe essere quella di avviare un processo figlio diverso dedicato alle cose OpenCL, e l'uso intelligente IPC meccanismi (es. pipe(7), shm_overview(7), sem_overview(7), ecc ...) quindi terminare (correttamente) quel processo figlio una volta che il materiale OpenCL è finito. Approfittate del fatto che il kernel sta pulendo tutte le risorse utilizzate da un processo defunto (non dimenticatevi di wait ... ad esempio usando waitpid(2) - per evitare di avere zombie processes).Se non hai familiarità con tutto ciò, leggi prima Advanced Linux Programming.

+0

Esiste un modo per forzare la libreria a scaricare e ripulire tutte le risorse che ha assegnato per sé, comprese le regioni 'mmap' e i descrittori di file aperti? – BlackSheep

+0

No, perché tali risorse sono globali per il processo. –

+0

Esiste un modo per ottenere un elenco di queste risorse, quindi deselezionarle manualmente? In particolare, libOpenCL effettua numerose chiamate 'open' su'/dev/nvidia0' e la memoria mappa un gruppo di regioni. C'è un modo per munmap automaticamente come parte della pulizia? – BlackSheep

Problemi correlati