LINUX.ORG.RU

Как послать сигнал во время вызова read () или write () ?

 , , ,


0

1
...
char buf[4];
size_t count = sizeof(buf);
while (count != 0 && (ret_val = read (fd, buf, count)) !=0) {
...

Как послать сигнал во время вызова read ()? Необходимо, чтобы сигнал дошел до процесса после того, как он считал первый байт из блока длиной count, но до считывания последнего байта из этого блока.



Последнее исправление: xilun (всего исправлений: 1)

А что это?

read (int fd,
//    ^^^--------- ???

(Всё, вопрос отпал: была опечатка, уже исправлена)

Т.к. fd - может быть дескриптором на открытый TCP-сокет, то можно сделать сервер, который отдаёт данные размером с sizeof(buf). Но отдаёт он их не сразу, а сначала только 1/2 байта, потом определяет pid клиента, посылает ему сигнал и продолжает слать оставшиеся 3/2 байта. Роли клиент/сервер можно поменять местами, тогда стартовать клиента с уже известным pid процесса сервера. А, может, и просто fork().

gag ★★★★★
()
Последнее исправление: gag (всего исправлений: 1)
Ответ на: комментарий от gag

А, может, и просто fork().

И вместо сокета pipe(), а потом fork().

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

сначала только 1/2 байта, потом определяет pid клиента, посылает ему сигнал и продолжает слать оставшиеся 3/2 байта

в цитатник :-)

MKuznetsov ★★★★★
()

Ты хочешь, чтоб процесс читающий fd знал сколько байт там доступно?

Если да, то «you are doing it wrong». Почитай man 2 select и man 2 fcntl (O_NONBLOCK). Посмотри примеры в сети, как люди делают.

KennyMinigun ★★★★★
()

read первого байта, потом sigwait'ом ловить сигнал, потом читать оставшееся. А вообще попахивает бредом.

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

А вообще попахивает бредом.

Я так понял, что просто интересно посмотреть, как ведёт себя read(), словив сигнал.

gag ★★★★★
()

В этом коде процесс ничего не читает, читает ядро и копирует из дискового буфера в память процесса. Если чтение из файла на локальной ФС, то read() не прервать, пока все данные не будут переданны в память процесса. Можно ли превать read() при копировании данных из буфера сокета не знаю, кому интерестно пусть изучает исходники ядра.

mky ★★★★★
()
Ответ на: комментарий от gag

Если мне не изменяет склероз то read относится к системным вызовам которые автоматически перезапускаются.

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

Нагуглил

К системным вызовам, которые перезапускаются автоматически, были отнесены ioctl, read, readv, write, writev, wait и waitpid.

itn ★★★
()
Ответ на: Нагуглил от itn

которые перезапускаются автоматически

Интересно, не знал. Но если гуглить дальше, то оказывается: которые могут быть перезапущены автоматически, если флаг SA_RESTART был использован при регистрации обработчика сигнала. А вот если обработчика не было...

gag ★★★★★
()
Ответ на: комментарий от mky

Если чтение из файла на локальной ФС, то read() не прервать, пока все данные не будут переданны в память процесса.

Ссылкой на стандарт не поделишься?

tailgunner ★★★★★
()
Ответ на: комментарий от gag

Мне уже дальше лень гуглить но вроде флаг SA_RESTART устанавливается по-умолчанию. Хотя...

Расширение XSI стандарта Single UNIX Specification оговаривает, что функция sigaction не должна перезапускать прерванные системные вызовы, если явно не указан флаг SA_RESTART.

Но я что-то сомневаюсь в верности этой информации. Это надо код для проверки писать...

itn ★★★
()
Ответ на: комментарий от gag

Я так понял, что просто интересно посмотреть, как ведёт себя read(), словив сигнал.

Да, просто интересно посмотреть, как ведет себя read () и что будет в buf .

xilun
() автор топика
Ответ на: комментарий от KennyMinigun

Ты хочешь, чтоб процесс читающий fd знал сколько байт там доступно?

Нет. Выше в предыдущем сообщении я указал цель.

xilun
() автор топика
Ответ на: комментарий от tailgunner

В posix'е про это нет, но всегда было, что при read()/write() процесс уходил в uninterruptible sleep.

Ещё NFS не позволяет по умолчанию (без опции intr) прерывать файловые операции.

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

всегда было, что при read()/write() процесс уходил в uninterruptible sleep.

В D-state уходили дисковые операции, а не чтение из файла.

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

Да, чтение закешированного файла не вводит процесс в состояние D. Как именно идёт чтение из исходников ядра я понять не смог, но у меня не получилось прервать сигналом read() при чтении обычного файла на локальной ФС. И это не похоже на SA_RESTART, если читать из обработчика сигнала буфер, указываемый в read(), то там либо пусто, либо все байты из файла.

В ″man 7 signal″ написано:

If  a blocked call to one of the following interfaces is interrupted by
a signal handler, then the call will be automatically  restarted  after
the  signal  handler returns if the SA_RESTART flag was used; otherwise
the call will fail with the error EINTR:

       * read(2), readv(2), write(2), writev(2),  and  ioctl(2)  calls  on
         «slow»  devices.   A  «slow» device is one where the I/O call may
          block for an indefinite time, for example, a terminal,  pipe,  or
          socket.   (A  disk is not a slow device according to this defini-
          tion.)  If an I/O call on a slow device has  already  transferred
          some data by the time it is interrupted by a signal handler, then
          the call will return a success status (normally,  the  number  of
          bytes transferred).

То есть EINTR или число прочитанных байт применимо к read() только при чтении с «медленных» устройств. Не помню где я прочитал, что файлы на локальном диске не относятся к «медленным».

mky ★★★★★
()
Ответ на: комментарий от tailgunner

Я перепутал, согласен что это бред.

itn ★★★
()
Ответ на: комментарий от mky

у меня не получилось прервать сигналом read() при чтении обычного файла на локальной ФС

Я думаю, это особенности (кривой) реализации. Стандарт этого не требует и не обещает.

tailgunner ★★★★★
()
Ответ на: комментарий от slovazap

Не бредом а линуксом, у вас кроме селекта и полл нифига нет, хаха!

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

Т.к. fd - может быть дескриптором на открытый TCP-сокет, то можно сделать сервер, который отдаёт данные размером с sizeof(buf).

И вместо сокета pipe(), а потом fork().

Хотелось бы проверить на обычных файлах из локальной ФС и на файлах блочных устройств.

xilun
() автор топика
Ответ на: комментарий от mky

у меня не получилось прервать сигналом read() при чтении обычного файла на локальной ФС.

Как послать сигнал, чтобы он дошел до процесса в момент чтения ядром файла(1-й вариант) и в момент копирования из дискового буфера в память процесса (2-й вариант) ?

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

Во время дисковой операции послать сигнал просто — делаете размер buf побольше, допустим 500 Мбайт и читаете файл, которого нет в кеше. Если читать не с ssd, то будет вполне ощутимый промежуток времени.

Относительно второго не знаю, я пробовал долбить процесс сигналом когда он в цикле читал один и тот же файл. Если ядро действительно копирует данные из буфера в память процесса, то это занимает ощутимый промежуток времени, в который можно попасть сигналом. Но я не уверен, что используется именно копирование, а не особая страничная магия.

mky ★★★★★
()
Ответ на: комментарий от tailgunner

Она и не должна:

          A  «slow» device is one where the I/O call may
          block for an indefinite time, for example, a terminal,  pipe,  or
          socket.   (A  disk is not a slow device according to this defini-
          tion.)

Реализация тут не кривая, а типовая - проверка на наличие недоставленных сигналов осуществляется только при переключении контекстов пользователь-ядро. Если чтение уже частично произошло (какой-то объём данных считан в буфер), то контекст задачи уже установлен на код, отвечающий за чтение данных, проверка на наличие сигналов при переходе пользователь-ядро уже пройдена, и будет в следующий раз пройдена при обратном переходе ядро-пользователь, то есть по завершении записи доступных данных в буфер.

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

Она и не должна:

Возможно. Я поэтому и сказал «кривая», а не «ошибочная.

Если чтение уже частично произошло (какой-то объём данных считан в буфер), то контекст задачи уже установлен на код, отвечающий за чтение данных, проверка на наличие сигналов при переходе пользователь-ядро уже пройдена, и будет в следующий раз пройдена при обратном переходе ядро-пользователь

Какой „контекст задачи“, какой „код чтения данных“, кем и как „установлен“? Какой-то набор слов.

проверка на наличие сигналов при переходе пользователь-ядро уже пройдена

Если ты хочешь сказать, что сигналы проверяются только при проходе границы между ядром и пользователем, то это 1) не так 2) даже если это когда-то было так, это было деталью реализации.

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

