Re[9]: Запустить параллельный процесс
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 13.01.10 08:48
Оценка:
Здравствуйте, igna, Вы писали:

Pzz>>Ну, например.


I>А еще? Альтернатива у тебя есть?


Мнэээ... а что не устраивает?
The God is real, unless declared integer.
Re[10]: Запустить параллельный процесс
От: igna Россия  
Дата: 13.01.10 11:50
Оценка:
Здравствуйте, netch80, Вы писали:

N>Мнэээ... а что не устраивает?


Все устраивает, просто возможно у Pzz есть что-то еще, не просто же так он продолжил тему после твоего исчерпывающего ответа.
Re[9]: Запустить параллельный процесс
От: Pzz Россия https://github.com/alexpevzner
Дата: 13.01.10 12:52
Оценка:
Здравствуйте, igna, Вы писали:

Pzz>>Ну, например.


I>А еще? Альтернатива у тебя есть?


Ну я, например, использую для этих целей самодельный протокол, работающий через pipe, через который процесс-потомок может сказать родителю, чем там все завершилось. Цель, на самом деле, состояла в том, чтобы при запуске демона родительский процесс завершался не после первого fork'а, а после окончания инициализации потомка — чтобы следующий инициализационный скрипт запускался не раньше, чем мой демон готов к работе
Re[10]: Запустить параллельный процесс
От: Pzz Россия https://github.com/alexpevzner
Дата: 13.01.10 12:53
Оценка:
Здравствуйте, netch80, Вы писали:

I>>А еще? Альтернатива у тебя есть?


N>Мнэээ... а что не устраивает?


Если запускаешь не свою программу, а какую-то произвольную, нет гарантии, что она будет следовать соглашениям о exit codes
Re[11]: Запустить параллельный процесс
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 13.01.10 13:54
Оценка: +1
Здравствуйте, Pzz, Вы писали:

I>>>А еще? Альтернатива у тебя есть?


N>>Мнэээ... а что не устраивает?


Pzz>Если запускаешь не свою программу, а какую-то произвольную, нет гарантии, что она будет следовать соглашениям о exit codes


Именно поэтому я привёл второй метод с пайпом — который таких проблем не имеет.
The God is real, unless declared integer.
Re[10]: Запустить параллельный процесс
От: igna Россия  
Дата: 13.01.10 13:58
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Ну я, например, использую для этих целей самодельный протокол, работающий через pipe, через который процесс-потомок может сказать родителю, чем там все завершилось.


А в чем отличие твоего протокола от того, что предложил netch80?
Re[11]: Запустить параллельный процесс
От: Pzz Россия https://github.com/alexpevzner
Дата: 13.01.10 20:18
Оценка: :)
Здравствуйте, igna, Вы писали:

Pzz>>Ну я, например, использую для этих целей самодельный протокол, работающий через pipe, через который процесс-потомок может сказать родителю, чем там все завершилось.


I>А в чем отличие твоего протокола от того, что предложил netch80?


Понятия не имею. Может и не в чем
Re[7]: Запустить параллельный процесс
От: akaky_kalistrat  
Дата: 18.01.10 16:24
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, alexsy, Вы писали:


A>>Указатель на int видно?? Вот через этот указатель и вернётся результат твоего дочернено процесса.


I>
I>for (;;)
I>    Нужно не дождаться пока процесс-потомок закончится, а убедиться в том, что вызов execlp в нем завершился успешно.
I>


посмори в сторону vfork, кажется он не отпускает родителя до тех пор пока потомок не вызовет exec.
Re[7]: Запустить параллельный процесс
От: akaky_kalistrat  
Дата: 18.01.10 16:36
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, alexsy, Вы писали:


A>>Указатель на int видно?? Вот через этот указатель и вернётся результат твоего дочернено процесса.


I>
I>for (;;)
I>    Нужно не дождаться пока процесс-потомок закончится, а убедиться в том, что вызов execlp в нем завершился успешно.
I>


