A veces, debe asegurarse de que solo se esté ejecutando una instancia de un script de shell al mismo tiempo.
Por ejemplo, un trabajo cron que se ejecuta a través de crond que no proporciona
bloqueo por sí solo (por ejemplo, el crond predeterminado de Solaris).
Un patrón común para implementar el bloqueo es un código como este:
#!/bin/sh
LOCK=/var/tmp/mylock
if [ -f $LOCK ]; then # 'test' -> race begin
echo Job is already running!
exit 6
fi
touch $LOCK # 'set' -> race end
# do some work
rm $LOCK
Por supuesto, dicho código tiene una condición de carrera. Hay una ventana de tiempo en la que la
ejecución de dos instancias puede avanzar después de la línea 3 antes de que una pueda
tocar el $LOCK
archivo.
Para un trabajo cron esto no suele ser un problema porque tiene un intervalo de
minutos entre dos invocaciones.
Pero las cosas pueden salir mal, por ejemplo, cuando el archivo de bloqueo está en un servidor NFS,
que se bloquea. En ese caso, varios trabajos cron pueden bloquearse en la línea 3 y ponerse en cola. Si
el servidor NFS vuelve a estar activo, entonces tiene una manada atronadora de trabajos
en ejecución paralelos.
Buscando en la web encontré la herramienta lockrun que parece una buena
solución a ese problema. Con él, ejecuta un script que necesita bloqueo como
este:
$ lockrun --lockfile=/var/tmp/mylock myscript.sh
Puede poner esto en un envoltorio o usarlo desde su crontab.
Utiliza lockf()
(POSIX) si está disponible y recurre a flock()
(BSD). Y lockf()
el soporte para NFS debería estar relativamente extendido.
¿Existen alternativas a lockrun
? ?
¿Qué pasa con otros demonios cron? ¿Existen crond comunes que admitan el bloqueo de una
forma sensata? Una mirada rápida a la página del manual de Vixie Crond (predeterminada en
sistemas Debian/Ubuntu) no muestra nada sobre el bloqueo.
¿Sería una buena idea incluir una herramienta como lockrun
? en coreutils?
En mi opinión implementa un tema muy similar a timeout
, nice
y amigos.
Respuesta aceptada:
Aquí hay otra forma de bloquear el script de shell que puede evitar la condición de carrera que describió anteriormente, donde dos trabajos pueden pasar la línea 3. El noclobber
La opción funcionará en ksh y bash. No uses set noclobber
porque no debería estar escribiendo en csh/tcsh. 😉
lockfile=/var/tmp/mylock
if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then
trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
# do stuff here
# clean up after yourself, and release your trap
rm -f "$lockfile"
trap - INT TERM EXIT
else
echo "Lock Exists: $lockfile owned by $(cat $lockfile)"
fi
YMMV con bloqueo en NFS (ya sabe, cuando no se puede acceder a los servidores NFS), pero en general es mucho más robusto de lo que solía ser. (hace 10 años)
Si tiene trabajos cron que hacen lo mismo al mismo tiempo, desde varios servidores, pero solo necesita 1 instancia para ejecutarse, algo como esto podría funcionar para usted.
Relacionado:¿comando simple de Shell de alternancia de Bluetooth de MacOS?No tengo experiencia con lockrun, pero tener un entorno de bloqueo preestablecido antes de que el script se ejecute realmente podría ayudar. O puede que no. Simplemente está configurando la prueba para el archivo de bloqueo fuera de su secuencia de comandos en un contenedor y, en teoría, ¿no podría simplemente alcanzar la misma condición de carrera si dos trabajos fueron llamados por lockrun exactamente al mismo tiempo, al igual que con el 'interior- ¿la solución del guión?
El bloqueo de archivos respeta el comportamiento del sistema de todos modos, y cualquier secuencia de comandos que no verifique la existencia del archivo de bloqueo antes de ejecutarse hará lo que sea que vaya a hacer. Con solo realizar la prueba del archivo de bloqueo y el comportamiento adecuado, resolverá el 99 % de los problemas potenciales, si no el 100 %.
Si te encuentras mucho con las condiciones de carrera del archivo de bloqueo, puede ser un indicador de un problema mayor, como no tener tus trabajos programados correctamente, o quizás si el intervalo no es tan importante como la finalización del trabajo, tal vez tu trabajo sea más adecuado para ser demonizado. .
EDITAR A CONTINUACIÓN:2016-05-06 (si está utilizando KSH88)
Basado en el comentario de @Clint Pachl a continuación, si usa ksh88, use mkdir
en lugar de noclobber
. Esto mitiga principalmente una posible condición de carrera, pero no la limita por completo (aunque el riesgo es minúsculo). Para obtener más información, lea el enlace que Clint publicó a continuación.
lockdir=/var/tmp/mylock
pidfile=/var/tmp/mylock/pid
if ( mkdir ${lockdir} ) 2> /dev/null; then
echo $$ > $pidfile
trap 'rm -rf "$lockdir"; exit $?' INT TERM EXIT
# do stuff here
# clean up after yourself, and release your trap
rm -rf "$lockdir"
trap - INT TERM EXIT
else
echo "Lock Exists: $lockdir owned by $(cat $pidfile)"
fi
Y, como ventaja adicional, si necesita crear archivos tmp en su secuencia de comandos, puede usar el lockdir
directorio para ellos, sabiendo que se limpiarán cuando finalice el script.
Para fiestas más modernas, el método noclobber en la parte superior debería ser adecuado.