Недавно пришлось активно заниматься отладкой одного самописного сервера на одном скриптовом языке (то есть без явной компиляции). Процесс происходил в два окна. В одном открыт исходник, в другом запускается сервер и наблюдается результат. Перезапускать вручную сервер после каждой правки быстро надоело, захотелось, чтобы всё происходило само, по нажатию волшебной кнопки save. Как бы это сделать? Решение посетило следующее: В качестве "штуки", перезапускающей сервер был написан monitor.c (возможно, не самое подходящее название):
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
struct sigaction act;
pid_t pid;
void handle_usr1(int s) {
if (kill(pid, SIGINT) == -1 && errno == ESRCH) {
perror("wrong pid of killed process");
exit(1);
}
wait(NULL);
}
int main(int argc, char * argv[]) {
act.sa_handler = handle_usr1;
sigaction(SIGUSR1, &act, NULL);
printf("MONITOR PID %d\n", (int) getpid());
loop:
pid = fork();
if (pid == 0) { /* is a child */
execvp(argv[1], &argv[1]);
perror("exec returned control");
exit(1);
} else if (pid > 0) { /* is a parent */
if (wait(NULL) != -1) {
exit(2);
}
goto loop;
} else { /* fork failed, no child is created */
perror("fork failed");
exit(3);
}
}
Запускать его следует так:
monitor cmd [arg] ...
Monitor выводит в терминал свой PID и запускает cmd с аргументами arg ... По сигналу SIGUSR1, monitor прибивает запущенную программу и перезапускает её с теми же аргументами.
Monitor ожидает, что сервер не завершится сам по себе, а только по сигналу, в противном случае, monitor также завершается с кодом 2.
Итак, сервер перезапускается по сигналу SIGUSR1, что дальше? Дальше следует как-то посылать сигнал. Для этого я использовал автокоманды редактора vim:
:au BufWritePost $PATTERN !kill -s USR1 $PID
При записи буфера в файл, имя которого соответствует $PATTERN, автоматически будет выполняться команда:
kill -s USR1 $PID
Итак, всё что нужно:
# иметь скомпилированный монитор:
gcc -o monitor monitor.c
# запустить монитор с сервером, увидеть PID монитора:
./monitor ./server
> MONITOR PID 1234
> ...
# открыть vim и добавить автокоманду с нужным PID
vim server
:au BufWritePost server !kill -s USR1 1234
Теперь, при сохранении исходника (vim команда :w), сервер будет рестартовать автоматически - одним рутинным действием меньше.
Также стоит отметить, если есть уверенность, что на системе запускается только один процесс с именем ./server, вполне можно обойтись простым циклом в sh:
while true; do ./server; done
и следующей автокомандой для vim:
:au BufWritePost server !pkill ./server
Несомненным преимуществом такого подхода является его простота.