man vfork:

vfork() differs from fork() in that the parent is suspended until the child makes a call to execve(2) or _exit(2).

то есть, как мне кажется, это то, что Вам нужно...

— вызвать в родителе vfork
— родитель засыпает (не возвращается из vfork пока потомок не позвал exec)
— потомок получив управление вызывает exec
— родитель побежал дальше...

как-то так...

спасибо.
Re[8]: Запустить параллельный процесс
От: ДимДимыч Украина http://klug.org.ua
Дата: 18.01.10 16:38
Оценка:
Здравствуйте, akaky_kalistrat, Вы писали:

_>посмори в сторону vfork, кажется он не отпускает родителя до тех пор пока потомок не вызовет exec.


Он может не отпускать родителя — это один из способов, которым оптимизируется запуск процессов из других образов. Но он не обязан этого делать. В некоторых реализациях vfrok() эквивалентен fork().
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[9]: Запустить параллельный процесс
От: akaky_kalistrat  
Дата: 18.01.10 17:14
Оценка:
Здравствуйте, ДимДимыч, Вы писали:

ДД>Здравствуйте, akaky_kalistrat, Вы писали:


_>>посмори в сторону vfork, кажется он не отпускает родителя до тех пор пока потомок не вызовет exec.


ДД>Он может не отпускать родителя — это один из способов, которым оптимизируется запуск процессов из других образов. Но он не обязан этого делать. В некоторых реализациях vfrok() эквивалентен fork().


Да, я в курсе этой оптимизации... Действительно, не имеет смысл копировать каталоги и таблицы страниц если дитятко сразу же собирается вызвать exec. Однако из мана мне не очевидно, что поведение vfork в части ожидания вызова потомка является опциональным...

Для Линукса по меньшей мере...

[root@akaky2 ~]# cat /etc/redhat-release
CentOS release 5.2 (Final)
Re[10]: Запустить параллельный процесс
От: ДимДимыч Украина http://klug.org.ua
Дата: 18.01.10 17:52
Оценка:
Здравствуйте, akaky_kalistrat, Вы писали:

_>Однако из мана мне не очевидно, что поведение vfork в части ожидания вызова потомка является опциональным...


А мне из мана не очевидно, что vfork обязан не отпускать родителя.




VFORK(3P)                  POSIX Programmer's Manual                 VFORK(3P)



PROLOG
       This  manual  page is part of the POSIX Programmer's Manual.  The Linux
       implementation of this interface may differ (consult the  corresponding
       Linux  manual page for details of Linux behavior), or the interface may
       not be implemented on Linux.

NAME
       vfork - create a new process; share virtual memory

SYNOPSIS
       #include <unistd.h>

       pid_t vfork(void);


DESCRIPTION
       The vfork() function shall be equivalent to  fork(),  except  that  the
       behavior is undefined if the process created by vfork() either modifies
       any data other than a variable of type pid_t used to store  the  return
       value  from  vfork(), or returns from the function in which vfork() was
       called, or calls any other function before successfully calling _exit()
       or one of the exec family of functions.

RETURN VALUE
       Upon successful completion, vfork() shall return 0 to the child process
       and return the process ID of the child process to the  parent  process.
       Otherwise,  -1  shall be returned to the parent, no child process shall
       be created, and errno shall be set to indicate the error.

ERRORS
       The vfork() function shall fail if:

       EAGAIN The system-wide limit on the total  number  of  processes  under
              execution  would be exceeded, or the system-imposed limit on the
              total number of processes under execution by a single user would
              be exceeded.

       ENOMEM There is insufficient swap space for the new process.


       The following sections are informative.

EXAMPLES
       None.

