Фоновые и приостановленные процессы. Реализация оболочки управления заданиями в C
Я внедряю оболочку Job Control в C в Linux как проект по теме, связанной с операционными системами. У меня есть функция main(), которая выполняет управление дочерними процессами, помогая с помощью связанного списка, как показано здесь, в котором хранится информация фоновых и приостановленных заданий:
typedef struct job_
{
pid_t pgid; /* group id = process lider id */
char * command; /* program name */
enum job_state state;
struct job_ *next; /* next job in the list */
} job;
Каждый раз, когда дочерний процесс завершается или останавливается, SIGCHLD отправляется родительскому процессу, чтобы получить информацию об этом. Затем, как показано здесь, у меня есть обработчик сигнала, который для каждого узла этого связанного списка состояний заданий проверяет, завершился ли процесс, представленный в этом узле, и если он это сделал, этот узел был удален из связанного списка. Вот код для обработчика SIGCHLD, где "job_list" - это связанный список, в котором хранится информация:
void mySIGCHLD_Handler(int signum) {
block_SIGCHLD();
if (signum == 17) {
job *current_node = job_list->next, *node_to_delete = NULL;
int process_status, process_id_deleted;
while (current_node) {
/* Wait for a child process to finish.
* - WNOHANG: return immediately if the process has not exited
*/
waitpid(current_node->pgid, &process_status, WNOHANG);
if (WIFEXITED(process_status) != 0) {
node_to_delete = current_node;
current_node = current_node->next;
process_id_deleted = node_to_delete->pgid;
if (delete_job(job_list, node_to_delete)) {
printf("Process #%d deleted from job list\n", process_id_deleted);
} else {
printf("Process #%d could not be deleted from job list\n", process_id_deleted);
}
} else {
current_node = current_node->next;
}
}
}
unblock_SIGCHLD();
}
Дело в том, что когда вызывается обработчик, некоторые записи, которые не должны быть удалены, потому что процесс, который они представляют, не завершены, удаляются, когда они не должны. Кто-нибудь знает, почему это происходит?
Спасибо и извините за потерянное время:(
1 ответ
Я вижу много проблем в этом коде, но ближайшая проблема, вероятно, здесь:
waitpid(current_node->pgid, &process_status, WNOHANG);
if (WIFEXITED(process_status) != 0) {
когда waitpid(pid, &status, WNOHANG)
возвращается, потому что процесс не завершился, он ничего не записывает в status
так что последующее if
разветвляется на мусор. Вам необходимо проверить фактическое возвращаемое значение waitpid
прежде чем предположить status
имеет смысл.
Наиболее важные другие проблемы:
Ядру разрешено отправлять только одно
SIGCHLD
сказать вам, что несколько процессов вышли. Когда вы получаетеSIGCHLD
нужно позвонитьwaitpid(0, &status, WNOHANG)
в цикле, пока он не скажет вам, что больше нет процессов, ожидающих, и вам нужно обработать (без каламбура) все идентификаторы вышедших процессов, о которых он говорит вам.Не безопасно звонить
printf
или жеfree
из асинхронного обработчика сигнала. Вместо этого добавьте завершенные процессы в список отложенных задач. Обязательно заблокируйте SIGCHLD в коде основного цикла, который использует этот список.Не блокируйте и не разблокируйте
SIGCHLD
себя в обработчике; это неизбежное состояние гонки. Вместо этого, пусть ядро сделает это за вас, атомарно, правильно настроив ваш обработчик сигнала: usesigaction
и не кладитеSA_NODEFER
вsa_flags
, (Сделать ставитьSA_RESTART
вsa_flags
, если у вас нет очень веских причин не делать этого.)Буквенное число 17 должно быть постоянной сигнала
SIGCHLD
вместо. Некоторые номера сигналов были стабильными во всех Unix-системах на протяжении истории, ноSIGCHLD
не один из них.