Nikita Shirikov Ed blog

Processes

Более полная версия лекции от Андрея @khromotov

Процессы

Посмореть список процессов можно с помощью команды ps

Или зайдя в виртуальную директорию /proc

Возможные состояния процессов:

Можем останавливать процессы с помощью < 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, пока не найдется подходящая программа.

#Os