APPLICATION USAGE
       Conforming  applications  are recommended not to depend on vfork(), but
       to use fork() instead. The vfork()  function  may  be  withdrawn  in  a
       future version.

       On some implementations, vfork() is equivalent to fork().

       The vfork() function differs from fork() only in that the child process
       can share code and data with the calling process (parent process). This
       speeds cloning activity significantly at a risk to the integrity of the
       parent process if vfork() is misused.

       The use of vfork() for any purpose except as a prelude to an  immediate
       call to a function from the exec family, or to _exit(), is not advised.

       The  vfork() function can be used to create new processes without fully
       copying the address space of the old process. If a  forked  process  is
       simply going to call exec, the data space copied from the parent to the
       child by fork() is not used. This  is  particularly  inefficient  in  a
       paged  environment,  making vfork() particularly useful. Depending upon
       the size of the parent's data space, vfork()  can  give  a  significant
       performance improvement over fork().

       The  vfork()  function  can normally be used just like fork().  It does
       not work, however, to return while running in the child's context  from
       the caller of vfork() since the eventual return from vfork() would then
       return to a no longer existent stack  frame.   Care  should  be  taken,
       also,  to call _exit() rather than exit() if exec cannot be used, since
       exit() flushes and closes standard I/O channels, thereby  damaging  the
       parent  process' standard I/O data structures. (Even with fork(), it is
       wrong to call exit(), since buffered data would then be flushed twice.)

       If signal handlers are invoked in the child process after vfork(), they
       must follow the same rules as other code in the child process.

RATIONALE
       None.

FUTURE DIRECTIONS
       This function may be withdrawn in a future version.

SEE ALSO
       exec(),   exit(),  fork(),  wait(),  the  Base  Definitions  volume  of
       IEEE Std 1003.1-2001, <unistd.h>

COPYRIGHT
       Portions of this text are reprinted and reproduced in  electronic  form
       from IEEE Std 1003.1, 2003 Edition, Standard for Information Technology
       -- Portable Operating System Interface (POSIX),  The  Open  Group  Base
       Specifications  Issue  6,  Copyright  (C) 2001-2003 by the Institute of
       Electrical and Electronics Engineers, Inc and The Open  Group.  In  the
       event of any discrepancy between this version and the original IEEE and
       The Open Group Standard, the original IEEE and The Open Group  Standard
       is  the  referee document. The original Standard can be obtained online
       at http://www.opengroup.org/unix/online.html .



IEEE/The Open Group                  2003                            VFORK(3P)


_>[root@akaky2 ~]# cat /etc/redhat-release

_>CentOS release 5.2 (Final)

