50 votes

Obtenir le descripteur de fichier alloué le plus élevé

Est-il un portable moyen (POSIX) pour obtenir la plus haute attribuée descripteur de fichier numéro du processus en cours?

Je sais qu'il y a une belle manière de faire le nombre sur AIX, par exemple, mais je suis à la recherche d'un portable de la méthode.

La raison pour laquelle je te demande, c'est que je veux fermer tous les descripteurs de fichiers ouverts. Mon programme est un serveur qui s'exécute en tant que root et les fourches et les execs enfant de programmes pour les utilisateurs non-root. Laissant le privilège des descripteurs de fichiers ouverts dans le processus de l'enfant est un problème de sécurité. Certains descripteurs de fichier peut être ouvert que par le code je ne peux pas le contrôle (la bibliothèque C, des bibliothèques tierces, etc.), je ne peux donc pas compter sur FD_CLOEXEC soit.

71voto

mark4o Points 20472

Alors que les portables, la fermeture de tous les descripteurs de fichier jusqu'à sysconf(_SC_OPEN_MAX) n'est pas fiable, parce que sur la plupart des systèmes de cet appel renvoie le descripteur de fichier de la limite douce, qui aurait pu être abaissée en dessous de la plus haute utilisés descripteur de fichier. Un autre problème est que sur beaucoup de systèmes, sysconf(_SC_OPEN_MAX) peut renvoyer INT_MAX, ce qui peut provoquer cette approche trop lent. Malheureusement, il n'est pas fiable, portable alternative qui ne nécessite pas de parcourir tous les cas éventuels de non-négatif int descripteur de fichier.

Bien que n'étant pas portable, la plupart des systèmes d'exploitation en usage courant aujourd'hui de fournir une ou plusieurs des solutions suivantes pour résoudre ce problème:

  1. Une fonction de bibliothèque à fermer tous les descripteurs de fichier >= fd. C'est la solution la plus simple pour le cas de la fermeture de tous les descripteurs de fichier, bien qu'il ne peut pas être utilisé pour beaucoup de choses. Pour fermer tous les descripteurs de fichier, sauf pour un certain nombre, dup2 peut être utilisé pour déplacer vers le bas de la fourchette à l'avance, et de les replacer par la suite si nécessaire.

    • closefrom(fd) (Solaris 9 ou plus tard, FreeBSD 8.0 7.3 ou et plus tard, NetBSD 3.0 ou plus tard, à partir de la version 3.5 ou ultérieure.)

    • fcntl(fd, F_CLOSEM, 0) (AIX, IRIX, NetBSD)

  2. Une fonction de bibliothèque à fournir le maximum de descripteurs de fichiers actuellement utilisés par le processus. Pour fermer tous les descripteurs de fichier ci-dessus un certain nombre, soit près tous d'entre eux jusqu'à ce maximum, ou obtenez en permanence et à proximité de la plus haute descripteur de fichier dans une boucle jusqu'à ce que le faible lié est atteint. Ce qui est plus efficace repose sur le descripteur de fichier de la densité.

    • fcntl(0, F_MAXFD) (NetBSD)

    • pstat_getproc(&ps, sizeof(struct pst_status), (size_t)0, (int)getpid())
      Renvoie des informations sur le processus, y compris la plus haute descripteur de fichier actuellement ouvert en ps.pst_highestfd. (HP-UX)

  3. Un répertoire contenant une entrée pour chaque descripteur de fichier ouvert. C'est l'approche la plus souple car il permet de fermer tous les descripteurs de fichiers, la recherche de la plus haute descripteur de fichier, ou de faire à peu près rien d'autre sur chaque descripteur de fichier ouvert, même ceux d'un autre processus (sur la plupart des systèmes). Cependant, cela peut être plus compliqué que les autres approches pour les utilisations courantes. En outre, il peut échouer pour diverses raisons telles que le proc/fdescfs pas monté, un environnement chroot, ou pas de descripteurs de fichiers disponibles pour ouvrir le répertoire (processus ou d'un système de limite). Par conséquent, l'utilisation de cette approche est souvent combiné avec un mécanisme de secours. Exemple (OpenSSH), un autre exemple (glib).

    • /proc/pid/fd/ ou /proc/self/fd/ (Linux, Solaris, AIX, Cygwin, NetBSD)
      (AIX ne prend pas en charge "self")

    • /dev/fd/ (FreeBSD, Darwin, OS X)

    Il peut être difficile de gérer tous les cas de coin de manière fiable avec cette approche. Par exemple, considérons la situation où tous les descripteurs de fichier >= fd sont fermées, mais tous les descripteurs de fichier < fd sont utilisés, le processus actuel plafond de ressources est fd, et il y a des descripteurs de fichier >= fd en cours d'utilisation. Parce que le processus de plafond de ressources a été atteint, le répertoire ne peut pas être ouvert. Si la fermeture de chaque descripteur de fichier de fd à travers le plafond de ressources ou d' sysconf(_SC_OPEN_MAX) est utilisé comme un secours, rien ne sera fermé.

13voto

chuck Points 646

La méthode POSIX est:

 int maxfd=sysconf(_SC_OPEN_MAX);
for(int fd=3; fd<maxfd; fd++)
    close(fd);
 

(notez que vous fermez à partir de 3, pour garder stdin / stdout / stderr ouvert)

close () inoffensif renvoie EBADF si le descripteur de fichier n'est pas ouvert. Inutile de gaspiller une autre vérification des appels système.

Certains Unix supportent un closefrom (). Cela évite le nombre excessif d'appels à fermer () en fonction du nombre maximal de descripteurs de fichier possibles. Bien que la meilleure solution que je connaisse, elle est complètement non portable.

6voto

Hongli Points 8862

J'ai écrit du code pour traiter toutes les fonctionnalités spécifiques à la plate-forme. Toutes les fonctions sont protégées contre le signal asynchrone. Je pensais que les gens pourraient trouver cela utile. Seulement testé sur OS X pour le moment, n'hésitez pas à améliorer / corriger.

 // Async-signal safe way to get the current process's hard file descriptor limit.
static int
getFileDescriptorLimit() {
    long long sysconfResult = sysconf(_SC_OPEN_MAX);

    struct rlimit rl;
    long long rlimitResult;
    if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
        rlimitResult = 0;
    } else {
        rlimitResult = (long long) rl.rlim_max;
    }

    long result;
    if (sysconfResult > rlimitResult) {
        result = sysconfResult;
    } else {
        result = rlimitResult;
    }
    if (result < 0) {
        // Both calls returned errors.
        result = 9999;
    } else if (result < 2) {
        // The calls reported broken values.
        result = 2;
    }
    return result;
}