Какой „контекст задачи“,

Единица управления задачами в ядре, struct task_struct, если угодно.

какой „код чтения данных“

Последовательность инструкций, отвечающая за чтение данных и их запись в буфер.

кем и как „установлен“?

Ведром.

Какой-то набор слов.

Вроде все слова простые, русские, импортозамещённые. Не понимаю, что тут не понятного?

LamerOk ★★★★★
()
Ответ на: комментарий от tailgunner

Если ты хочешь сказать, что сигналы проверяются только при проходе границы между ядром и пользователем, то это 1) не так

Хочу сказать, что в рассматриваемом случае именно тогда. Еще проверка на них происходит при получении задачей управления, если задача «спит» на блокрующем вызвое, но это не случай чтения данных с диска.

2) даже если это когда-то было так, это было деталью реализации.

Это самая общая деталь реализации для множества известных систем. Тут нет ничего «нового» или «необычного».

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

если задача «спит» на блокрующем вызвое, но это не случай чтения данных с диска.

Это как раз случай чтения с диска.

Это самая общая деталь реализации для множества известных систем

Вот просто-таки «самая»? Ну окей, это всё меняет.

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

Если ты хочешь сказать, что сигналы проверяются только при проходе границы между ядром и пользователем, то это 1) не так 2) даже если это когда-то было так, это было деталью реализации.

ЕМНИП ты не можешь прервать сискол, когда ядро уже зослало пачку submit_bio() и занимается хитрой магией с DMA.

kirk_johnson ★☆
()
Ответ на: комментарий от kirk_johnson

когда ядро уже зослало пачку submit_bio() и занимается хитрой магией с DMA.

«Хитрая магия» продолжается миллисекунды (плюс тайм-аут, если у железа проблемы); деталей работы bio не знаю, но буду очень удивлен, если у них нет функции cancel или аналогичной. Так что было бы желание...

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

если задача «спит» на блокрующем вызвое

Это как раз случай чтения с диска.

И что конкретно «блокирует» ядро от чтения данных с диска?

Мы как бе о логике доставки сигналов говорим, т.е. о разговор идёт исключительно в контексте ядра. Это пользовательский код «приостановлен» в рамках этой задачи, ядрёный-то пашет как папа карло, пока не закончится одно из двух - буфер или данные. Управление перешло из пользовательского кода в код ядра, но никакой «приостановки» выполнения кода нет.

LamerOk ★★★★★
()
Ответ на: комментарий от tailgunner

«Хитрая магия» продолжается миллисекунды

Ну и зачем прерывать эти миллисекунды? Доставка обрабатываемого сигнала ничем не отличается от любой другой функции ядра в отношении процесса (типа чтения данных с диска), и никакого супер-пупер приоритета у неё нет.

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

Это как раз случай чтения с диска.

И что конкретно «блокирует» ядро от чтения данных с диска?

Внезапно, операция обмена с диском.

«Хитрая магия» продолжается миллисекунды

Ну и зачем прерывать эти миллисекунды?

Вот именно эти миллисекунды можно и не прерывать (хотя можно и прерывать), за одним важным исключением - за счет сигналов и/или тайм-аутов должно быть обеспечено разумное функционирование на сбойных устройствах (привет ушлепку Хартману и USB-подсистеме). Но большая операция read/write включает в себя десятки операций В/В на устройствах - после завершения каждой из них следует проверять флаг получения сигнала и отваливаться.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Внезапно, операция обмена с диском.

после завершения каждой из них следует проверять флаг получения сигнала и отваливаться.

И как часто прилетают сигналы к процессу в сравнении с операциями В/В? Сколько нулей после запятой?

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

И как часто прилетают сигналы к процессу в сравнении с операциями В/В? Сколько нулей после запятой?

Ты еще спроси, как часто нужны подушки безопасности или аварийные выходы.

tailgunner ★★★★★
()
Ответ на: комментарий от LamerOk

И что общего у какого-нибудь SIGUSR2 с авариями, катастрофами и концом света?

Надеюсь, насчет SIGKILL и SIGTERM такого вопроса не возникает? А обрабатываются они все одинаково.

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

А обрабатываются они все одинаково.

ШТА?!?!

static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
			int group, int from_ancestor_ns)
{
...
	/*
	 * fast-pathed signals for kernel-internal things like SIGSTOP
	 * or SIGKILL.
	 */
	if (info == SEND_SIG_FORCED)
		goto out_set;
...
out_set:
	signalfd_notify(t, sig);
	sigaddset(&pending->signal, sig);
	complete_signal(sig, t, group);
ret:
	trace_signal_generate(sig, info, t, group, result);
	return ret;
}


static void complete_signal(int sig, struct task_struct *p, int group)
...
	/*
	 * Found a killable thread.  If the signal will be fatal,
	 * then start taking the whole group down immediately.
	 */
	if (sig_fatal(p, sig) &&
	    !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) &&
	    !sigismember(&t->real_blocked, sig) &&
	    (sig == SIGKILL || !t->ptrace)) {
		/*
		 * This signal will be fatal to the whole group.
		 */
		if (!sig_kernel_coredump(sig)) {
			/*
			 * Start a group exit and wake everybody up.
			 * This way we don't have other threads
			 * running and doing things after a slower
			 * thread has the fatal signal pending.
			 */
			signal->flags = SIGNAL_GROUP_EXIT;
			signal->group_exit_code = sig;
			signal->group_stop_count = 0;
			t = p;
			do {
				task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK);
				sigaddset(&t->pending.signal, SIGKILL);
				signal_wake_up(t, 1);
			} while_each_thread(p, t);
			return;
		}
	}

Как вообще могла придти в голову такая мысль?

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

А обрабатываются они все одинаково.

ШТА?!?!

Та. Посмотри, что такое signal_wake_up.

Как вообще могла придти в голову такая мысль?

Опыт. Я знаю, как оно делается, а ты грепаешь сырцы по слову signal.

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

Посмотри, что такое signal_wake_up.

Посмотрел, в целом то, что я ожидал увидеть:

static inline void signal_wake_up(struct task_struct *t, bool resume)
{
	signal_wake_up_state(t, resume ? TASK_WAKEKILL : 0);
}

void signal_wake_up_state(struct task_struct *t, unsigned int state)
{
	set_tsk_thread_flag(t, TIF_SIGPENDING);
	/*
	 * TASK_WAKEKILL also means wake it up in the stopped/traced/killable
	 * case. We don't check t->state here because there is a race with it
	 * executing another processor and just now entering stopped state.
	 * By using wake_up_state, we ensure the process will wake up and
	 * handle its death signal.
	 */
	if (!wake_up_state(t, state | TASK_INTERRUPTIBLE))
		kick_process(t);
}

А вот ты посмотри на первое троеточие в __send_signal, чтобы понять, о чём речь.

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

Посмотри, что такое signal_wake_up.

Посмотрел, в целом то, что я ожидал увидеть:

А, ну тогда окей. Правда, непонятно, о чем ты спорил или в чем пытался меня уличить.

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

Если тебе сишный код непонятен, давай я переведу на русский:

По SIGKILL задаче будет выставлен флаг «сдохни», и задаче будет незамедлительно передано управление, т.е. она тут же будет завершена ядром.

Обычный сигнал будет просто повешен в очередь на обработку, и обработан в общем порядке, т.е. когда шедулер ядра передаст управление этому процессу и код ядра будет проходить проверки на наличие сигналов, только тогда возврат из ядра произойдёт на обработчик сигнала.

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

Если тебе сишный код непонятен, давай я переведу на русский:

Благодарю.

По SIGKILL задаче будет выставлен флаг «сдохни»

Верно.

и задаче будет незамедлительно передано управление

Не совсем верно - задача будет незамедлительно переведена в режим ядра.

т.е. она тут же будет завершена ядром.

Неверно. Задача, выполняющаяся в режиме ядра (как раз наш случай) завершится тогда, когда сочтет нужным обратить внимание на signal_pending, прервать то, чем занимается, и выйти на тот уровень ядра, который собственно и обработает сигнал.

tailgunner ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.