$ echo /etc/*-release
/etc/arch-release
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[11]: Запустить параллельный процесс
От: akaky_kalistrat  
Дата: 18.01.10 20:09
Оценка:
Здравствуйте, ДимДимыч, Вы писали:

ДД>Здравствуйте, akaky_kalistrat, Вы писали:


_>>Однако из мана мне не очевидно, что поведение vfork в части ожидания вызова потомка является опциональным...


ДД>А мне из мана не очевидно, что vfork обязан не отпускать родителя.



ДД>[code]

ДД>VFORK(3P) POSIX Programmer's Manual VFORK(3P)
...

Да, согласен, мне то же не очевидно. Однако на Линуксе это так! Собсна, если топикстартер не пишет систему, которая должна работать, по возможности, на любой POSIX compliant платформе, а, скажем, ограничевается Линуксом, то решение вполне подходящие. Более того запуск процесса будет эффективней в силу упомянутой оптимизации.

Спасибо.
Re[12]: Запустить параллельный процесс
От: ДимДимыч Украина http://klug.org.ua
Дата: 18.01.10 21:28
Оценка:
Здравствуйте, akaky_kalistrat, Вы писали:

_>Собсна, если топикстартер не пишет систему, которая должна работать, по возможности, на любой POSIX compliant платформе, а, скажем, ограничевается Линуксом, то решение вполне подходящие.


Использование vfork() ничем не поможет топикстартеру. Нет никакой гарантии, что родительский процесс разморозится до того, как дочерний успешно отработает и завершится. С точки зрения родительского процесса оба варианта: "неудачное выполнение exec и выполнение _exit" и "удачное выполнение exec и выполнение _exit" могут выглядеть абсолютно одинаково.
Мало того, использование vfork() еще и навредит: вызов любых функций, отличных от _exit() и семейства exec(), равно как и модификация памяти, ведет к неопределенному поведению, так что гарантированно сообщить родительскому процессу о результате вызова exec() в дочернем никак нельзя.
Если хочется спецэффектов и платформа ограничена Linux'ом, то лучше использовать clone(2), в частности, с флагом CLONE_VFORK. И то я бы не стал полагаться на все сто, что заявленное поведение будет всегда и везде.
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[13]: Запустить параллельный процесс
От: akaky_kalistrat  
Дата: 19.01.10 11:21
Оценка:
Здравствуйте, ДимДимыч, Вы писали:

ДД>Здравствуйте, akaky_kalistrat, Вы писали:


_>>Собсна, если топикстартер не пишет систему, которая должна работать, по возможности, на любой POSIX compliant платформе, а, скажем, ограничевается Линуксом, то решение вполне подходящие.


ДД>Использование vfork() ничем не поможет топикстартеру. Нет никакой гарантии, что родительский процесс разморозится до того, как дочерний успешно отработает и завершится.


Насколько я понял из вопроса, нет необходимости ждать завершения дочернего процеса, иначе waitpid решал бы все проблемы.

ДД>С точки зрения родительского процесса оба варианта: "неудачное выполнение exec и выполнение _exit" и "удачное выполнение exec и выполнение _exit" могут выглядеть абсолютно одинаково.

Для этого есть возвращаемые из main коды ошибок или параметр функции _exit.

ДД>Мало того, использование vfork() еще и навредит: вызов любых функций, отличных от _exit() и семейства exec(), равно как и модификация памяти, ведет к неопределенному поведению, так что гарантированно сообщить родительскому процессу о результате вызова exec() в дочернем никак нельзя.

ДД>Если хочется спецэффектов и платформа ограничена Linux'ом, то лучше использовать clone(2), в частности, с флагом CLONE_VFORK. И то я бы не стал полагаться на все сто, что заявленное поведение будет всегда и везде.

согласно исходникам ядра (2.6.18) vfork именно это и делает.
i386\kernel\process.c:
asmlinkage int sys_vfork(struct pt_regs regs)
{
return do_fork(!!!! CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
}

и вот еще
kernel\fork.c

if (clone_flags & CLONE_VFORK) {
wait_for_completion(&vfork);!!!!!
if (likely(is_user))
tracehook_report_vfork_done(p, nr);
}


...

/* notify parent sleeping on vfork() */
if (vfork_done) {
task_aux(tsk)->vfork_done = NULL;
complete(vfork_done);
}

И, как я понимаю, такое поведение vfork-а на Линуксе сделано намеренно, ибо, в противном случае, как Вы справедливо заметили, им пользоваться просто нельзя в силу того, что очень велики риски внести несанкционированные изменения в адресное пространство как родителя, так и порождаемого процесса. Таким образом, после того как exec все подготовила для старта нового процесса(виртуальное адресное пространство и прочие нужности), она вызывает соответсвующую нотификацию(см. mm_release) и vfork отпускает управление. Таким образом, на момент вызова mm_release новый процесс с _новым_ адресным пространством готов к запуску и это с допущениями можно считать тем самым событием, которое и хочет получить автор топика. С допущениями, так как ряд вещей для успешной работы процесса(загрузка необходимых SO, статическая инициализация и проч...) на этот момент не выполненны.

А в родителе vfork не отпускает управление до этого события, дабы намеренно не позволить ему поменять состояние своего адресного пространства. Да, я согласен, что это Linux specified, но, надо признать, что специфика эта полезная, так как исключает неопределенности описанные Вами выше.