// Async-signal safe function to get the highest file
// descriptor that the process is currently using.
// See also http://stackoverflow.com/questions/899038/getting-the-highest-allocated-file-descriptor
static int
getHighestFileDescriptor() {
#if defined(F_MAXFD)
    int ret;

    do {
        ret = fcntl(0, F_MAXFD);
    } while (ret == -1 && errno == EINTR);
    if (ret == -1) {
        ret = getFileDescriptorLimit();
    }
    return ret;

#else
    int p[2], ret, flags;
    pid_t pid = -1;
    int result = -1;

    /* Since opendir() may not be async signal safe and thus may lock up
     * or crash, we use it in a child process which we kill if we notice
     * that things are going wrong.
     */

    // Make a pipe.
    p[0] = p[1] = -1;
    do {
        ret = pipe(p);
    } while (ret == -1 && errno == EINTR);
    if (ret == -1) {
        goto done;
    }

    // Make the read side non-blocking.
    do {
        flags = fcntl(p[0], F_GETFL);
    } while (flags == -1 && errno == EINTR);
    if (flags == -1) {
        goto done;
    }
    do {
        fcntl(p[0], F_SETFL, flags | O_NONBLOCK);
    } while (ret == -1 && errno == EINTR);
    if (ret == -1) {
        goto done;
    }

    do {
        pid = fork();
    } while (pid == -1 && errno == EINTR);

    if (pid == 0) {
        // Don't close p[0] here or it might affect the result.

        resetSignalHandlersAndMask();

        struct sigaction action;
        action.sa_handler = _exit;
        action.sa_flags   = SA_RESTART;
        sigemptyset(&action.sa_mask);
        sigaction(SIGSEGV, &action, NULL);
        sigaction(SIGPIPE, &action, NULL);
        sigaction(SIGBUS, &action, NULL);
        sigaction(SIGILL, &action, NULL);
        sigaction(SIGFPE, &action, NULL);
        sigaction(SIGABRT, &action, NULL);

        DIR *dir = NULL;
        #ifdef __APPLE__
            /* /dev/fd can always be trusted on OS X. */
            dir = opendir("/dev/fd");
        #else
            /* On FreeBSD and possibly other operating systems, /dev/fd only
             * works if fdescfs is mounted. If it isn't mounted then /dev/fd
             * still exists but always returns [0, 1, 2] and thus can't be
             * trusted. If /dev and /dev/fd are on different filesystems
             * then that probably means fdescfs is mounted.
             */
            struct stat dirbuf1, dirbuf2;
            if (stat("/dev", &dirbuf1) == -1
             || stat("/dev/fd", &dirbuf2) == -1) {
                _exit(1);
            }
            if (dirbuf1.st_dev != dirbuf2.st_dev) {
                dir = opendir("/dev/fd");
            }
        #endif
        if (dir == NULL) {
            dir = opendir("/proc/self/fd");
            if (dir == NULL) {
                _exit(1);
            }
        }

        struct dirent *ent;
        union {
            int highest;
            char data[sizeof(int)];
        } u;
        u.highest = -1;

        while ((ent = readdir(dir)) != NULL) {
            if (ent->d_name[0] != '.') {
                int number = atoi(ent->d_name);
                if (number > u.highest) {
                    u.highest = number;
                }
            }
        }
        if (u.highest != -1) {
            ssize_t ret, written = 0;
            do {
                ret = write(p[1], u.data + written, sizeof(int) - written);
                if (ret == -1) {
                    _exit(1);
                }
                written += ret;
            } while (written < (ssize_t) sizeof(int));
        }
        closedir(dir);
        _exit(0);

    } else if (pid == -1) {
        goto done;

    } else {
        do {
            ret = close(p[1]);
        } while (ret == -1 && errno == EINTR);
        p[1] = -1;

        union {
            int highest;
            char data[sizeof(int)];
        } u;
        ssize_t ret, bytesRead = 0;
        struct pollfd pfd;
        pfd.fd = p[0];
        pfd.events = POLLIN;

        do {
            do {
                // The child process must finish within 30 ms, otherwise
                // we might as well query sysconf.
                ret = poll(&pfd, 1, 30);
            } while (ret == -1 && errno == EINTR);
            if (ret <= 0) {
                goto done;
            }

            do {
                ret = read(p[0], u.data + bytesRead, sizeof(int) - bytesRead);
            } while (ret == -1 && ret == EINTR);
            if (ret == -1) {
                if (errno != EAGAIN) {
                    goto done;
                }
            } else if (ret == 0) {
                goto done;
            } else {
                bytesRead += ret;
            }
        } while (bytesRead < (ssize_t) sizeof(int));

        result = u.highest;
        goto done;
    }

done:
    if (p[0] != -1) {
        do {
            ret = close(p[0]);
        } while (ret == -1 && errno == EINTR);
    }
    if (p[1] != -1) {
        do {
            close(p[1]);
        } while (ret == -1 && errno == EINTR);
    }
    if (pid != -1) {
        do {
            ret = kill(pid, SIGKILL);
        } while (ret == -1 && errno == EINTR);
        do {
            ret = waitpid(pid, NULL, 0);
        } while (ret == -1 && errno == EINTR);
    }

    if (result == -1) {
        result = getFileDescriptorLimit();
    }
    return result;
#endif
}

void
closeAllFileDescriptors(int lastToKeepOpen) {
    #if defined(F_CLOSEM)
        int ret;
        do {
            ret = fcntl(lastToKeepOpen + 1, F_CLOSEM);
        } while (ret == -1 && errno == EINTR);
        if (ret != -1) {
            return;
        }
    #elif defined(HAS_CLOSEFROM)
        closefrom(lastToKeepOpen + 1);
        return;
    #endif

    for (int i = getHighestFileDescriptor(); i > lastToKeepOpen; i--) {
        int ret;
        do {
            ret = close(i);
        } while (ret == -1 && errno == EINTR);
    }
}
 

-2voto

alamar Points 6376

Pourquoi ne fermez-vous pas tous les descripteurs de 0 à 10000, par exemple?

Ce serait assez rapide et le pire qui arriverait serait l'EBADF.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X