LINUX.ORG.RU

Си, fork() и глобальные переменные

 , ,


1

2

Хочу впилить в свой сишный код уже готовый фрагмент кода и запустить его в другом процессе.
Код определяет и использует свои глобальные переменные, нигде кроме как в этом коде они не используются.
Это безопасно?



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

У процессов разные адресные пространства.

Octagon
()

Это безопасно?

Мусор безопасен?

Запихни их всех в одну глобальную структуру и подмени.

Deleted
()

безопасно
/thread

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

но так не хочется перебирать чужой уже рабочий код.

Так не перебирай, никто не заставляет. Но и вопросы не задавай, всё-равно ничего делать то не собираешься. Верно?

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

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

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

Память процесса копируется (на самом деле cow), те остаётся как на момен до вызова fork().

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

Короче, порядок не ясен

Так. Вызываю телепатов и Кашпировских. Срочно надо наванговать код ТС.

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

Та он тупо не понимает как fork() работает.

anonymous
()

Там есть нюансы. Читай хорошую литературу по применению fork. Имею в виду, что если там в примерах будет вызов printf в дочернем процессе до загрузки другого образа, то это уже неправильные примеры, хотя для иллюстрации годятся, если авторы предупредили об этом. Там все непросто из-за сигналов.

dave ★★★★★
()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int global = 1;

void
update(int i, int n)
{
	int before = global;
	global = n;
	printf("[%d] %d -> %d\n", i, before, global);
}

int
main()
{
	update(1, 10);		// [1] 1 -> 10
	switch (fork()) {
	case -1:
		exit(-1);
	case 0:			// child
		update(2, 20);	// [2] 10 -> 20
		exit(0);
	default:		// parent
		update(3, 30);	// [3] 10 -> 30
		wait(NULL);
	}
	update(4, 40);		// [4] 30 -> 40
	return 0;
}
beastie ★★★★★
()

Иначе говоря, если ты не учтешь важные нюансы, то твой код в дочернем процессе просто ссегфолтится под нагрузкой.

dave ★★★★★
()

Всё ок. Там будет copy-on-write на уровне страниц памяти (обычно 4кб). У нового процесса страницы будут помечены как read-only, во время записи будет page fault. В этот момент ядро за тебя склонирует страницу и продолжит исполнение в новой странице.

snizovtsev ★★★★★
()

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

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

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

а такой общий вариант вариант:


void any_func() {

pid_t pid;

//этот массив создается на стеке
char any_str_param[MAX_LEN_CHEGO_TO_TAM];
strcpy(any_str_param, "param_value");


//тут дитя
if(!pid) {

  ececl(
    BINPATH_STR,
    BINNAME,
    char any_str_param, //массив на стеке от родителя
    NULL
  );

}

return;

}
BINNAME может содержать любой код, даже мьютексы и пайпы. По идее, образ процесса-дитя замещается новым образом BINNAME. Должно быть так что массив созданный на стеке будет иметь копию в порожденном процессе и его адрес передастся в execl. Затем где-то в недрах execl скопируется весь массив как строка и только тогда будет загружен новый образ процесса.

Поправьте меня пожалуйста

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

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

Это вы с кодом внутри обработчиков сигналов попутали. После fork() в дочернем процессе использовать мьютексы можно и безопасно (если вы, конечно, не пытаетесь делать что-то типа разблокировки мьютекса, заблокированного перед форком родительским процессом).

Sorcerer ★★★★★
()

Если ты берешь *.c файл, в котором статиком объявлена переменная, для которой есть функции реиниализации — все ок. Если нет, рано или поздно будет срань.

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

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

посмотрел, нету ничего инициализирующего. решил написать код сам. большое спасибо за советы, особенно за

Всё ок. Там будет copy-on-write на уровне страниц памяти (обычно 4кб). У нового процесса страницы будут помечены как read-only, во время записи будет page fault. В этот момент ядро за тебя склонирует страницу и продолжит исполнение в новой странице.

это показало мне уровень моей некомпитентности

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

После fork() в дочернем процессе использовать мьютексы можно и безопасно (если вы, конечно, не пытаетесь делать что-то типа разблокировки мьютекса, заблокированного перед форком родительским процессом).

Ты точно ничего не напутал сам? Безопасно только после загрузки через execv (и подобные) в новом образе. Пока образ не загружен - опасно.

У меня был наглядный пример, когда серверный софт регулярно падал под нагрузкой из-за этой хрени. Как поправили, то стало работать как часы.

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

После execl может делать, что хочешь. Главное, между fork и execl быть очень осторожным! Только я не понял, как ты собрался передать данные через execl. Читай лучше документацию и меньше советуйся на ЛОРе.

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

Да, точно. Это вообще общепринятая практика в реализации серверов, состоящих из нескольких процессов. Да и вообще иначе и нельзя сделать многопроцессный сервер, кроме как делая fork(), но _не_ делая exec.

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

даннеы в execl передаются как локальные массивы созданные в родителе. Локальные переменные ведь должны иметь копии в процессе-потомке?

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

Сразу после fork - да, но обращение к ним не всегда безопасно, а вот после безопасного exec(*) - уже нет никаких данных. Если бы было все так просто, то fork давно бы перетащили во все языки программирования! Поэтому нужно продумать, как передавать данные. А ты думал заполучить все на халяву?! :)

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

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

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

Ну, тут выбор - либо общие данные (многопоточность), либо изолированность (процессы). А кластерах пока не говорим.

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