Вызов newpid=fork()
создает новый процесс, являющейся точной копией текущего и отличающийся лишь возвращаемым значением newpid
. В родительском процессе newpid
равно PID
дочернего процесса, в дочернем процессе newpid
равно 0. Свой PID можно узнать вызовом mypid=getpid()
, родительский – вызовом parentpid=getppid()
.
Типичный пример
int pid, cpid;
int
int status;
pid=fork()
if( pid > 0 ){
cpid=waitpid(&status);
if( cpid > 0 ){
printf("Я старый процесс (pid=%i) создал новый (pid=%i) завершившийся с кодом %i\n",
getpid(),pid,WEXITSTATUS(status) );
}
}else if( pid == 0 ){
printf("Я новый процесс (pid=%i) создан старым (pid=%i)\n",getpid(),getppid());
}else{
perror("Страшная ошибка:");
}
В оперативной памяти процесса находятся код и данные, загруженные из файла. При запуске программы из командной строки, обычно создается новый процесс и в его память загружается файл с программой. Загрузка файла делается вызовом одной из функций семейства exec (см. man 3 exec
). Функции отличаются способом передачи параметров, а также тем, используется ли переменная окружения PATH для поиска исполняемого файла. Например execl в качестве первого параметра принимает имя исполняемого файла, вторым и последующими – строки аргументы, передаваемые в argv[], и, наконец, последний параметр должен быть NULL, он дает процедуре возможность определить, что параметров больше нет.
int pid=fork();
if( pid > 0 ){
waitpid(NULL);
}else if( pid == 0 ) {
if(-1 == execl("/bin/ls","ls","-l",NULL) ) {
exit(1);
}
}
Пример exec
с двумя ошибками:
if( 0 == execl("/bin/ls","-l",NULL) ){
printf("Программа ls запущена успешно\n");
}else{
printf("Программа ls не запущена\n");
}
Ошибка 1: Первый аргумент передаваемый программе это имя самой программы. В данном примере в списке процессов будет видна программа с именем -l, запущенная без параметров.
Ошибка 2:
Поскольку код из файла /bin/ls
будет загружен в текущий процесс, то старый код и данные, в том числе printf("Программа ls запущена успешно\n"),
будет затерты. Первый printf
не сработает никогда.
Процесс может завершиться, получив сигнал или через системный вызов _exit(int status)
. status
может принимать значения от 0 до 255. По соглашению, status==0
означает успешное завершение программы, а ненулевое значение - означает ошибку. Некоторые программы (например kaspersky для Linux) используют статус для возврата некоторой информации о результатах работы программы.
_exit()
может быть вызван несколькими путями.
return status;
в функции main()
. В этом случае _exit()
выполнит некая служебная функция, вызывающая main()
exit(status)
, которая завершает работу библиотеки libc
и вызывает _exit()
_exit()
После завершения процесса его pid
остается занят - это состояние процесса называется "зомби". Чтобы освободить pid
родительский процесс должен дождаться завершения дочернего и очистить таблицу процессов. Это достигается вызовом:
pid_t cpid=waitpid(pid_t pid, int *status, int options)
//или
pid_t cpid=wait(int *status)
Вызов wait(&status);
эквивалентен waitpid(-1, &status, 0);
waitpid
ждет завершения дочернего процесса и возвращает его PID
. Код завершения и обстоятельства завершения заносятся в переменную status. Дополнительно, поведением waitpid
можно управлять через параметр options.
Опция WNOHANG - означает неблокирующую проверку завершившихся дочерних процессов.
Статус завершения проверяется макросами: