Solaris: Buceando en el Kernel con libkvm

Kernel, OpenSolaris Dejar un comentario

En la serie de artículos, que comenzó con Solaris: Buceando en el Kernel con mdb(I), analizamos cómo podemos utilizar la herramienta MDB para conocer qué está haciendo el Kernel, utilizando los distintos comandos y walkers podemos obtener una idea de qué esta ocurriendo en el sistema, en otro artículo veremos como podemos implementar nuestros propios comandos en MDB, pero ahora, vamos a ver qué otra herramienta está disponible en nuestro sistema, que nos ayudará para que podamos bucear en el Kernel. En este artículo no vamos a ver una herramienta propiamente dicha, analizaremos una de las librerías del sistema /usr/lib/libkvm.so.1, con la cual, podremos construir nuestros propios comandos o simplemente pequeños programas para identificar elementos en nuestro sistema.

libkvm

Consiste en una librería de funciones que nos permiten acceder al espacio de direcciones del Kernel, el acceso se realiza mediante la imagen del espacio de direcciones del Kernel que está en /dev/kmem y se trata de una sencilla interfaz que nos pemite movernos por dicho espacio de direcciones, utilizando funciones como open, write, read y otras. Mediante el comando man podemos acceder a toda la información sobre las distintas funciones que se implementan en la librería, por lo que no vamos a profundizar en todas. En la siguiente lista podemos ver las funciones que están disponibles en la librería libkvm:

   kvm_close            kvm_getcmd            kvm_getproc
   kvm_getu             kvm_kread             kvm_kwrite
   kvm_nextproc         kvm_nlist             kvm_open
   kvm_read             kvm_setproc           kvm_uread
   kvm_uwrite           kvm_write

Como podemos ver, la librería presenta una serie de funciones, las cuales conforman un sencillo interfaz, básicamente podremos abrir, leer, escribir y buscar un proceso (mejor dicho, buscar la estructura de datos que define a un proceso en el Kernel). Cuando vayamos a utilizar la librería libkvm, es muy importante conocer si nuestro sistema se está ejecutando en 32bits o en 64bits, ya que esta información la necesitaremos a la hora de compilar nuestros programas. Para conocer el modo en que está corriendo nuestro Kernel podemos utiliza el comando isainfo.

 

(root@huelva)# isainfo -b
64
(root@huelva)#

Con el parámetro -b, el comando nos devolverá el número de bits que se están utilizando para el espacio de direcciones, en nuestro ejemplo, las direcciones son de 64 bits.

Ejemplo: kvm_fd_list.c

Ya hemos visto que la librería libkvm consiste en un sencillo interfaz para poder bucear en el Kernel, no vamos a profundizar en cada una de las funciones de la librería, ya que esta información la podemos encontrar en el man, lo que vamos a hacer es desarrollar un sencillo ejemplo, con el que intentaremos conseguir, por un lado demostrar lo fácil que es consultar información del Kernel y por otro lado, demostrar su potencia para desarrollar ciertos comandos. En nuestro ejemplo, vamos a intentar imitar la salida del comando pfiles, este comando devuelve la lista de descriptores de ficheros abiertos por un proceso. En el siguiente ejemplo, podemos ver la salida típica del comando pfiles.

 

(root@huelva)# pfiles 24533
24533:  /usr/local/apache2/bin/httpd -k start
  Current rlimit: 65536 file descriptors
   0: S_IFCHR mode:0666 dev:273,0 ino:3401 uid:0 gid:3 rdev:13,2
      O_RDONLY
   1: S_IFCHR mode:0666 dev:273,0 ino:3401 uid:0 gid:3 rdev:13,2
      O_WRONLY|O_CREAT|O_TRUNC
   2: S_IFREG mode:0644 dev:273,0 ino:156991 uid:0 gid:1 size:9476644
      O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE
   3: S_IFSOCK mode:0666 dev:312,0 ino:13178 uid:0 gid:0 size:0
      O_RDWR
        sockname: AF_INET6 ::  port: 80
   4: S_IFDOOR mode:0444 dev:318,0 ino:57 uid:0 gid:0 size:0
      O_RDONLY|O_LARGEFILE FD_CLOEXEC  door to nscd[476]
   5: S_IFIFO mode:0000 dev:313,0 ino:10931863 uid:0 gid:1 size:0
      O_RDWR|O_NONBLOCK
   6: S_IFIFO mode:0000 dev:313,0 ino:10931863 uid:0 gid:1 size:0
      O_RDWR
   7: S_IFREG mode:0644 dev:273,0 ino:156991 uid:0 gid:1 size:9476644
      O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE
   8: S_IFREG mode:0644 dev:273,0 ino:156997 uid:0 gid:1 size:45286270
      O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE
   9: S_IFREG mode:0600 dev:0,4 ino:925812982 uid:0 gid:1 size:0
      O_RDWR|O_CREAT|O_EXCL
  10: S_IFREG mode:0644 dev:273,0 ino:156984 uid:0 gid:1 size:0
      O_WRONLY|O_CREAT|O_EXCL
