# rm-rf.es | Administración de sistemas

Blog de un SysAdmin Unix, Gnu/Linux, Windows y lo que haga falta.

Ver el tiempo que un proceso lleva en ejecución

En *nix, el comando ps permite listar y manejar procesos pero no se suele conocer la forma directa de visualizar el tiempo que lleva un proceso en ejecución. En realidad se puede sacar con la salida por defecto  a partir del valor de la columna STIME, que indica el momento en el que se inició (START TIME) pero hay que “calcular” el tiempo.

Para calcular el tiempo en ejecución de un proceso podemos utilizar el parámetro etime (elapsed time), el cual muestra en formato de fecha el tiempo que el proceso que le indiquemos lleva ejecutándose:

etime - elapsed time since the process was started, in the form [[dd-]hh:]mm:ss.

Lo combinaremos con otro parámetro (-p pidlist) en el que le diremos el PID a revisar y el formato (-o):

$ ps -p PID -o etime=
$ ps -p 4283 -o etime=
      06:18

También podemos especificar el “output” que queramos para ps especificando los argumentos separados por comas después del “-o”, un ejemplo:

$ ps -eo pid,comm,args,user,etime --sort user | more
  PID COMMAND         COMMAND                     USER         ELAPSED
 2969 fctsslvpndaemon /crypted/Proyectos/         4294967294  02:39:36
 1929 gnome-keyring-d /usr/bin/gnome-keyring-daem 1000        03:04:27
 1948 sh              /bin/sh /etc/xdg/xfce4/xini 1000        03:04:27
 1977 ssh-agent       /usr/bin/ssh-agent /usr/bin 1000        03:04:26
 1980 dbus-launch     /usr/bin/dbus-launch --exit 1000        03:04:26
 1981 dbus-daemon     //bin/dbus-daemon --fork -- 1000        03:04:26
 1989 xfconfd         /usr/lib/xfce4/xfconf/xfcon 1000        03:04:26
 1995 xscreensaver    xscreensaver -no-splash     1000        03:04:26

Sabiendo esto podéis jugar creando alias de ps con la salida que necesitéis u crear las combinaciones que estiméis oportunas.

Ocultar procesos a otros usuarios en Linux

A partir de la versión 3.2 del kernel de Linux es posible ocultar los procesos que un usuario está ejecutando al resto de usuarios del sistema gracias a la opción de montaje “hidepid“. Esto significa que no tendrá acceso a la información de los procesos en el filesystem /proc ni tampoco al ejecutar el típico ps o top:

$ ps -ef

En sistemas multiusuario puede ser una interesante opción de seguridad. Además el kernel 3.2 o superior está disponible en Red Hat y CentOS a partir de la versión 6.5. Hay que decir que root independientemente de que esta opción esté aplicada a /proc seguirá pudiendo ver todos los procesos.

Para configurar “hidepid” debemos aplicar la “flag” al punto de montaje, así que hay que remontarlo:

# mount | grep ^proc
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

Remontamos:

# mount -o remount,nosuid,noexec,hidepid=2 /proc

Recordad que de este modo no lo hacemos persistente, para ello hay que modificar el archivo /etc/fstab y añadir la opción al punto de montaje /proc:

proc	/proc	proc	defaults,hidepid=2	0 0

Nota: es posible que no esté definido el punto de montaje, en ese caso, lo añadís.

Si os preguntáis que significa el número 2, estas son los valores aplicables de hidepid:

  • hidepid 0: valor por defecto.
  • hidepid 1: un usuario estándar podrá ver únicamente sus procesos. Esto implica ver sólo sus procesos en el comando top, ps, htop… pero seguirá pudiendo ver los PIDs en el filesystem /proc
  • hidepid 2: Al igual que con la opción “1”, el usuario sólo podrá ver sus procesos y además tampoco tendrá acceso en /proc a los PID de otros usuarios.

Vamos a probarlo, ¿no?

Primero remontamos el filesystem aplicando el valor 2 a hidepid:

# mount -o remount,nosuid,noexec,hidepid=2 /proc
# mount | grep ^proc
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime,hidepid=2)

Abrimos una shell de un usuario estándar y verificamos que efectivamente, ¡funciona! no puede ver nada excepto sus procesos:

# su - foo
$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
foo       2288  2287  1 17:02 pts/0    00:00:00 -bash
foo       2311  2288  0 17:02 pts/0    00:00:00 ps -ef
$ top

