Quería encontrar todos los fds abiertos para un proceso en Linux.
¿Puedo hacerlo con las funciones de la biblioteca simplista?
Quería encontrar todos los fds abiertos para un proceso en Linux.
¿Puedo hacerlo con las funciones de la biblioteca simplista?
orwellófilo
Aquí hay un código que solía usar, no sabía sobre /proc/self (¡gracias Donal!), pero esta forma es probablemente más genérica de todos modos. He incluido las inclusiones requeridas para todas las funciones en la parte superior.
#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;
}
Pasé por un problema muy grave con la fuga de identificadores de archivos una vez, y resulta que en realidad codifiqué la solución que Tom H. sugirió:
/* 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;
}
Probablemente querrás estos también, para satisfacer la última impresión anterior…
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 suele ser 1024, y el máximo de archivos por proceso suele ser 1024. Si quiere estar seguro, puede reemplazarlo con una llamada a esta función, como lo describe 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 junta todo eso en un solo archivo (lo cual hice, solo para verificarlo), puede producir un resultado similar a este para confirmar que funciona como se anuncia:
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
Espero que responda cualquier pregunta que tenga, y en caso de que se lo pregunte, en realidad vine aquí buscando la respuesta a la pregunta que hizo el OP, y al leer la respuesta, recuerde que ya había escrito el código hace años. Disfrutar.
Tenga en cuenta que pth_util_fd_valid no funcionará con huecos en la tabla fd. Considere el siguiente caso: int fd = open(...); // fd =3
fd = open(...); // fd=4
close(4)
La función no alcanzará fd=4
– kfir
06/02/2015 a las 14:55
@kfir: pth_util_fd_valid solo informa la validez de un identificador de archivo dado, no contiene un bucle.
– Orwellófilo
14 de febrero de 2015 a las 16:24
El segundo ejemplo de código debe incluir sys/select.h para definir FD_SETSIZE.
– Andrew Domaszek
11 mayo 2017 a las 15:39
Sería más fácil escanear /proc/self/fd
o el ligeramente más portátil /dev/fd
que ensamblar un nombre de ruta basado en el ID del proceso actual.
– David Foerster
7 mayo 2021 a las 9:10
Becarios Donal
Ya que está en Linux, tiene (casi seguro) la /proc
sistema de archivos montado. Eso significa que el método más fácil será obtener una lista del contenido de /proc/self/fd
; cada archivo allí tiene el nombre de un FD. (Utilizar g_dir_open
, g_dir_read_name
y g_dir_close
para hacer la lista, por supuesto.)
De lo contrario, obtener la información es moderadamente incómodo (por ejemplo, no hay una API POSIX útil; esta es un área que no estaba estandarizada).
Para enumerarlos para otro proceso, enumere el directorio /proc/PID/fd
en su lugar (donde PID es la identificación del proceso en cuestión). Solo podrá verlos para algunos procesos si no es root.
– Becarios Donal
5 de julio de 2011 a las 13:18
El uso de /proc/*/pid es, por supuesto, muy específico de Linux y nada portátil, pero si eso no es un problema, entonces debería funcionar bien.
– TomH
5 de julio de 2011 a las 13:37
@Tom: La pregunta era etiquetado con linux
…
– Becarios Donal
17/07/2011 a las 21:40
Hay un pequeño escollo en lo que se busca: el /proc/self/fd
El directorio en sí mismo también cuenta como un descriptor de archivo abierto mientras lo escanea.
– C2H5OH
24 de enero de 2013 a las 15:08
@C2H5OH Sí. Pero las alternativas son terribles o igual de problemáticas (p. ej., ejecutar un subproceso para realizar el sondeo abrirá más FD).
– Becarios Donal
25 de enero de 2013 a las 18:03
fyr
Si puede identificar el proceso a través de pid, simplemente puede hacer
ls -l /proc/<pid>/fd | wc - l
En C, puede canalizar todo y reutilizar la salida o puede contar los archivos usted mismo en el directorio mencionado anteriormente (método de conteo, por ejemplo, aquí Contando la cantidad de archivos en un directorio usando C)
A veces, C++ es una opción, la solución de Donal usando 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;
}
Si quiere decir cómo puede hacerlo programáticamente desde dentro del proceso, entonces el método normal (aunque un poco horrible) es hacer algo como recorrer todos los descriptores posibles (use getrlimit()
leer RLIMIT_NOFILE
para encontrar el rango) llamando a algo como fcntl(fd, F_GETFD, 0)
en cada uno y verificando las respuestas de EBADF para ver cuáles no están abiertas.
Si quiere decir que desea averiguar desde el shell qué archivos ha abierto un proceso, entonces lsof -p <pid>
es lo que quieres
Veo una buena cantidad de procesos que tienen FD abiertos 0, 1, 2 y 255. Obtener 252 llamadas al sistema fallidas no sería agradable…
– Becarios Donal
5 de julio de 2011 a las 13:21
Todo depende: obviamente, no desea hacerlo en una pieza de código crítica para el rendimiento, pero aparte de eso, no es un gran problema. De todos modos, es más probable que el límite sea algo así como 2048 en un sistema Linux moderno.
– TomH
5 de julio de 2011 a las 13:36
RLIMIT_NOFILE solo le indica el máximo para los descriptores de archivos recién creados, no el límite de los descriptores de archivos abiertos actualmente, por lo que no puede usar getrlimit para descubrir un límite superior en los números de descriptores de archivos.
-Richard Kettlewell
5 de julio de 2011 a las 17:47
Sí, estoy tratando de hacer esto ahora como parte de un código de sandboxing. Quiero ejecutar un código de configuración arbitrario, luego enumerar todos los identificadores de archivos abiertos y cerrarlos, y luego usar setrlimit para evitar que se abran nuevos identificadores de archivos.
– gsteff
26 de diciembre de 2011 a las 17:18
maheshgupta024
El comando fstat enumera todos los procesos en ejecución del sistema y sus descriptores abiertos; además, enumera qué tipo de descriptor es (archivo, socket, tubería, etc.) e intenta dar una pista de lo que el descriptor está leyendo o escribiendo, como en qué sistema de archivos y qué número de inodo en ese sistema de archivos
Veo una buena cantidad de procesos que tienen FD abiertos 0, 1, 2 y 255. Obtener 252 llamadas al sistema fallidas no sería agradable…
– Becarios Donal
5 de julio de 2011 a las 13:21
Todo depende: obviamente, no desea hacerlo en una pieza de código crítica para el rendimiento, pero aparte de eso, no es un gran problema. De todos modos, es más probable que el límite sea algo así como 2048 en un sistema Linux moderno.
– TomH
5 de julio de 2011 a las 13:36
RLIMIT_NOFILE solo le indica el máximo para los descriptores de archivos recién creados, no el límite de los descriptores de archivos abiertos actualmente, por lo que no puede usar getrlimit para descubrir un límite superior en los números de descriptores de archivos.
-Richard Kettlewell
5 de julio de 2011 a las 17:47
Sí, estoy tratando de hacer esto ahora como parte de un código de sandboxing. Quiero ejecutar un código de configuración arbitrario, luego enumerar todos los identificadores de archivos abiertos y cerrarlos, y luego usar setrlimit para evitar que se abran nuevos identificadores de archivos.
– gsteff
26 de diciembre de 2011 a las 17:18