Вот как-то так...

Спасибо.
Re[14]: Запустить параллельный процесс
От: ДимДимыч Украина http://klug.org.ua
Дата: 19.01.10 13:01
Оценка:
Здравствуйте, akaky_kalistrat, Вы писали:

ДД>>Использование vfork() ничем не поможет топикстартеру. Нет никакой гарантии, что родительский процесс разморозится до того, как дочерний успешно отработает и завершится.

_>Насколько я понял из вопроса, нет необходимости ждать завершения дочернего процеса, иначе waitpid решал бы все проблемы.

Но мы не можем заставить родительский процесс проснуться сразу же после того, как отработает exec() в дочернем. Может я не понимаю алгоритм, в котором Вы предлагаете использовать vfork() — так опишите его.

ДД>>С точки зрения родительского процесса оба варианта: "неудачное выполнение exec и выполнение _exit" и "удачное выполнение exec и выполнение _exit" могут выглядеть абсолютно одинаково.

_>Для этого есть возвращаемые из main коды ошибок или параметр функции _exit.

И как мы отличим, этот код ошибки возвратился по _exit() из нашего образа, или по _exit() другого образа, загруженного с помощью exec()? Если бы мы точно знали, что запускаемый образ никогда не возвращает какой-то определенный код, то могли бы его использовать в качестве индикатора неуспешности exec(). Но мы ведь этого не знаем. В половине треда это обсуждалось.

_>согласно исходникам ядра (2.6.18) vfork именно это и делает.


Есть еще такой нюанс, что используя vfork() мы позволяем компилировать этот код на других системах, в частности на тех, где vfork() полностью эквивалентен fork(). Чтобы избежать при этом проблем, нужно вводить дополнительные проверки. Использование clone() лишено такого недостатка.

_>И, как я понимаю, такое поведение vfork-а на Линуксе сделано намеренно


Где гарантия, что такое же поведение будет в каком-нибудь ядре 2.8.20? Стандартом гарантируется лишь то, что если между vfork() и exec() или _exit() не вызывать никаких функций и не модифицировать память, то результат будет такой же, как с fork(). Иначе — неопределенное поведение. Все.

_>А в родителе vfork не отпускает управление до этого события, дабы намеренно не позволить ему поменять состояние своего адресного пространства.


Это всего-лишь один из методов облегчения запуска образа. Допустим, на какой-нибудь платформе появился очень дешевый copy-on-write. Замораживать родительский процесс в таком случае нет никакого смысла, и все хаки, основанные на предположении, что vfork() не вернется, пока не завершится дочерняя часть, идут лесом.

_>Да, я согласен, что это Linux specified, но, надо признать, что специфика эта полезная, так как исключает неопределенности описанные Вами выше.


Но вносит другие неопределенности. Поэтому повторюсь: хочется Linux specific — пользуйтесь clone().
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[15]: Запустить параллельный процесс
От: akaky_kalistrat  
Дата: 19.01.10 19:06
Оценка:
Здравствуйте, ДимДимыч, Вы писали:

ДД>Это всего-лишь один из методов облегчения запуска образа. Допустим, на какой-нибудь платформе появился очень дешевый copy-on-write. Замораживать родительский процесс в таком случае нет ДД>никакого смысла, и все хаки, основанные на предположении, что vfork() не вернется, пока не завершится дочерняя часть, идут лесом.


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

copy-on-write тут совсем не причем, vfork отимизирует не это! Основная оптимизация, которая делается vfork-ком это экономия на
копировании описателей страниц. В случае с классическим fork описатели страниц родителя копируются всегда целиком и сотояния их модифицируются
таким образом, что бы при записи в память случался copy-on-write, как в родителе, так и в потомке. В этом случае Ваш дешевый copy-on-write просто
в целом ускорит систему, а на vfork не повлияет, так как vfork и так ничего не копирует.


И потом, это не предположение, это в линуксовом мане написано.

LINUX DESCRIPTION
vfork(), just like fork(2), creates a child process of the calling process. For details and return value and errors, see fork(2).

vfork() is a special case of clone(2). It is used to create new processes without __copying the page tables__ of the parent process. It may be useful in performance sensi-
tive applications where a child will be created which then immediately issues an execve().

vfork() differs from fork() in that the parent is __suspended__ until the child makes a call to execve(2) or _exit(2). The child shares all memory with its parent, including
the stack, until execve() is issued by the child. The child must not return from the current function or call exit(), but may call _exit().

Signal handlers are inherited, but not shared. Signals to the parent arrive after the child releases the parent’s memory.
Re[16]: Запустить параллельный процесс
От: ДимДимыч Украина http://klug.org.ua
Дата: 19.01.10 19:39
Оценка:
Здравствуйте, akaky_kalistrat, Вы писали:

_>vfork не ждет пока завершится дочерняя часть,


Имеется ввиду часть дочернего процесса, находящаяся в том образе, который вызвал vfork(). Думал, это очевидно из контекста.

_>copy-on-write тут совсем не причем, vfork отимизирует не это! Основная оптимизация, которая делается vfork-ком это экономия на

_>копировании описателей страниц.

Хорошо, насчет copy-on-write я ошибаюсь. Предположим даже, что родительский процесс на определенной платформе всегда гарантированно будет ждать, пока дочерний процесс не выполнит _exit() или exec(). Как это поможет в данной задаче? Опишите алгорим. Может я туплю и чего-то не понимаю.
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[17]: Запустить параллельный процесс
От: akaky_kalistrat  
Дата: 20.01.10 18:17
Оценка:
если я правильно понял, что нужно автору, то вот:

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

int main(int argc, char **argv)
{
    if (argc == 2)
    {
        sleep(atoi(argv[1]));
        printf("Child: at this point my parent exited vfork and waits for me...\n");
        return EXIT_SUCCESS;
    }
    char* chld_argv[] = {argv[0], "1", NULL};

    clearenv();
    pid_t pid = vfork();
    if (pid == 0) 
    {
        printf("Child: at this point my parent is blocked by vfork.\n");//this line is unsafe - added for example purposes...
        execvp(chld_argv[0], chld_argv);
        _exit(127);//127 traditionally indicates exec failure...
    }

    int status = 0;
    printf("Parent: at this point child process is ready to continue or execvp has failed.\n");
    while (true) 
    {
        int wpid = wait(&status);
        if (wpid > 0 && wpid != pid)
            continue;
        if (wpid == pid)
        {
            if (status != 127)
                break;
            printf("Parent: execvp has failed in context of child process.\nGoodbye.\n");
            return EXIT_FAILURE;
        }
    }

    printf("Parent: child process exited with status %d.\nGoodbye.\n", status);
    return EXIT_SUCCESS;
}
Re[18]: Запустить параллельный процесс
От: ДимДимыч Украина http://klug.org.ua
Дата: 20.01.10 18:31
Оценка:
Здравствуйте, akaky_kalistrat, Вы писали:

_>если я правильно понял, что нужно автору, то вот:


_>
...
_>        return EXIT_SUCCESS;
...
_>        _exit(127);//127 traditionally indicates exec failure...
...
_>            if (status != 127)
...
_>


Это хорошо, когда мы знаем, что дочерний процесс всегда возвращает EXIT_SUCCESS. Если же он тоже может возвращать 127, то отличить это от нашего _exit(127) мы не сможем. Здесь
Автор: netch80
Дата: 08.01.10
о кодах возврата тоже есть.
vfork() в данном примере — платформенно-зависимый аналог pipe()/fork()/FD_CLOEXEC. Ладно, соглашусь, в наколенных поделках использовать можно
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.