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) : Solaris OpenSolaris Kernel