как дочерние процессы завершаются при использовании execve loader в C - PullRequest
0 голосов
/ 15 ноября 2018

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

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

Вот мой вопрос, если мы разветвляем дочерний процесс для вызова execve но так как execve никогда не вернется, он всегда будет выполнять что-то, если все в порядке, что означает, что дочерний процесс никогда не завершится, так как родительский процесс может пожинать этот дочерний процесс?ниже приведен пример кода

if ((pid = Fork()) == 0) { /* Child runs user job */
   if (execve(argv[0], argv, environ) < 0) {      -------->line 2
      printf("%s: Command not found.\n", argv[0]);
      exit(0);
   }
}

/* Parent waits for foreground job to terminate */
if (waitpid(pid, &status, 0) < 0) {  ------------> but the child process never terminated
   printf("waitpid error");
}

, поэтому в строке 2 execve(argv[0], argv, environ) никогда не возвращается, поэтому дочерний процесс никогда не завершается?

Ответы [ 3 ]

0 голосов
/ 15 ноября 2018

В простом случае дочерний процесс в конечном итоге завершится независимо от того, успешно ли вы вызвали exec. Так что это легко ... просто укажите родителя wait для каждого потомка, которого он создает с помощью fork.

Тем не менее, существует методика передачи сбоев от exec к родителю. Техника работает так:

  1. Создать трубу.
  2. Вилка.
  3. В дочернем процессе закройте сторону чтения канала. Пометить сторону записи как близкую к exec.
  4. Звоните exec у ребенка. Если это не удалось, напишите сообщение о сбое в канал и выйдите.
  5. Из родительского процесса закройте конец записи канала и прочитайте с конца чтения.

Если exec завершится успешно, родительский процесс не будет читать данные и просто получит EOF. Если это не удается, родитель прочитает сообщение об ошибке.

0 голосов
/ 15 ноября 2018

Ваша программа foo, запустит какой-нибудь дочерний процесс для запуска другой программы, bar, и вы хотите сделать это с помощью основных системных вызовов fork и execve

Давайте назовем ваш начальный foo процесс p1 . (Это означает: pid .)

Сначала вы позвоните fork . Это создает дочерний процесс p1 , который выполняет другой экземпляр из foo Вызовите этот дочерний процесс p1.1 .

p1.1 работает foo. Но вы хотите запустить bar. Так что сразу в p1.1 , foo звонит execve(path/to/bar ...). Это заменяет экземпляр foo, который p1.1 работает, с экземпляром bar. Тогда ваш дочерний процесс p1.1 выполняется bar, как вы хотите.

Будьте ясны об этом : -

execve(path/to/bar ...) делает не началом bar в новом подпроцессе p1.1 и оставьте p1.1 все еще работающим после пост-форка экземпляра foo. Вместо этого execve(path/to/bar ...) заменяет тот экземпляр foo с экземпляром bar в процессе p1.1 . После fork, но до execve, у нас есть:

p1[foo] -> p1.1[foo]

А после execve имеем:

p1[foo] -> p1.1[bar]

не

p1[foo] -> p1.1[foo] -> p1.1.1[bar]

Вы можете видеть, что execve не может вернуть success вызывающему, p1.1 [foo], потому что если execve успешно, то p1.1 [foo] больше не существует . И, конечно же, execve не может вернуть успех p1 [foo], потому что p1 [foo] не вызывал его .

поскольку execve никогда не возвращается, он всегда будет выполнять что-то, если все в порядке

Нет. execve заменяет p1.1 [foo] на p1.1 [bar] и не возвращает, потому что вызывающий абонент больше не существует. Затем p1.1 [bar] работает, пока не завершится.

p1.1 [bar] рано или поздно завершится одним из способов что любая программа завершает свою работу: она будет работать до нормального exit или быть убитым сигналом, или это может вызвать abort по собственному желанию.

Как родительский процесс ( p1 ) может пожинать этот дочерний процесс ( p1.1 )?

Во-первых, это не обязательно . Как только p1 [foo] запустится p1.1 , он может если ты этого хочешь, просто забудь о p1.1 , займись другими делами если есть, и в конечном итоге exit. Если p1 завершается раньше p1.1 , тогда p1.1 становится бесхозным процессом . Процесс-сирота сразу же принимается в качестве ребенка процессом init . Так если ничего не прервется за это время, p1.1 будет получено после завершения init при выключении системы.

Но, скорее всего, вы не хотите оставлять сирот, и вы делаете хотите foo узнать статус выхода ребенка bar. В таком случае, p1 [foo] рано или поздно должен позвонить wait / waitpid чтобы узнать сейчас p1.1 закончили, а затем действуйте соответственно.

Между тем, p1 [foo] вполне может связываться с p1.1 [bar], используя некоторые форма межпроцессное взаимодействие . И / или p1 [foo] может отметить прошедшее время, пока p1.1 [bar] еще не закончилось. Одним из этих или других способов p1 [foo] может определить что p1.1 [bar] попал в беду, слишком долго и решил убить p1.1 . Когда p1.1 будет убит - кто бы это ни сделал - или закончит по собственному желанию, wait/waitpid вернет эту информацию p1 [foo], а затем может покинуть себя или продолжать заниматься чем-то другим.

В комментарии вы задали вопрос:

не можем ли мы спроектировать [execve] как: вернуть 1, если дочерний процесс завершен?

Такой системный вызов, безусловно, может быть спроектирован и уже существует, но он не может быть неблокирующим системным вызовом, который заменяет вызывающий процесс , вот что execve является. Это был бы системный вызов , который запускает подпроцесс вызова process и возвращает статус завершения подпроцесса родительскому элементу . Тот, который делает это system

0 голосов
/ 15 ноября 2018

со справочной страницы execve :

В случае успеха execve () не возвращается, при ошибке -1 возвращается и errno установлен соответствующим образом.

Чтобы получить возвращаемое значение от дочернего процесса, необходимо использовать wait

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...