Solaris: Navegando en el /proc (IV)

Kernel, OpenSolaris Dejar un comentario

Hasta ahora hemos visto, la estructura básica del sistema de archivos /proc, la estructura de algunos de los fichero que lo forman y como podemos acceder al espacio de direcciones de un proceso simplemente utilizando las llamadas open() y read(), hasta hemos visto como cambiar el valor de una variable de un proceso. Este artículo nació de la charla que tuvimos algunos compañeros con los que trabajo, sobre la razón de que una de las aplicaciones con las que trabajamos estaba continuamente realizando llamadas poll() y necesitabamos conocer cuales son los descriptores de ficheros que están utilizando las distintas llamadas a poll().

truss

El primer paso consiste en realizar un pequeño estudio sobre el número de llamadas al sistema, su frecuencia y el tipo, para esta tarea podemos utiliza el comando truss, con la opción -c obtendremos las estadísticas de las llamadas a sistema.

Este puede ser un ejemplo de la salida del comando truss.


(root@huelva)# truss -c -p 25089
^C
syscall               seconds   calls  errors
read                     .000      36     12
write                    .000      12
close                    .000       1
fcntl                    .000       1
poll                     .010    1419
lwp_self                 .000       8
lwp_mutex_wakeup         .000      31
lwp_mutex_lock           .000      18
lwp_cond_wait            .002      95     61
lwp_cond_signal          .000       4
lwp_cond_broadcast       .000      30
lwp_schedctl             .000       8
accept                   .000       1
send                     .001      17
getsockname              .000       4
setsockopt               .000       1
                     --------  ------   ----
sys totals:              .015    1686     73
usr time:                .114
elapsed:                2.480
(root@huelva)#

Con los resultados anteriores no podemos considerar que tengamos un ejemplo, unicamente es un ejemplo para destacar que el número de llamadas poll() es bastante superior al del resto de llamadas. Ahora tenemos que averiguar si existen varias llamadas poll() distintas, en distintos procesos o si solo existe un poll, responsable de todas las llamadas, ejecutaremos el comando truss para que unicamente nos enseñe las llamadas poll().

(root@huelva)# truss -t poll -f -p 25089
25089/10:       poll(0x310FFD58, 3, 50)                         = 0
25089/46:       poll(0x00273E50, 3, -1)                         = 1
25089/46:       poll(0x00273E50, 4, -1)                         = 1
25089/46:       poll(0x27DFEE10, 1, 30000)                      = 1
25089/10:       poll(0x310FFD58, 3, 50)                         = 0
25089/46:       poll(0x00273E50, 3, -1)                         = 1
25089/46:       poll(0x00273E50, 4, -1)                         = 1
25089/46:       poll(0x27DFEE10, 1, 5000)                       = 1
...

En la salida del ejemplo podemos observar que la llamada poll() acepta como parámetros, el puntero a un array de elementos de tipo struct pollfd, el número de elementos del array y un timeout. Como conocemos la dirección del array de elementos podemos utilizar el fichero /proc/< PID >/as, que recordemos es el espacio de direcciones del proceso, para leer el contenido del array, para ello utilizaremos el siguiente programa en C.

proc_as_rd.c

/*
 * Este programa lee una dir de memoria y la interpreta
 * como dato de tipo "pollfd_t" el cual es una estructura
 * que contiene un descriptor de fichero y dos mascaras de
 * eventos.
 *
 * La llamada poll() utiliza un array de elementos de tipo "pollfd_t".
 *
 * Uso: proc_as_rd < pid >  0x< addr_hex > < iter >
 *
 *  < pid > PID del proceso.
 *  0x< addr_hex > es la dir de memoria en hexadecimal.
 *  < iter > es el numero de elementos del array
 *
 * IMPORTANTE: Este programa debe compilarse con la opcion de soporte para
 *             64bits, en gcc es "-m64"
*/

#include < stdio.h >
#include < sys/types.h >
#include < sys/stat.h >
#include < fcntl.h >

#include < poll.h >