(root@huelva)#

Antes de que veamos el código completo de nuestro ejemplo kvm_fd_list.c, vamos, de una forma rápida, a ver qué va a hacer nuestro programa.

  • Abrir el fichero con la imagen del espacio de direcciones del Kernel, utilizando la función kvm_open
  • Buscar la estructura de datos de un proceso determinado, para ello utilizaremos la función kvm_getproc
  • Obtener el número de descriptores de ficheros consultando el campo p_user.u_finfo.fi_nfiles de la estructura de datos del proceso.
  • Mediante un bucle recorrer el array de elementos de tipo uf_entry_t
  • Leer el campo file_t uf_entry_t.uf_file
  • Leer el campo vnode_t uf_file.f_vnode

Sería interesante que antes de continuar, echemos un vistazo al artículo Solaris: Buceando en el Kernel con mdb (II), donde se explica cómo podemos obtener la lista de ficheros abiertos por un proceso utilizando MDB, en este artículo se describen algunas de las estructuras de datos que se utilizarán aquí, para entender perfectamente el ejemplo que vamos a realizar, debemos tener conocimiento de algunas de estas estructuras de datos que se utilizarán.

A continuación podemos ver el código fuente del programa kvm_fd_list.c, por supuesto, este programa se ha escrito con la única finalidad de que sirva como ejemplo a la utilización de la librería libkvm, en ningún momento se pretende que reemplace al comando pfiles.

 

/*
 * Este programa acepta como parametro el PID de un proceso
 * y utiliza las fucniones de la libreria libkvm para obtener
 * la lista de los descriptores de ficheros abiertos por el proceso.
 *
 * Uso: kvm_fd_list

 *
 *
 PID del proceso.
 *
 * IMPORTANTE: Este programa debe compilarse con la opcion de soporte para
 *             64bits, en gcc es "-m64" y "-lkvm" para utiliza la libkvm
*/

#include < stdio.h >

#define _KERNEL

#include < kvm.h >
#include < fcntl.h >
#include < sys/file.h >
#include < sys/fs/ufs_inode.h >
#include < sys/fs/namenode.h >
#include < sys/fs/tmpnode.h >
#include < sys/fs/fifonode.h >
#include < sys/vfs.h >
#include < sys/fstyp.h >
#include < sys/fsid.h >

void main(int argc, char **argv)
{
int n;
int nfd;

char cad[80];
char cadena2[FSTYPSZ];

kvm_t *h_p;

struct pid pid;
proc_t *pproc;
int npid;

uf_entry_t *p_filist;
uf_entry_t ufentry;
file_t ufile;
vnode_t uvnode;
inode_t uinode;
struct vfs uvfsp;
int fstype;

struct namenode  unnode;
fifonode_t  ufnode;
struct tmpnode  utnode;

if (argc<2)
        {printf("Error: Falta el < pid >nn     Uso: %s < pid >nn",argv[0]);return;}

npid=0;
if(argc==2)
        {npid=atoi(argv[1]);}

h_p=kvm_open(NULL,NULL,NULL,O_RDONLY,cad);

if(h_p==NULL)
        {printf("n kvm_open ERROR %snn",cad);return;}

/*
 * Se obtiene un puntero a una estructura de tipo proc_t
*/
pproc=kvm_getproc(h_p,npid);

if(kvm_read(h_p,(pproc->p_pidp),&pid,sizeof(pid))<0)
        {printf(" Error");}

printf("n PID: %d  tPPID: %d ",pid.pid_id,pproc->p_ppid);
printf("n ARGS: %s  NARGS:%d",pproc->p_user.u_psargs,pproc->p_user.u_argc);

printf("nFDtADDR fi_listtuf_refcnttuf_flagtVnode      tFILETYPEtFSTYPEtinode");

nfd=pproc->p_user.u_finfo.fi_nfiles;

for(n=0;n < nfd;n++)
        {
        p_filist=&pproc->p_user.u_finfo.fi_list[n];

        if(kvm_read(h_p,p_filist,&ufentry,sizeof(ufentry))<0)
                {printf(" Error");}

        if(ufentry.uf_file > 0)
                {
                printf("n%dt0x%lllx ",n,&pproc->p_user.u_finfo.fi_list[n]);
                printf("t  %d       t %d",ufentry.uf_refcnt,ufentry.uf_flag);

                if(kvm_read(h_p,ufentry.uf_file,&ufile,sizeof(ufile))<0)
                        {printf(" Error");}

                printf("t0x%lllx",ufile.f_vnode);

                if(kvm_read(h_p,ufile.f_vnode,&uvnode,sizeof(uvnode))<0)
                        {printf(" Error");}

                printf(" t ");
                switch(uvnode.v_type)
                        {
                        case 0: printf("NON");break;
                        case 1: printf("REG");break;
                        case 2: printf("DIR");break;
                        case 3: printf("BLK");break;
                        case 4: printf("CHR");break;
                        case 5: printf("LNK");break;
                        case 6: printf("FIFO");break;
                        case 7: printf("DOOR");break;
                        case 8: printf("PROG");break;
                        case 9: printf("SOCK");break;
                        case 10: printf("BAD");break;
                        }

                if(kvm_read(h_p,uvnode.v_vfsp,&uvfsp,sizeof(uvfsp))<0)
                        {printf(" Error");}

                fstype=uvfsp.vfs_fstype;

                sysfs(GETFSTYP,fstype,cadena2);
                printf("     t %s",cadena2);

                if(strcmp(cadena2,"ufs")==0)
                        {
                        if(kvm_read(h_p,uvnode.v_data,&uinode,sizeof(uinode))<0)
                                {printf(" Error");}
                        printf(" t%llllu ",uinode.i_number);
                        }

                if(strcmp(cadena2,"fifofs")==0)
                        {
                        if(kvm_read(h_p,uvnode.v_data,&ufnode,sizeof(ufnode))<0)
                                {printf(" Error");}
                        printf(" t%llllu ",ufnode.fn_ino);
                        }

                if(strcmp(cadena2,"namefs")==0)
                        {
                        if(kvm_read(h_p,uvnode.v_data,&unnode,sizeof(unnode))<0)
                                {printf(" Error");}
                        printf(" t  ");
                        }

                if(strcmp(cadena2,"tmpfs")==0)
                        {
                        if(kvm_read(h_p,uvnode.v_data,&utnode,sizeof(utnode))<0)
                                {printf(" Error");}
                        printf(" t%llllu ",utnode.tn_attr.va_nodeid);
                        }
                }
        }
printf("n");
return;
}