top - 17:02:38 up 14 min,  2 users,  load average: 0.00, 0.01, 0.03
Tasks:   2 total,   1 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.7 sy,  0.0 ni, 98.2 id,  0.7 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:    760092 total,   198032 used,   562060 free,      764 buffers
KiB Swap:   630780 total,        0 used,   630780 free.    68452 cached Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
2288 foo       20   0  115352   1996   1624 S   0.0  0.3   0:00.02 bash
2313 foo       20   0  123552   1376   1052 R   0.0  0.2   0:00.01 top
$ ls -l /proc/
total 0
dr-xr-xr-x.  8 foo  foo                0 Sep  4 17:02 2288
dr-xr-xr-x.  8 foo  foo                0 Sep  4 17:03 2317
dr-xr-xr-x.  2 root root               0 Sep  4 17:03 acpi
dr-xr-xr-x.  5 root root               0 Sep  4 17:03 asound
-r--r--r--.  1 root root               0 Sep  4 17:03 buddyinfo
dr-xr-xr-x.  4 root root               0 Sep  4 17:03 bus
-r--r--r--.  1 root root               0 Sep  4 17:03 cgroups
-r--r--r--.  1 root root               0 Sep  4 17:03 cmdline
[...]

Y si revertimos el cambio todo vuelve a la normalidad:

# mount -o remount,nosuid,noexec,hidepid=0 /proc
# su - foo
$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 16:48 ?        00:00:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 23
root         2     0  0 16:48 ?        00:00:00 [kthreadd]
root         3     2  0 16:48 ?        00:00:00 [ksoftirqd/0]
root         5     2  0 16:48 ?        00:00:00 [kworker/0:0H]
root         6     2  0 16:48 ?        00:00:00 [kworker/u4:0]
root         7     2  0 16:48 ?        00:00:00 [migration/0]
root         8     2  0 16:48 ?        00:00:00 [rcu_bh]
root         9     2  0 16:48 ?        00:00:00 [rcuob/0]
root        10     2  0 16:48 ?        00:00:00 [rcuob/1]
root        11     2  0 16:48 ?        00:00:00 [rcu_sched]
root        12     2  0 16:48 ?        00:00:00 [rcuos/0]
root        13     2  0 16:48 ?        00:00:00 [rcuos/1]
root        14     2  0 16:48 ?        00:00:00 [watchdog/0]
root        15     2  0 16:48 ?        00:00:00 [watchdog/1]
[...]

Otro detalle más, podemos especificar usuarios o grupos a los que excluimos de la restricción. En otra palabras, UIDs/GIDs a los que no les afectará la medida:

# mount -o remount,nosuid,noexec,hidepid=2,uid=1200,gid=1300 /proc

Poner un proceso en segundo plano y desasociarlo de la shell