main(int argc, char **argv)
{

int fd;
char s_dir[16];
char cadena[80];
char HEX[]="0123456789ABCDEF";
int pid;
int n,i;
long long addr;
char c;

int nelem;

pollfd_t dato;

if (argc<3)
        {printf("nn     Uso: %s
  0x nn",argv[0]);return;}

pid=atoi(argv[1]);
nelem=atoi(argv[3]);

/*
 * Se lee la dir de memoria en formato HEX
 * y se convierte en una dir de tipo "long long"
*/

if(argv[2][1]=='x')
        argv[2][1]='0';
        else
        {printf("nn     Uso: %s
  0x ");
	printf("nnLa direccion de memoria tiene que estar en formato hex 0xnn",argv[0]);return;}

sprintf(s_dir,"%016s",argv[2]);
addr=0;
for(n=0;n< sizeof(s_dir);n++)
        {
        i=0;

        while((HEX[i]!=toupper(s_dir[n]))&&(i<16))
                {i++;}

        if (i>15)
                {printf("nError: La direccion contiene caracteres no validosnn");return;}

        addr|=i;
        addr=addr<<4;
        }

addr=addr>>4;

/*
 * La variable addr contiene la dir de memoria
 * convertida de HEX -> long long
*/

printf("n PID: %d   n Dir. de memoria: 0x%llx n",pid,addr);
sprintf(cadena,"/proc/%d/as",pid);

fd=open(cadena,O_RDONLY);
if (fd<0)
        {printf("n Error: Abriendo el fichero %sn",cadena);return;}
printf("n Abriendo el fichero %sn",cadena);

for(n=0;n< nelem;n++)
        {

        if (lseek(fd,addr,SEEK_SET)<0)
                {printf("nError: Moviendo el puntero %sn",cadena);return;}

        if(read(fd,&dato,sizeof(dato))<0)
                {printf("nError: Escribiendo el fichero %sn",cadena);return;}

        printf("n Dir. de memoria 0x%lx (%ld)  n",addr,addr);
        printf("n typedef struct pollfd {");
        printf("n        int fd = %d",dato.fd);
        printf("n        short events = %d",dato.events);
        printf("n        short revents = %d",dato.revents);
        printf("n} pollfd_t;");

        printf("n----------------------n");
        addr=addr+(sizeof(pollfd_t));
        }
close(fd);
}

El programa abre el fichero /proc/< PID >/as, desplaza el puntero a la dirección 0xaddr_hex y leen tantas estructuras de tipo pollfd_t como le digamos con el parámetro < iter>. La salida será parecida a la siguiente, utilizaremos la información de las llamadas poll() que recuperamos con el comando truss, ejecutaremos proc_as_rd con el proceso 25089 y la dirección de memoria 0x27DFEE10

(root@huelva)# ./proc_as_rd 25089 0x27DFEE10 1

 PID: 25089
 Dir. de memoria: 0x27dfee10

 Abriendo el fichero /proc/25089/as

 Dir. de memoria 0x27dfee10 (668986896)

 typedef struct pollfd {
        int fd = 77
        short events = 39
        short revents = 24568
} pollfd_t;
----------------------
(root@huelva)#

La salida devuelve la información representada como una estructura struct pollfd, cuya definición podemos encontrar en el fichero de cabecera /usr/include/sys/poll.h, para nuestro ejemplo, la llamada poll() utiliza un array, el cual tiene solo un elemento, de elementos de tipo pollfd_t, cuyo campo fd almacena el descriptor de fichero 77. Podemos ver a qué fichero corresponde el descriptor de fichero 77 en el proceso 25089 mediante el comando pfiles


(root@huelva)# pfiles 25089
25089:
  Current rlimit: 1024 file descriptors
   0: S_IFIFO mode:0000 dev:301,0 ino:125986510 uid:10001 gid:10000 size:0
      O_RDWR
   1: S_IFREG mode:0644 dev:261,44006 ino:296 uid:10001 gid:10000 size:1340974
      O_RDWR|O_APPEND
   2: S_IFREG mode:0644 dev:261,44006 ino:297 uid:10001 gid:10000 size:2965
      O_RDWR|O_APPEND
 ...

  77: S_IFSOCK mode:0666 dev:300,0 ino:12816 uid:0 gid:0 size:0
      O_RDWR
        sockname: AF_INET 192.168.0.72  port: 64095
        peername: AF_INET 192.168.0.126  port: 5588

(root@huelva)#

En este caso, el descriptor de fichero corresponde con un socket, que la aplicación está utilizando, esta información nos puede ayudar para identificar cual es la causa para que la aplicación esté realizando un uso excesivo de llamadas poll().

Este artículo pretende enseñar lo sencillo que es trabajar con el fichero del espacio de direcciones del proceso y como nos puede ayudar a identificar problemas, ya que nos permite consultar la información del proceso que nos interese.

Solaris: Navegando en el /proc (III)<< | >>

Technorati Tag(s) :

Los comentarios están cerrados.

My name is null, /dev/null
Algunos derechos reservados. Licencia Creative Commons