Processes
Процессы
Посмореть список процессов можно с помощью команды ps
Или зайдя в виртуальную директорию /proc
Возможные состояния процессов:
- I (служебные)
- S (sleeping)
- R (running / runnable)
- D (состояние непрерываемого сна)
- Z (зомби)
Можем останавливать процессы с помощью < C-t > (сигнала). Все процессы в состоянии R планировщик ядра распределяет по ресурсам.
Учимся работать с процессами:
системный вызов fork
Виртуальная память та же, таблица файловых дискрипторов та же, состояние регистеров тоже и т.п. Индентификатор другой.
#include ‹unistd.h>
#include <stdio.h>
int main() {
fork();
printf("hello world from %d\n", getpid());
sleep(1);
}
Процессы могут быть связаны родитель - ребенок отношениями. Пользуясь fork новый процесс будет дочерним.
Узнать родительский id, можем с помощью getppid()
Сискол fork() возвращает в родительский pid дочернего, в дочерний возвращает 0 (-1 если что-то пошло не так)
Дальше играем с fork, как пример код с семинара (feat Виталий):
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
int main(int argc, char **argv) {
// 1 - command, 2 - input, 3 - output
// < 2 | > 3
// example: wc <input >output
pid_t pid = fork();
// int pid = 1;
if (pid != 0) {
int fd_in = open(argv[2], O_RDONLY);
if (fd_in == -1) {
printf("Could not open input\n");
exit(1);
}
int dp_in = dup2(fd_in, 0);
if (dp_in == -1) {
printf("some troubles with input\n");
close(fd_in);
exit(1);
}
int fd_out = open(argv[3], O_WRONLY | O_TRUNC | O_CREAT, 0664);
if (fd_out == -1) {
printf("Could not open output\n");
close(fd_in);
exit(1);
}
int dp_out = dup2(fd_out, 1);
if (dp_out == -1) {
printf("some troubles with output\n");
close(fd_in);
close(fd_out);
exit(1);
}
int ex_int = execlp(argv[1], argv[1], NULL);
close(fd_in);
close(fd_out);
}
while (wait(NULL) > 0)
;
return 0;
}
Системный вызов wait
Много различный реализаций, самая обычная:
int status;
pid_t dead = wait(&status);
В статус возвращается, как завершился процесс.
Если дочерний процесс завершается, а его родитель не ждет, то дочерний процесс переходит в статус зомби, чтоб можно было о нем достать инфу позже.
При этом дочерние процессы убиваются, если завершается родительский процесс.
Чтение файлов
Если читать один файл из 2-ух процессов, то позиция чтения / записи шарится. Почему это происходит?
struct file {.., off_t pos; int refcount = x; ...} // структура файлов внутри ядра
// файловый дескриптор, просто привязывается к этому структуре
Exec
Чтобы в текущем процессе заместить его тело какой-то другой командой, нам нужны вызовы семейства exec:
int main() {
puts("executing echo");
if (fork() == 0) {
// child process
int fd = open("output", O_WRONLY | O_CREAT | O_TRUNC, 0666);
// новый системный вызов, копирует значение одного fd в другой (закрывая его)
dup2(fd, STDOUT_FILENO); // подменяем стандартный выход на файл
close(fd);
char* cmd = "/bin/nonexistent";
char* argv[] = {"echo", "42", NULL};
char* envp[] = (NULL);
execve(cmd, argv, envp);
perror(cmd);
return 127;
}
// parent process
inr status = -1;
wait(&status);
printf("child exited with status: &d\n", WEXITSTATUS(status));
}
Как соптимизировать копирование? Будем лениво копировать, только при возникновении исключительной ситуации page fault -> если хотим изменить какую-то страницу, то тогда уже копируем и меняем.
Переменная $path - список стандартных системных директорий
В С есть обертка над сисколами семейства exec, суффикс p обозначает, что имя программы подставляется ко всем директориям из $path, пока не найдется подходящая программа.