Linux c fork exec
Для порождения процессов в ОС Linux существует два способа. Один из них позволяет полностью заменить другой процесс, без замены среды выполнения. Другим способом можно создать новый процесс с помощью системного вызова fork() . Синтаксис вызова следующий:
pid_t является примитивным типом данных, который определяет идентификатор процесса или группы процессов. При вызове fork() порождается новый процесс (процесс-потомок), который почти идентичен порождающему процессу-родителю. Процесс-потомок наследует следующие признаки родителя:
- сегменты кода, данных и стека программы;
- таблицу файлов, в которой находятся состояния флагов дескрипторов файла, указывающие, читается ли файл или пишется. Кроме того, в таблице файлов содержится текущая позиция указателя записи-чтения;
- рабочий и корневой каталоги;
- реальный и эффективный номер пользователя и номер группы;
- приоритеты процесса (администратор может изменить их через nice );
- контрольный терминал;
- маску сигналов;
- ограничения по ресурсам;
- сведения о среде выполнения;
- разделяемые сегменты памяти.
Потомок не наследует от родителя следующих признаков:
- идентификатора процесса (PID, PPID);
- израсходованного времени ЦП (оно обнуляется);
- сигналов процесса-родителя, требующих ответа;
- блокированных файлов (record locking).
При вызове fork() возникают два полностью идентичных процесса. Весь код после fork() выполняется дважды, как в процессе-потомке, так и в процессе-родителе.
Процесс-потомок и процесс-родитель получают разные коды возврата после вызова fork() . Процесс-родитель получает идентификатор (PID) потомка. Если это значение будет отрицательным, следовательно при порождении процесса произошла ошибка. Процесс-потомок получает в качестве кода возврата значение 0, если вызов fork() оказался успешным.
Таким образом, можно проверить, был ли создан новый процесс:
Пример порождения процесса через fork() приведен ниже:
Когда потомок вызывает exit() , код возврата передается родителю, который ожидает его, вызывая wait() . WEXITSTATUS() представляет собой макрос, который получает фактический код возврата потомка из вызова wait() .
Функция wait() ждет завершения первого из всех возможных потомков родительского процесса. Иногда необходимо точно определить, какой из потомков должен завершиться. Для этого используется вызов waitpid() с соответствующим PID потомка в качестве аргумента. Еще один момент, на который следует обратить внимание при анализе примера, это то, что и родитель, и потомок используют переменную rv . Это не означает, что переменная разделена между процессами. Каждый процесс содержит собственные копии всех переменных.
Рассмотрим следующий пример:
В этом случае будет создано семь процессов-потомков. Первый вызов fork() создает первого потомка. Как указано выше, процесс наследует положение указателя команд от родительского процесса. Указатель команд содержит адрес следующего оператора программы. Это значит, что после первого вызова fork() указатель команд и родителя, и потомка находится перед вторым вызовом fork() .После второго вызова fork() и родитель, и первый потомок производят потомков второго поколения — в результате образуется четыре процесса. После третьего вызова fork() каждый процесс производит своего потомка, увеличивая общее число процессов до восьми.
Так называемые процессы-зомби возникают, если потомок завершился, а родительский процесс не вызвал wait() . Для завершения процессов используют либо оператор возврата, либо вызов функции exit() со значением, которое нужно возвратить операционной системе. Операционная система оставляет процесс зарегистрированным в своей внутренней таблице данных, пока родительский процесс не получит кода возврата потомка, либо не закончится сам. В случае процесса-зомби его код возврата не передается родителю, и запись об этом процессе не удаляется из таблицы процессов операционной системы. При дальнейшей работе и появлении новых зомби таблица процессов может быть заполнена, что приведет к невозможности создания новых процессов.
Understanding fork, exec, and wait in C++ (Linux)
I’m very new to using these different types of system calls in linux, Which led me to much confusion. With this, I am only asking for a push in the right direction/a start, not for a completion. Using fork , exec , and wait , I have read up on them, but that still hasn’t really helped me in my situation. What I have to do is the following,
Print a promt and wait for the user to enter a command with up to four arguments or options. «exit» will stop the program.
Some examples, mkdir blah , creates the directory and then prompts for a new command, touch blah/this blah/that blah/there .
I have to call fork to create a child process to execute the command entered, then call exec in the child process to make the child become the program that is to be executed (this part confuses me even more), and finally call wait in the parent process so that the interpreter doesn’t print next prompt until command is finished.
What would be the best way to accomplish this? As in, what’s the best way to read in the command/arguments/options and then have them executed? I would assume it would be better to do this is a do..while with the while condition being what checks for «exit»
What little I have done, which isn’t much, I know.
Понимание fork, exec и wait в C ++ (Linux)
Я очень новичок в использовании этих различных типов системных вызовов в Linux, что привело меня в замешательство. При этом я прошу только толчок в правильном направлении / начало, а не завершение.
С помощью fork , exec , а также wait Я прочитал их, но это все еще не помогло мне в моей ситуации.
Что мне нужно сделать, это следующее,
Распечатайте приглашение и подождите, пока пользователь введет команду с четырьмя аргументами или параметрами. «выход» остановит программу.
Некоторые примеры, mkdir blah , создает каталог, а затем запрашивает новую команду, touch blah/this blah/that blah/there ,
Я должен позвонить fork создать дочерний процесс для выполнения введенной команды, затем вызвать exec в дочернем процессе, чтобы заставить ребенка стать программой, которая должна быть выполнена (эта часть меня смущает еще больше), и, наконец, вызвать wait в родительском процессе, чтобы интерпретатор не выводил следующую подсказку до завершения команды.
Каков наилучший способ сделать это? Как лучше всего прочитать команду / arguments / options и затем выполнить их?
Я бы предположил, что было бы лучше сделать это do..while с while условие, которое проверяет «выход»
То, что я сделал немного, я знаю немного.
Решение
Общая разбивка того, что делает каждый из этих системных вызовов:
вилка: Форки текущего процесса. Буквально, когда вызывается fork, выполнение приостанавливается при вызове fork, и вся программа копируется в новое пространство процесса, являющееся дочерним по отношению к оригиналу. Затем оба процесса продолжают выполнение сразу после параллельного вызова fork. Вам нужно будет получить PID, чтобы узнать, является ли выполняемая в данный момент программа дочерним или родительским.
Exec: Приостанавливает выполнение текущего процесса, стирает текущий процесс в памяти с помощью назначенной новой программы для запуска. Затем он запускает новую программу.
Подождите: Приостанавливает текущий процесс до тех пор, пока не завершится хотя бы один дочерний процесс. Это обертка вокруг waitpid (), которая позволяет приостановить выполнение текущего процесса и дождаться изменения состояния дочернего процесса текущего процесса (который может быть клоном самого себя или новой программы, замененной на Exec)
Вот некоторый код, демонстрирующий ожидание и разветвление (но не exec) из класса, который я взял в университете:
YoLinux Tutorial: Fork, Exec and Process control
This tutorial will cover the creation of child processes and process control using fork, exec and other C library function calls using the GNU «C» compiler on the Linux operating system.
Related YoLinux Tutorials:
The fork() system call will spawn a new child process which is an identical process to the parent except that has a new system process ID. The process is copied in memory from the parent and a new process structure is assigned by the kernel. The return value of the function is which discriminates the two threads of execution. A zero is returned by the fork function in the child’s process.
The environment, resource limits, umask, controlling terminal, current working directory, root directory, signal masks and other process resources are also duplicated from the parent in the forked child process.
Compile: g++ -o ForkTest ForkTest.cpp
Run: ForkTest
Note on exit() vs _exit(): The C library function exit() calls the kernel system call _exit() internally. The kernel system call _exit() will cause the kernel to close descriptors, free memory, and perform the kernel terminating process clean-up. The C library function exit() call will flush I/O buffers and perform aditional clean-up before calling _exit() internally. The function exit(status) causes the executable to return «status» as the return code for main(). When exit(status) is called by a child process, it allows the parent process to examine the terminating status of the child (if it terminates first). Without this call (or a call from main() to return()) and specifying the status argument, the process will not return a value.
The vfork() function is the same as fork() except that it does not make a copy of the address space. The memory is shared reducing the overhead of spawning a new process with a unique copy of all the memory. This is typically used when using fork() to exec() a process and terminate. The vfork() function also executes the child process first and resumes the parent process when the child terminates.
Compile: g++ -o VForkTest VForkTest.cpp
Run: VForkTest
Note: The child process executed first, updated the variables which are shared between the processes and NOT unique, and then the parent process executes using variables which the child has updated.
[Potential Pitfall] : A deadlock condition may occur if the child process does not terminate, the parent process will not proceed.- vfork — create a child process and block parent
- _exit — — terminate the current process
The function clone() creates a new child process which shares memory, file descriptors and signal handlers with the parent. It implements threads and thus launches a function as a child. The child terminates when the parent terminates.
See the YoLinux POSIX threads tutorial
The parent process will often want to wait until all child processes have been completed. this can be implemented with the wait() function call.
wait(): Blocks calling process until the child process terminates. If child process has already teminated, the wait() call returns immediately. if the calling process has multiple child processes, the function returns when one returns.
waitpid(): Options available to block calling process for a particular child process not the first one.
Avoids orphaned process group when parent terminates. When parent dies, this will be a zombie. (No parent process. Parent=1) Instead, create a new process group for the child. Later process the group is terminated to stop all spawned processes. Thus all subsequent processes should be of this group if they are to be terminated by the process group id. Process group leader has the same process id and group process id. If not changed then the process group is that of the parent. Set the process group id to that of the child process.
The macro testing for __gnu_linux__ is for cross platform support as man other OS’s use a different system call.
- setpgid/getpgid setpgrp/getpgrp — set process group
- setsid — creates a session and sets the process group ID
- getuid/geteuid — get user identity
- setgid — set group identity
- getgid/getegid — get group (real/effective) identity
- setreuid/setregid — set real user or group identity
- errno — number of last error
Kill all processes in a process group:
See /usr/include/bits/signum.h for list of signals.
Man Pages:
- killpg — send signal to a process group
- kill — send signal to a process
- signal (2) — ANSI C signal handling
- signal (7) — List of available signals
- sigaction — POSIX signal handling functions.
- pause (2) — wait for signal
- raise (3) — send a signal to a current process
The system() call will execute an OS shell command as described by a character command string. This function is implemented using fork(), exec() and waitpid(). The command string is executed by calling /bin/sh -c command-string. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored. The call «blocks» and waits for the task to be performed before continuing.
The popen() call opens a process by creating a pipe, forking, and invoking the shell (bourne shell on Linux). The advantage to using popen() is that it will allow one to interrogate the results of the command issued.
This example opens a pipe which executes the shell command «ls -l«. The results are read and printed out.
The second argument to popen:
- r: Read from stdin (command results)
- w: write to stdout (command)
- For stderr: command=»ls -l 2>&1″,»w»);
The exec() family of functions will initiate a program from within a program. They are also various front-end functions to execve().
The functions return an integer error code. (0=Ok/-1=Fail).
execl() and execlp():
int execl(const char *path, const char *arg0, const char *arg1, const char *arg2, . const char *argn, (char *) 0);
The routine execlp() will perform the same purpose except that it will use environment variable PATH to determine which executable to process. Thus a fully qualified path name would not have to be used. The first argument to the function could instead be «ls». The function execlp() can also take the fully qualified name as it also resolves explicitly.
execv() and execvp():
This is the same as execl() except that the arguments are passed as null terminated array of pointers to char. The first element «argv[0]» is the command name.
int execv(const char *path, char *const argv[]);
The routine execvp() will perform the same purpose except that it will use environment variable PATH to determine which executable to process. Thus a fully qualified path name would not have to be used. The first argument to the function could instead be «ls». The function execvp() can also take the fully qualified name as it also resolves explicitly.
execve():
The function call «execve()» executes a process in an environment which it assigns.
Set the environment variables:
Man Pages:
- strerror / strerror_r — return string describing error code
- errno — number of last error
- perror — print a system error message
Data File: environment_variables.conf
Man Pages:
- execve — execute with given environment
Note: Don’t mix malloc() and new. Choose one form of memory allocation and stick with it.
Man Pages:
- malloc — Dynamically allocate memory
- free — Free allocated memory
«UNIX Network Programming, Volume 1: Sockets Networking API» Third Edition by W. Richard Stevens, Bill Fenner, Andrew M. Rudoff, Richard W. Stevens ISBN # 0131411551, Addison-Wesley Pub Co; 3 edition (October 22, 2003) This book covers POSIX, IPv6, network APIs, sockets (elementary, advanced, routed, and raw), multicast, UDP, TCP, Threads, Streams, ioctl. In depth coverage of topics. | ||
«UNIX Network Programming, Volume 1: Networking APIs — Sockets and XTI» Second Edition by W. Richard Stevens ISBN # 013490012X, Prentice Hall PTR This book covers network APIs, sockets + XTI, multicast, UDP, TCP, ICMP, raw sockets, SNMP, MBONE. In depth coverage of topics. | ||
«UNIX Network Programming Volume 2: Interprocess Communications» by W. Richard Stevens ISBN # 0130810819, Prentice Hall PTR This book covers semaphores, threads, record locking, memory mapped I/O, message queues, RPC’s, etc. | ||
«Advanced Linux Programming» by Mark Mitchell, Jeffrey Oldham, Alex Samuel, Jeffery Oldham ISBN # 0735710430, New Riders Good book for programmers who already know how to program and just need to know the Linux specifics. Covers a variety of Linux tools, libraries, API’s and techniques. If you don’t know how to program, start with a book on C. | ||
«Advanced UNIX Programming» Second Edition by Marc J. Rochkind ISBN # 0131411543, Addison-Wesley Professional Computing Series | ||
«Advanced Programming in the UNIX Environment» First Edition by W. Richard Stevens ISBN # 0201563177, Addison-Wesley Professional Computing Series It is the C programmers guide to programming on the UNIX platform. This book is a must for any serious UNIX/Linux programmer. It covers all of the essential UNIX/Linux API’s and techniques. This book starts where the basic C programming book leaves off. Great example code. This book travels with me to every job I go to. | ||
«Advanced Unix Programming» by Warren W. Gay ISBN # 067231990X, Sams White Book Series This book covers all topics in general: files, directories, date/time, libraries, pipes, IPC, semaphores, shared memory, forked processes and I/O scheduling. The coverage is not as in depth as the previous two books (Stevens Vol 1 and 2) | ||
«Linux Programming Bible» by John Goerzen ISBN # 0764546570, Hungry Minds, Inc This covers the next step after «C» programming 101. |