Una vez hayamos compilado el programa en nuestro sistema, recordemos que le tenemos que indicar al compilador que debe utilizar el soporte de 64bits, en el caso de que nuestro sistema esté utilizando 64bits, podemos realizar un prueba pasando como parámetro el PID de un proceso, la salida que obtendríamos al ejecutar el programa sería parecida a la siguiente, dónde tendremos, una fila por cada descriptor de fichero que el proceso tenga abierto, en cada fila tendremos el identificador del descriptor, la dirección del campo fi_list, el contador de referencias, un flag, la dirección del VNode, el tipo de fichero, el tipo del FS y el número del inode.

 

(root@huelva)# ./kvm_fd_list  24533

 PID 24533      PPID4608
 ARGS:/usr/local/apache2/bin/httpd -k start  NARGS:3
FD      ADDR fi_list    uf_refcnt       uf_flag Vnode           FILETYPE        FSTYPE  inode
0       0x46bfb6c6000     0              0      0x30001c36d38    CHR             ufs    21643480
1       0x46bfb6c6028     0              0      0x30001c36d38    CHR             ufs    21643480
2       0x46bfb6c6050     0              0      0x300cfb217a0    REG             ufs    156991
3       0x46bfb6c6078     1              0      0x3000866f580    SOCK            ufs
4       0x46bfb6c60a0     0              1      0x300035f3d08    DOOR            namefs
5       0x46bfb6c60c8     0              0      0x362b6a46dc0    FIFO            fifofs 10931863
6       0x46bfb6c60f0     0              0      0x362b6a46ec8    FIFO            fifofs 10931863
7       0x46bfb6c6118     0              0      0x300cfb217a0    REG             ufs    156991
8       0x46bfb6c6140     0              0      0x300cfb211e8    REG             ufs    156997
9       0x46bfb6c6168     0              0      0x3c9b97647d0    REG             tmpfs  925812982
10      0x46bfb6c6190     0              0      0x300cfb222a0    REG             ufs    156984
(root@huelva)#

Como hemos comentado anteriormente, este es un sencillo ejemplo, el cual deberíamos pulir un poco más, para que realmente fuese útil, pero puede servir de ayuda a todos aquellos que se animen a utilizar la librería libkvm. Ahora el límite está en la imaginación o las necesidades de cada uno. Ya lo he comentado en alguno de los artículos anteriores, las herramientas como MDB o libkvm, nos pueden ayudar a entender cómo funciona nuestro sistema y por lo tanto, utilizarlas, nos ayudará a resolver futuros problemas, ya sean de rendimiento o de cualquier otro tipo. Más adelante, veremos cómo podemos utiliza libkvm para construir otros ejemplos que nos puedan ayudar a administrar nuestro sistema, siempre desde la perspectiva del administrador de sistemas.

Nota - Si tiene alguna pregunta o sugerencia sobre este artículo, por favor, no dude en ponerse en contacto conmigo mediante email en jjmora AT arrakis DOT es

<< | >>

Technorati Tag(s) :

Los comentarios están cerrados.

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