web-dev-qa-db-fra.com

Trouver des descripteurs de fichiers ouverts pour un processus Linux (code C)?

Je voulais trouver tous les fds ouverts pour un processus sous Linux.

Puis-je le faire avec les fonctions de la bibliothèque glib?

26
Ashish

Puisque vous êtes sous Linux, vous avez (presque certainement) le /proc système de fichiers monté. Cela signifie que la méthode la plus simple sera d'obtenir une liste du contenu de /proc/self/fd; chaque fichier est nommé d'après un FD. (Utilisation g_dir_open, g_dir_read_name et g_dir_close pour faire la liste, bien sûr.)

Sinon, obtenir les informations est modérément délicat (il n'y a pas d'API POSIX utile par exemple; c'est un domaine qui n'a pas été standardisé).

26
Donal Fellows

Voici du code que j'utilisais, je ne connaissais pas/proc/self (thx Donal!), Mais cette façon est probablement plus générique de toute façon. J'ai inclus les inclus requis pour toutes les fonctions en haut.

#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/resource.h>

#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif

/* implementation of Donal Fellows method */ 
int get_num_fds()
{
     int fd_count;
     char buf[64];
     struct dirent *dp;

     snprintf(buf, 64, "/proc/%i/fd/", getpid());

     fd_count = 0;
     DIR *dir = opendir(buf);
     while ((dp = readdir(dir)) != NULL) {
          fd_count++;
     }
     closedir(dir);
     return fd_count;
}

J'ai rencontré une fois un très mauvais problème avec des fuites de descripteurs de fichiers, et il s'avère que j'ai en fait codé la solution suggérée par Tom H.:

/* check whether a file-descriptor is valid */
int pth_util_fd_valid(int fd)
{
     if (fd < 3 || fd >= FD_SETSIZE)
          return FALSE;
     if (fcntl(fd, F_GETFL) == -1 && errno == EBADF)
          return FALSE;
     return TRUE;
}

/* check first 1024 (usual size of FD_SESIZE) file handles */
int test_fds()
{
     int i;
     int fd_dup;
     char errst[64];
     for (i = 0; i < FD_SETSIZE; i++) {
          *errst = 0;
          fd_dup = dup(i);
          if (fd_dup == -1) {
                strcpy(errst, strerror(errno));
                // EBADF  oldfd isn’t an open file descriptor, or newfd is out of the allowed range for file descriptors.
                // EBUSY  (Linux only) This may be returned by dup2() during a race condition with open(2) and dup().
                // EINTR  The dup2() call was interrupted by a signal; see signal(7).
                // EMFILE The process already has the maximum number of file descriptors open and tried to open a new one.
          } else {
                close(fd_dup);
                strcpy(errst, "dup() ok");
          }
          printf("%4i: %5i %24s %s\n", i, fcntl(i, F_GETOWN), fd_info(i), errst);
     }
     return 0;
}

Vous les voudrez probablement aussi, pour satisfaire le dernier printf ci-dessus ...

char *fcntl_flags(int flags)
{
    static char output[128];
    *output = 0;

    if (flags & O_RDONLY)
        strcat(output, "O_RDONLY ");
    if (flags & O_WRONLY)
        strcat(output, "O_WRONLY ");
    if (flags & O_RDWR)
        strcat(output, "O_RDWR ");
    if (flags & O_CREAT)
        strcat(output, "O_CREAT ");
    if (flags & O_EXCL)
        strcat(output, "O_EXCL ");
    if (flags & O_NOCTTY)
        strcat(output, "O_NOCTTY ");
    if (flags & O_TRUNC)
        strcat(output, "O_TRUNC ");
    if (flags & O_APPEND)
        strcat(output, "O_APPEND ");
    if (flags & O_NONBLOCK)
        strcat(output, "O_NONBLOCK ");
    if (flags & O_SYNC)
        strcat(output, "O_SYNC ");
    if (flags & O_ASYNC)
        strcat(output, "O_ASYNC ");

    return output;
}

char *fd_info(int fd)
{
    if (fd < 0 || fd >= FD_SETSIZE)
        return FALSE;
    // if (fcntl(fd, F_GETFL) == -1 && errno == EBADF)
    int rv = fcntl(fd, F_GETFL);
    return (rv == -1) ? strerror(errno) : fcntl_flags(rv);
}

FD_SETSIZE est généralement 1024, et le nombre maximal de fichiers par processus est généralement 1024. Si vous voulez être sûr, vous pouvez le remplacer par un appel à cette fonction, comme décrit par TomH.

#include <sys/time.h>
#include <sys/resource.h>

rlim_t get_rlimit_files()
{
    struct rlimit rlim;
    getrlimit(RLIMIT_NOFILE, &rlim);
    return rlim.rlim_cur;
}   

Si vous mettez tout cela ensemble dans un seul fichier (ce que j'ai fait, juste pour le vérifier), vous pouvez produire une sortie similaire à celle-ci pour confirmer que cela fonctionne comme annoncé:

0:     0                  O_RDWR  dup() ok
1:     0                O_WRONLY  dup() ok
2:     0                  O_RDWR  dup() ok
3:     0              O_NONBLOCK  dup() ok
4:     0     O_WRONLY O_NONBLOCK  dup() ok
5:    -1      Bad file descriptor Bad file descriptor
6:    -1      Bad file descriptor Bad file descriptor
7:    -1      Bad file descriptor Bad file descriptor
8:    -1      Bad file descriptor Bad file descriptor
9:    -1      Bad file descriptor Bad file descriptor

J'espère que cela répond à toutes vos questions, et au cas où vous vous poseriez la question, je suis effectivement venu ici chercher la réponse à la question posée par le PO, et en lisant la réponse, rappelez-vous que j'avais déjà écrit le code il y a des années. Prendre plaisir.

27
Orwellophile

Si vous pouvez identifier le processus via pid, vous pouvez simplement le faire

ls -l /proc/<pid>/fd | wc - l

En C, vous pouvez tout diriger et réutiliser la sortie ou vous pouvez compter les fichiers par vous-même dans le répertoire mentionné ci-dessus (méthode de comptage, par exemple ici Compter le nombre de fichiers dans un répertoire en utilisant C )

6
fyr

Parfois, C++ est une option, la solution de Donal utilisant boost :: filesystem:

#include <iostream>
#include <string>
#include <boost/filesystem.hpp>
#include <unistd.h>

namespace fs = boost::filesystem;

int main()
{
    std::string path = "/proc/" + std::to_string(::getpid()) + "/fd/";
    unsigned count = std::distance(fs::directory_iterator(path),
                                   fs::directory_iterator());
    std::cout << "Number of opened FDs: " << count << std::endl;
}
3
Janek Olszak

Si vous voulez dire comment pouvez-vous le faire par programme à partir du processus, la méthode normale (si légèrement horrible) consiste à faire une boucle sur tous les descripteurs possibles (utilisez getrlimit() pour lire RLIMIT_NOFILE À trouver la plage) appeler quelque chose comme fcntl(fd, F_GETFD, 0) sur chacun et vérifier les réponses EBADF pour voir lesquelles ne sont pas ouvertes.

Si vous voulez savoir à partir du shell quels fichiers un processus a ouverts, alors lsof -p <pid> Est ce que vous voulez.

2
TomH

la commande fstat répertorie tous les processus en cours d'exécution du système et leurs descripteurs ouverts.En outre, elle répertorie de quel type de descripteur il s'agit (fichier, socket, pipe, etc.) et essaie de donner une indication de ce que le descripteur lit ou écrit, par exemple sur quel système de fichiers et quel numéro d'inode sur ce système de fichiers

1
maheshgupta024