LINUX.ORG.RU

fork + popen. Залипание на wait при SIGCHLD

 , ,


0

2

непоняная для меня картина случается когда выполняется код:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

static void sig_chld_handler() {

	int status;
	wait(&status);
	//waitpid(-1, &status, WNOHANG);
}

void signals_init() {

	signal(SIGCHLD, sig_chld_handler);
}

void bash_cmd(char* cmd_str) {

	FILE* fp;
	const int buflen = 1024;
	char buf[buflen];

	if(!(fp = popen(cmd_str, "r"))) {

		fprintf(stderr, "\npopen error\n");
		return;
	}

	while( fgets(buf, buflen - 1, fp)  ) {

		fprintf(stderr, "\n%s", buf);
	}

	pclose(fp);
}

int main(void) {

	signals_init();
	pid_t pid = fork();

	if(!pid) {
		execl("/bin/sleep", "sleep", "1000", (char*)NULL);
		exit(EXIT_SUCCESS);
	}

	while(1) {

		bash_cmd("pwd");
		sleep(1);
	}

	return EXIT_SUCCESS;
}

Примерно на 3-10-й итерации цикла popen прилетает SICHLD и wait входит в ожидание. Если я не делаю execl(...) в форке, то wait ведет себя нормально.
Вместо wait стал использовать

waitpid(-1, &status, WNOHANG);
и в случае если прилетит sigchld, а никакой процесс не помер, то waitpid не впадет в ожидание.

Откуда SIGCHLD если никакой процесс не умер и почему это случается когда использую fork() до popen?


Сходу ответить не смогу, но при работе с сигналами и форками на таком низком уровне есть миллион способов выстрелить себе в ногу.

Напр, ты в форкнутом процессе вызываешь exit, который сбрасывает буферы stdio. Т.е. если ты сделаешь printf, особенно без окончания строки, то он у тебя закешируется, затем ты форкаешься и буферы stdio тоже форкаются. После этого ты в обоих процессах вызываешь exit и буферы флешатся 2 раза. Поэтому нужно либо перед форком аккуратно сбрасывать все буферы (и следить, чтобы их никто не заполнил), либо внутри форка вызывать не exit, а _exit. И таких нюансов сотни.

С сигналами их еще больше — возможно получения сигнала в момент нахождения уже в обработчике сигнала, возможно прерывания системных вызовов сигналами в середине их работы и т.д.

Лучше почитать специализированную литературу. Например, «Линукс API. Исчерпывающее руководство» Керрикса. Там чуть ли не треть книги посвещена форкам и обработки сигналов. Недавно переведена на русский. На рутреке валяется пиратская копия, но в ней нехватает где-то трети страниц ближе к концу книги. Можно для ознакомления скачать (про сигалы рассказывается ближе к началу) и потом купить полноценную, благо электронная версия стоит на сайте издательства копейки.

anonymous ()
Ответ на: комментарий от anonymous

прямо сейчас и читаю Керриска.

strace -f

Ох как много вопросов появилось.

Напр, ты в форкнутом процессе вызываешь exit, который сбрасывает буферы stdio. Т.е. если ты сделаешь printf, особенно без окончания строки, то он у тебя закешируется, затем ты форкаешься и буферы stdio тоже форкаются.

Специально максимально упростил пример чтобы было минимум побочных эффектов

ocr ()
Ответ на: комментарий от anonymous

чуть ли не треть книги посвещена форкам и обработки сигналов

Кто-нибудь, сообщите автору что лихие 90-е прошли. Можно вылезать из бункера.

anonymous ()
Ответ на: комментарий от anonymous

чуть ли не треть книги посвещена форкам и обработки сигналов

Кто-нибудь, сообщите автору что лихие 90-е прошли. Можно вылезать из бункера.

Форки - абсолютное зло, и не нужны, потому что неприменимы к многопотокам, соглашусь.

По поводу проблемы ОП-а - я вообще не понимаю, что ему не понравилось. Он хочет зависнуть до события? Ну он завис, поздравляю. Что не так? Что гонки плохо предсказуемы? Ну сделай синхронизацию какую-то.

Откуда SIGCHLD если никакой процесс не умер

Как это не умер? Ты в цикле каждую секунду создаешь никакой pwd.

byko3y ()