La situación es la siguiente, tenemos una shell bash ejecutando un proceso cuyo tiempo de ejecución va a ser muy largo y necesitamos cerrar la shell, por supuesto no podemos parar el proceso y arrancarlo de nuevo. Para solucionar este problema podemos hacer uso de los comandos bg (recordad el artículo sobre gestión de trabajos en BASH (jobs, fg, bg, &… que escribí hace un tiempo) y disown.

Por ejemplo, tenemos este comando en ejecución que simplemente escribe algo en un fichero de texto cada dos segundos. Lo tenemos en primer plano (fg):

Nota: los paréntesis son porque ejecuto una sub-shell para que interprete todos los comandos (separados por “;” como un único comando.

$ (while true; do echo "prueba - `date`" >> test.txt; sleep 2; done)

Puedo ver que efectivamente escribe cada dos segundos ejecutando un tail al fichero:

$ tail -f test.txt 
prueba - Thu Apr 11 21:11:56 CEST 2013
prueba - Thu Apr 11 21:11:58 CEST 2013
prueba - Thu Apr 11 21:12:00 CEST 2013
prueba - Thu Apr 11 21:12:02 CEST 2013
prueba - Thu Apr 11 21:12:04 CEST 2013

Ahora le vamos a mandar una señal SIGSTOP al proceso. Esto lo detendrá temporalmente. Para ello usamos Ctrl + z “^z”. En la misma shell de bash desde la que lo hemos parado vemos con el comando jobs que el proceso está parado:

$ jobs
[1]+  Stopped                 ( while true; do
    echo "prueba - `date`" >> test.txt; sleep 2;
done )

Ahora lo vamos a pasar a segundo plano (background) con el comando bg:

$ bg
[1]+ ( while true; do
    echo "prueba - `date`" >> test.txt; sleep 2;
done ) &

Y automáticamente el proceso seguirá funcionando, pero ahora ya en segundo plano:

$ tail -f test.txt
prueba - Thu Apr 11 21:17:34 CEST 2013
prueba - Thu Apr 11 21:17:36 CEST 2013
prueba - Thu Apr 11 21:17:38 CEST 2013
prueba - Thu Apr 11 21:17:40 CEST 2013
prueba - Thu Apr 11 21:17:42 CEST 2013

Finalmente ejecutamos el comando disown para desasociar el comando a la shell en la que nos encontramos, ya podremos entonces cerrar con tranquilidad la terminal. Básicamente lo que hace este comando es evitar que se mande una señal SIGHUP al proceso cuando cerramos la shell:

$ disown

Debug de procesos en GNU/Linux con strace

Hace unos meses vimos como hacer debug de procesos en Solaris con el comando truss y dije además que este comando era la alternativa a strace en GNU/Linux. Vamos a ver entonces las posibilidades que nos ofrece el comando strace. Nos permite visualizar en la salida estándar los errores, llamadas al sistema y señales que recibe el proceso. Esto implica ver todo el proceso de ejecución de un proceso desde que comienza hasta que termina a bajo nivel.

Hay dos formas muy sencillas de ejecutarlo, una es strace <COMANDO> y la otra strace -p <PID>. Vamos a ver por ejemplo las llamadas al sistema y señales que recibimos al ejecutar un simple comando date:

~$ strace date
execve("/bin/date", ["date"], [/* 42 vars */]) = 0
brk(0)                                  = 0x858f000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb774e000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=69920, ...}) = 0
mmap2(NULL, 69920, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb773c000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/librt.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320\30\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=30684, ...}) = 0
mmap2(NULL, 33360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xbee000
mmap2(0xbf5000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6) = 0xbf5000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0000\226\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1713640, ...}) = 0
mmap2(NULL, 1723100, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xce0000
mmap2(0xe7f000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19f) = 0xe7f000
mmap2(0xe82000, 10972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xe82000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0p[\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=124663, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb773b000
mmap2(NULL, 107008, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xa6e000
mmap2(0xa85000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16) = 0xa85000
mmap2(0xa87000, 4608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xa87000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb773a000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb773a6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xe7f000, 8192, PROT_READ)     = 0
mprotect(0xa85000, 4096, PROT_READ)     = 0
mprotect(0xbf5000, 4096, PROT_READ)     = 0
mprotect(0x8055000, 4096, PROT_READ)    = 0
mprotect(0xf67000, 4096, PROT_READ)     = 0
munmap(0xb773c000, 69920)               = 0
set_tid_address(0xb773a728)             = 4028
set_robust_list(0xb773a730, 0xc)        = 0
futex(0xbf808d94, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, NULL, b773a6c0) = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGRTMIN, {0xa73570, [], SA_SIGINFO}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {0xa735f0, [], SA_RESTART|SA_SIGINFO}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM_INFINITY}) = 0
uname({sys="Linux", node="Xubuntu", ...}) = 0
brk(0)                                  = 0x858f000
brk(0x85b0000)                          = 0x85b0000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=7245312, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb753a000
mmap2(NULL, 4096, PROT_READ, MAP_PRIVATE, 3, 0x46c) = 0xb774d000
close(3)                                = 0
clock_gettime(CLOCK_REALTIME, {1351934071, 598373225}) = 0
open("/etc/localtime", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=2593, ...}) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=2593, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb774c000
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\10\0\0\0\10\0\0\0\0"..., 4096) = 2593
_llseek(3, -28, [2565], SEEK_CUR)       = 0
read(3, "\nCET-1CEST,M3.5.0,M10.5.0/3\n", 4096) = 28
close(3)                                = 0
munmap(0xb774c000, 4096)                = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb774c000
write(1, "Sat Nov  3 10:14:31 CET 2012\n", 29Sat Nov  3 10:14:31 CET 2012
) = 29
close(1)                                = 0
munmap(0xb774c000, 4096)                = 0
close(2)                                = 0
exit_group(0)                           = ?

Como podéis observar, podemos ver todo el proceso de ejecución del comando, las llamadas a las librerías del sistema, ficheros que se han utilizado para la ejecución del comando, funciones, señales que recibimos de cada parte del proceso, etc.

A primera vista parece que vaya a resultar muy complicado hacer debug de un proceso con esta información, pero no es necesario conocer ni entender todo, puedes detectar cuando por ejemplo se llama a un fichero que no existe o cuando los permisos del mismo no dejan al comando ejecutar una determinada tarea dentro del marco de ejecución del proceso.

La otra forma de ejecutarlo que más se suele utilizar es pasandole un PID, esto es útil cuando el proceso que queremos revisar está ya en ejecución. Sería similar a un “tail -f” que haríamos a un fichero de log:

En este caso lo ejecutamos como root seguido del PID:

# strace -p 1967
Process 1697 attached - interrupt to quit
ppoll([{fd=5, events=POLLIN}, {fd=20, events=POLLIN}, {fd=4, events=POLLIN}, {fd=32, events=POLLIN}, {fd=33, events=POLLIN}, {fd=35, events=POLLIN}, {fd=9, events=POLLIN}, {fd=29, events=POLLIN}, {fd=31, events=POLLIN|POLLERR|POLLHUP}, {fd=31, events=0}, {fd=30, events=POLLIN}, {fd=23, events=POLLIN}, {fd=26, events=POLLIN}, {fd=16, events=POLLIN}, {fd=19, events=POLLIN}, {fd=15, events=POLLIN|POLLERR|POLLHUP}, {fd=15, events=0}, {fd=14, events=POLLIN}, {fd=13, events=POLLIN}, {fd=7, events=POLLIN}], 20, NULL, NULL, 8) = 1 ([{fd=19, revents=POLLIN}])
read(19, "\1\0\0\0\0\0\0\0", 8)         = 8
send(20, "\0\0\0\24\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0", 20, MSG_NOSIGNAL) = 20
ppoll([{fd=5, events=POLLIN}, {fd=20, events=POLLIN|POLLOUT}, {fd=4, events=POLLIN}, {fd=32, events=POLLIN}, {fd=33, events=POLLIN}, {fd=35, events=POLLIN}, {fd=9, events=POLLIN}, {fd=29, events=POLLIN}, {fd=31, events=POLLIN|POLLERR|POLLHUP}, {fd=31, events=0}, {fd=30, events=POLLIN}, {fd=23, events=POLLIN}, {fd=26, events=POLLIN}, {fd=16, events=POLLIN}, {fd=19, events=POLLIN}, {fd=15, events=POLLIN|POLLERR|POLLHUP}, {fd=15, events=0}, {fd=14, events=POLLIN}, {fd=13, events=POLLIN}, {fd=7, events=POLLIN}], 20, NULL, NULL, 8) = 1 ([{fd=20, revents=POLLOUT}])
send(20, "L\0\0\0=L\377\377\377\377L\0\0\0\2L\0\0\275 ", 20, MSG_NOSIGNAL) = 20
ppoll([{fd=5, events=POLLIN}, {fd=20, events=POLLIN|POLLOUT}, {fd=4, events=POLLIN}, {fd=32, events=POLLIN}, {fd=33, events=POLLIN}, {fd=35, events=POLLIN}, {fd=9, events=POLLIN}, {fd=29, events=POLLIN}, {fd=31, events=POLLIN|POLLERR|POLLHUP}, {fd=31, events=0}, {fd=30, events=POLLIN}, {fd=23, events=POLLIN}, {fd=26, events=POLLIN}, {fd=16, events=POLLIN}, {fd=19, events=POLLIN}, {fd=15, events=POLLIN|POLLERR|POLLHUP}, {fd=15, events=0}, {fd=14, events=POLLIN}, {fd=13, events=POLLIN}, {fd=7, events=POLLIN}], 20, NULL, NULL, 8) = 1 ([{fd=20, revents=POLLIN|POLLOUT}])
recvmsg(20, {msg_name(0)=NULL, msg_iov(1)=[{"\0\0\0\
...
...

También podríamos evitar ver toda la salida directa por pantalla y mostrar con el parámetro “-c” un resumen de las llamadas y errores durante la ejecución:

# strace -c date
Sat Nov  3 10:22:31 CET 2012
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  -nan    0.000000           0         5           read
  -nan    0.000000           0         1           write
  -nan    0.000000           0         6           open
  -nan    0.000000           0         8           close
  -nan    0.000000           0         1           execve
  -nan    0.000000           0         5         5 access
  -nan    0.000000           0         3           brk
  -nan    0.000000           0         3           munmap
  -nan    0.000000           0         1           uname
  -nan    0.000000           0         5           mprotect
  -nan    0.000000           0         1           _llseek
  -nan    0.000000           0         2           rt_sigaction
  -nan    0.000000           0         1           rt_sigprocmask
  -nan    0.000000           0         1           getrlimit
  -nan    0.000000           0        16           mmap2
  -nan    0.000000           0         8           fstat64
  -nan    0.000000           0         1         1 futex
  -nan    0.000000           0         1           set_thread_area
  -nan    0.000000           0         1           set_tid_address
  -nan    0.000000           0         1           clock_gettime
  -nan    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                    72         6 total

Para un uso más avanzado del comando y entender su salida como siempre os recomiendo su página man:

$ man strace

Debug de procesos en Solaris con Truss

Podríamos decir que truss es el equivalente en Solaris a strace de Linux, si bien existen también otras alternativas como dtrace que también cumplen perfectamente esta función.

Truss es una utilidad que permite ejecutar el comando especificado o un proceso concreto y muestra durante su ejecución las llamadas al sistema que ejecuta y las señales que recibe. Esto permite ver todo el proceso de ejecución y hacer debug de cualquier error o fault que ocurre.

Vamos a ver unos ejemplos. Lo más básico sería ejecutar truss seguido del comando del que queremos ver su debug, en el siguiente ejemplo simplemente ejecutamos el comando df:

# truss df
execve("/usr/gnu/bin/df", 0x08047E6C, 0x08047E74)  argc = 1
sysinfo(SI_MACHINE, "i86pc", 257)               = 6
mmap(0x00000000, 32, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1BB0000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1BA0000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1B90000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1B80000
memcntl(0xD1BB8000, 32064, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
memcntl(0x08050000, 15312, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
resolvepath("/usr/lib/ld.so.1", "/lib/ld.so.1", 1023) = 12
resolvepath("/usr/gnu/bin/df", "/usr/gnu/bin/df", 1023) = 15
sysconfig(_CONFIG_PAGESIZE)                     = 4096
stat64("/usr/gnu/bin/df", 0x08047AB0)           = 0
open("/var/ld/ld.config", O_RDONLY)             Err#2 ENOENT
stat64("/lib/libc.so.1", 0x08047260)            = 0
...
...
...

No muestro todo el output debido a su longitud, podemos volcarlo a un fichero para analizarlo mejor en lugar de la salida stderr:

# truss -o salida.out df

En el siguiente ejemplo hacemos debug de un proceso que ya se encuentra en ejecución:

# truss -rall -wall -f -p <PID>

“-rall” implica ver todos los datos de lectura y “-wall” todos los de escritura, con “-f” vemos los procesos fordked y “-p” especifica el PID.

Podéis ver más ejemplos e información del comando en la página man correspondiente. A continuación podéis ver un par:

$ man truss

Trazar las llamadas de sistema open, close, read y write únicamente:

# truss -t open,close,read,write find . -print >salida.out

Trazar todas las llamads a funciones a nivel de usuario desde y hacia cualquier sitio:

# truss -u a.out -u ld:: -u :: <comando>

Gestión y monitorización de servicios con Monit

Creo que todos los administradores de sistemas coincidimos en que un buen sysadmin es aquel que es capaz de conseguir que los sistemas se autogestionen en la mayor parte de lo posible. Esto implica, además de disponer de un buen sistema de monitorización tipo Big Brother, Nagios o Cacti, disponer de herramientas para que cuando un servicio o aplicación falle y se pare, automáticamente intenten reiniciarla sin necesidad del administrador.

Monit es una herramienta muy sencilla de instalar y configurar que se encargará de hacer esta tarea por nosotros. Monit permite monitorizar y gestionar procesos, ficheros, directorios y sistemas de ficheros.

Su instalación es realmente sencilla, podéis utilizar gestores de descargas como YUM o APT o compilarlo desde el código fuente (no tiene ningún misterio). Descarga aquí.

En CentOS o RHEL por ejemplo:

# yum install monit

Una vez instalado, simplemente tenéis que personalizar la configuración, encontraréis el fichero en la ruta /etc/monit.conf. Cada sección o directiva está perfectamente explicada, no obstante las más importantes:

set daemon  60             # check services at 2-minute intervals
     with start delay 240  # optional: delay the first check by 4-minutes (by
#                          # default Monit check immediately after Monit start)

set daemon indica que levantemos monit como demonio, a continuación le especificamos el intervalo de tiempo para el chequeo de servicios, también indicamos un retardo de 4 minutos para que no los chequee nada más arrancar.

 set mailserver localhost               # primary mailserver
set alert xxx@xxx.com/pre>

Con set mailserver indicaremos el servidor de correo a utilizar para el envío de alertas. Set alert especificará la cuenta de correo a la que enviar los avisos.

Ya pasamos a la configuración de servicios y lo que vamos a monitorizar. En primera instancia podemos indicar que nos envíe correos cuando el sistema alcance una determinada carga o uso de CPU, memoria, etc. Descomentar y personalizar según requerimientos:

#  check system myhost.mydomain.tld
#    if loadavg (1min) > 4 then alert
#    if loadavg (5min) > 2 then alert
#    if memory usage > 75% then alert
#    if swap usage > 25% then alert
#    if cpu usage (user) > 70% then alert
#    if cpu usage (system) > 30% then alert
#    if cpu usage (wait) > 20% then alert

También podemos monitorizar cambios en ficheros, permisos, checksum, usuarios y grupos, etc

## Check if a file exists, checksum, permissions, uid and gid. In addition
## to alert recipients in the global section, customized alert can be sent to
## additional recipients by specifying a local alert handler. The service may
## be grouped using the GROUP option. More than one group can be specified by
## repeating the 'group name' statement.
#
#  check file apache_bin with path /usr/local/apache/bin/httpd
#    if failed checksum and
#       expect the sum 8f7f419955cefa0b33a2ba316cba3659 then unmonitor
#    if failed permission 755 then unmonitor
#    if failed uid root then unmonitor
#    if failed gid root then unmonitor
#    alert security@foo.bar on {
#           checksum, permission, uid, gid, unmonitor
#        } with the mail-format { subject: Alarm! }
#    group server

En la parte de servicios podemos configurar servicios como Apache, Exim, FTP, MySQL, etc para que cuando haya un problema y se paren Monit los levante automáticamente, a continuación os dejo el ejemplo para Apache (rutas y demás pueden diferir según la instalación):

  check process apache with pidfile /usr/local/apache/logs/httpd.pid
    start program = "/etc/init.d/httpd start" with timeout 60 seconds
    stop program  = "/etc/init.d/httpd stop"
    if cpu > 60% for 2 cycles then alert
    if cpu > 80% for 5 cycles then restart
    if totalmem > 200.0 MB for 5 cycles then restart
    if children > 250 then restart
    if loadavg(5min) greater than 10 for 8 cycles then stop
    if failed host rm-rf.es port 80 protocol http
       and request "/index.phpsy"
       then restart
    if failed port 443 type tcpssl protocol http
       with timeout 15 seconds
       then restart
    if 3 restarts within 5 cycles then timeout
    #depends on apachectl
    group server

Como véis monitoriza el PID de apache y si no lo encuentra lo reinicia. También monitoriza el uso de CPU y memoria y si es muy elevado en X ciclos de comprobación lo reinicia o avisa al administrador, lo mismo para el SSL, etc.

En la ayuda de Monit encontraréis información sobre como añadir más servicios, aunque simplemente es coger el de apache como base y modificar parámetros. Otro ejemplo para MySQL:

check process mysql with pidfile /var/lib/mysql/mysql.pid
   group database
   start program = "/etc/init.d/mysql start"
   stop program = "/etc/init.d/mysql stop"
   if failed host 127.0.0.1 port 3306 then restart
   if 5 restarts within 5 cycles then timeout

Una vez que tengamos todo configurado, solo queda iniciar el servicio y comprobar su funcionamiento, por ejemplo tirando uno de los servicios monitorizados.

# /etc/init.d/monit start
Starting monit: Starting monit daemon
Monit start delay set -- pause for 240s
                                                           [  OK  ]

Tener Monit instalado es un buen ejemplo de monitorización proactiva en vuestros servidores. Os sirva para evitar tener que levantaros alguna que otra madrugada a levantar un servicio.

Asignar prioridad de CPU a procesos en Linux con nice

El comando nice en Linux nos permite modificar la prioridad de un proceso frente al resto dentro del sistema. El kernel Linux es el encargado de planificar y asignar tiempo de CPU a cada uno de los procesos que corren en el sistema. El rango de asignación de prioridad disponible es de -20 a 20 , siendo -20 la mayor prioridad y 20 la menor. Gracias a nice podemos asegurarnos que en momentos de usos elevados de CPU los procesos adecuados reciban el mayor % de la misma.

La sintaxis del comando nice es la siguiente:

# nice -n<PRIORIDAD> COMANDO

Así pues, si quisiéramos ejecutar un proceso con la mayor prioridad disponible (en este caso el script prueba.pl) haríamos lo siguiente:

# nice -n-20 ./prueba.pl

Nota: es necesario ser superusuario para poder utilizar el comando nice

Ejecutando el comando ps observaremos como efectivamente la prioridad ha sido asignada correctamente:

# ps al
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND

4     0  4204  3985   0 -20   1872   504 n_tty_ S<+  pts/0      0:00 /bin/sh ./prueba.pl

Nota: hay que pasar el parámetro l para visualizar la columna de nice.

Si en cambio quisieramos que tuviera la mínima prioridad pues haríamos todo lo contrario:

# nice -n20 ./prueba.pl

Si ejecutamos el comando nice sin parámetros nos indicará el valor asignado actualmente para nice:

# nice
0

Virtuozzo: Averiguar a que contenedor pertenece un proceso

Dentro de un nodo físico Virtuozzo, si ejecutamos un top podemos ver los procesos que más CPU y memoria están consumiendo, pero no podemos averiguar a simple vista a que máquina virtual pertenece. Para averiguarlo tenemos dos opciones.

La primera es sustituir el uso de top por vztop. Se trata de una versión de top modificada por Virtuozzo a través de la cual aparece una nueva columna que muestra el ID de la máquina virtual.

$ vztop

vztop - 16:59:44 up 72 days,  5:11,  1 user,  load average: 2.04, 2.60, 2.76
Tasks: 528 total,   1 running, 522 sleeping,   0 stopped,   5 zombie
Cpu(s): 19.6% us,  7.4% sy,  0.2% ni, 62.2% id,  9.9% wa,  0.0% hi,  0.6% si
Mem:   4038416k total,  3948496k used,    89920k free,   173288k buffers
Swap:  4192956k total,      160k used,  4192796k free,  1701956k cached

 CTID   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  104 20875 nobody    15   0 41748  32m 1508 S   11  0.8   0:00.17 httpd
  104 11085 nobody    15   0 41904  33m 1564 S   10  0.8   0:01.53 httpd
  104 20899 nobody    15   0 41748  32m 1508 S   10  0.8   0:00.09 httpd
  104 18757 nobody    15   0 42016  33m 1556 S    8  0.8   0:00.64 httpd
  104 21077 527       18   0     0    0    0 Z    8  0.0   0:00.04 php
  104 21079 527       18   0     0    0    0 Z    8  0.0   0:00.04 php
  104 18752 nobody    15   0 41632  32m 1536 S    6  0.8   0:00.50 httpd
  104 19390 nobody    15   0 41748  32m 1532 S    6  0.8   0:00.44 httpd
  104 20884 nobody    15   0 41932  33m 1600 S    6  0.8   0:00.11 httpd

La otra opción es utilizar otro comando propio de Virtuozzo, vzpid. Su uso es sencillo, hay que pasar como parámetro el PID y nos dirá el contenedor/máquina virtual al que pertenece:

# vzpid 20845
Pid	VEID	Name
20845	109	httpd

109 es el ID de la máquina virtual, ejecutando un vzlist veremos el hostname y más información sobre la misma:

# vzlist 109
      CTID      NPROC STATUS    IP_ADDR         HOSTNAME
       109        90 running   192.168.0.155       vps1.test.com

Linux: Listar procesos por % cpu o uso de memoria

Con estas dos secuencias de comandos podréis ver de un modo rápido y sencillo los procesos ordenados por uso (%) de CPU y memoria. Por supuesto no es necesario (ni práctico) escribir el comando completo en la shell cada vez que lo queráis ejecutar, os recomiendo crear un alias específico para cada uno de ellos.

Listar procesos por % cpu, veréis que eliminamos aquellos que usan 0.0 de CPU con sed (sed ‘/^ 0.0 /d):

ps -e -o pcpu,cpu,nice,state,cputime,args --sort pcpu | sed '/^ 0.0 /d'

Ejemplo:

$ ps -e -o pcpu,cpu,nice,state,cputime,args --sort pcpu | sed '/^ 0.0 /d'
%CPU CPU  NI S     TIME COMMAND
0.2   -   0 S 00:00:24 metacity
0.2   -   0 S 00:00:00 /usr/bin/python /usr/bin/terminator
0.2   -   0 S 00:00:28 gnome-screensaver
0.2   -   0 S 00:00:31 gnome-panel
4.6   -   0 S 00:08:23 /usr/bin/pulseaudio --start
7.4   -   0 S 00:13:24 /usr/X11R6/bin/X :0 -br -audit 0 -auth /var/lib/gdm/:0.Xauth -nolisten tcp vt7
7.5   -   0 S 00:04:31 rhythmbox
14.4   -   0 S 00:08:50 /usr/lib/firefox-3.5.4/firefox-3.5

Listar procesos por uso de memoria (en KB):

ps -e -orss=,args= | sort -b -k1,1n | pr -TW$COLUMNS

Ejemplo (reducido):

$ ps -e -orss=,args= | sort -b -k1,1n | pr -TW$COLUMNS
23964 gnome-panel
26168 nautilus
26256 /usr/bin/python /usr/bin/terminator
58340 /usr/X11R6/bin/X :0 -br -audit 0 -auth /var/lib/gdm/:0.Xauth -nolisten tcp vt7
58728 rhythmbox
128736 /usr/lib/firefox-3.5.4/firefox-3.5

Encontrado en pixelbeat

El comando ps: Listar y manejar procesos

Una entrada para los que se inician en el mundo Unix/Linux. El comando ps muestra por pantalla un listado de los procesos que se están ejecutando en el sistema.

Si no añadimos ningún parámetro, ps mostrará los procesos del usuario con el que estamos logueados. Por otra parte, los parámetros más básicos a conocer son los siguientes:

-aux Lista los procesos de todos los usuarios con información añadida (destacamos más abajo).

-a Lista los procesos de todos los usuarios.

-u Lista información del proceso como por ejemplo el usuario que lo está corriendo, la utilización de Cpu y memoria, etc.

-x Lista procesos de todas las terminales y usuarios

-l Muestra información que incluye el UID y el valor “nice“.

–forest – Muestra el listado procesos en un formato tipo árbol que permite ver como los procesos interactuan entre si, podría ser algo similar al comando pstree.
Ejemplo de uso del comando ps:

$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.3   2844  1692 ?        Ss   18:13   0:01 /sbin/init
root         2  0.0  0.0      0     0 ?        S<   18:13   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S<   18:13   0:00 [migration/0]
root         4  0.0  0.0      0     0 ?        S<   18:13   0:00 [ksoftirqd/0]
root         5  0.0  0.0      0     0 ?        S<   18:13   0:00 [watchdog/0]
root         6  0.0  0.0      0     0 ?        S<   18:13   0:00 [migration/1]
root         7  0.0  0.0      0     0 ?        S<   18:13   0:00 [ksoftirqd/1]
root         8  0.0  0.0      0     0 ?        S<   18:13   0:00 [watchdog/1]
root         9  0.0  0.0      0     0 ?        S<   18:13   0:00 [events/0]
root        10  0.0  0.0      0     0 ?        S<   18:13   0:00 [events/1]
root        11  0.0  0.0      0     0 ?        S<   18:13   0:00 [khelper]
root        47  0.0  0.0      0     0 ?        S<   18:13   0:00 [kblockd/0]
root        48  0.0  0.0      0     0 ?        S<   18:13   0:00 [kblockd/1]
root        51  0.0  0.0      0     0 ?        S<   18:13   0:00 [kacpid]
root        52  0.0  0.0      0     0 ?        S<   18:13   0:00 [kacpi_notify]
root       128  0.0  0.0      0     0 ?        S<   18:13   0:00 [kseriod]
root       168  0.0  0.0      0     0 ?        S    18:13   0:00 [pdflush]
root       169  0.0  0.0      0     0 ?        S    18:13   0:00 [pdflush]
root       170  0.0  0.0      0     0 ?        S<   18:13   0:00 [kswapd0]
...
...
...

Podéis revisar también la entrada en la que hablaba del comando pgrep o top, pues os puede ser de utilidad, en breve hablaremos de comandos relacionados como kill, killall , pkill, etc.