Лаб. работа "Работа с процессами в Linux"

Цель лабораторной работы

Изучение основных аспектов работы с процессами в операционной системе Linux с целью приобретения практических навыков управления процессами, мониторинга и анализа их работы.

Задачи лабораторной работы

  1. Изучение основных понятий в системном программировании относительно процессов в Linux (процессы, идентификаторы процессов, дерево процессов и т. д.).
  2. Ознакомиться с командами и утилитами Linux для работы с процессами (ps, top, kill и т. д.) и их ключами.
  3. Создать простые процессоы с использованием системных вызовов fork() и exec().
  4. Изучить механизмы коммуникации между процессами (каналы, сокеты, очереди сообщений).
  5. Оформить отчет о выполненной работе, включающий описание поставленных задач, исходный код программ и результаты тестирования.

Порядок выполнения работы

  1. Создайте программу на языке Си, которая использует системные вызовы fork() для создания нового процесса.

  2. Используйте системные вызовы exec() в дочернем процессе для выполнения другой программы (например, ls, ps, или ваша собственная программа).

  3. Изучите механизм каналов (pipes) и реализуйте коммуникацию между родительским и дочерним процессами через канал.

  4. Изучите семафоры (semaphores) и реализуйте пример синхронизации между несколькими процессами.

Примеры

Пример 1: Создание нового процесса с помощью системного вызова fork():

 1#include <stdio.h>
 2#include <unistd.h>
 3
 4int main() {
 5    pid_t pid;
 6
 7    pid = fork();
 8
 9    if (pid < 0) {
10        fprintf(stderr, "Ошибка при вызове fork()\n");
11        return 1;
12    } else if (pid == 0) {
13        printf("Это дочерний процесс (PID: %d)\n", getpid());
14    } else {
15        printf("Это родительский процесс (PID: %d), дочерний процесс создан (PID: %d)\n", getpid(), pid);
16    }
17
18    return 0;
19}

Пример 2: Использование системного вызова exec() для выполнения программы в созданном процессе:

 1#include <stdio.h>
 2#include <unistd.h>
 3
 4int main() {
 5    pid_t pid;
 6
 7    pid = fork();
 8
 9    if (pid < 0) {
10        fprintf(stderr, "Ошибка при вызове fork()\n");
11        return 1;
12    } else if (pid == 0) {
13        printf("Это дочерний процесс (PID: %d)\n", getpid());
14        execl("/bin/ls", "ls", "-l", NULL);
15    } else {
16        printf("Это родительский процесс (PID: %d), дочерний процесс создан (PID: %d)\n", getpid(), pid);
17    }
18
19    return 0;
20}

Пример 3: Использование каналов (pipes) для обмена данными между процессами:

 1#include <stdio.h>
 2#include <stdlib.h>
 3#include <unistd.h>
 4
 5int main() {
 6    int pipefd[2];
 7    pid_t pid;
 8    char buffer[20];
 9
10    if (pipe(pipefd) == -1) {
11        perror("pipe");
12        exit(EXIT_FAILURE);
13    }
14
15    pid = fork();
16
17    if (pid == -1) {
18        perror("fork");
19        exit(EXIT_FAILURE);
20    }
21
22    if (pid == 0) {
23        close(pipefd[0]);  // Закрываем конец для чтения в дочернем процессе
24        write(pipefd[1], "Привет от дочернего процесса", 30);
25        close(pipefd[1]);  // Закрываем конец для записи в дочернем процессе
26        exit(EXIT_SUCCESS);
27    } else {
28        close(pipefd[1]);  // Закрываем конец для записи в родительском процессе
29        read(pipefd[0], buffer, sizeof(buffer));
30        printf("Родительский процесс прочитал: %s\n", buffer);
31        close(pipefd[0]);  // Закрываем конец для чтения в родительском процессе
32    }
33
34    return 0;
35}

Пример 4: Использование семафоров (semaphores) для синхронизации между процессами:

 1#include <stdio.h>
 2#include <stdlib.h>
 3#include <unistd.h>
 4#include <sys/types.h>
 5#include <sys/ipc.h>
 6#include <sys/sem.h>
 7
 8#define SEM_KEY 1234
 9
10union semun {
11    int val;
12    struct semid_ds *buf;
13    unsigned short *array;
14};
15
16int main() {
17    int sem_id;
18    struct sembuf sops;
19
20    // Создание семафора
21    sem_id = semget(SEM_KEY, 1, IPC_CREAT | 0666);
22    if (sem_id == -1) {
23        perror("semget");
24        exit(EXIT_FAILURE);
25    }
26
27    // Установка начального значения семафора
28    union semun arg;
29    arg.val = 1;
30    if (semctl(sem_id, 0, SETVAL, arg) == -1) {
31        perror("semctl");
32        exit(EXIT_FAILURE);
33    }
34
35    pid_t pid = fork();
36    if (pid == -1) {
37        perror("fork");
38        exit(EXIT_FAILURE);
39    }
40
41    if (pid == 0) {
42        sops.sem_num = 0;
43        sops.sem_op = -1;  // Уменьшаем семафор
44        sops.sem_flg = 0;
45        semop(sem_id, &sops, 1);  // Захватываем семафор
46
47        printf("Дочерний процесс захватил семафор\n");
48
49        sops.sem_op = 1;  // Увеличиваем семафор
50        semop(sem_id, &sops, 1);  // Освобождаем семафор
51    } else {
52        sops.sem_num = 0;
53        sops.sem_op = -1;  // Уменьшаем семафор
54        sops.sem_flg = 0;
55        semop(sem_id, &sops, 1);  // Захватываем семафор
56
57        printf("Родительский процесс захватил семафор\n");
58
59        sops.sem_op = 1;  // Увеличиваем семафор
60        semop(sem_id, &sops, 1);  // Освобождаем семафор
61    }
62
63    return 0;
64}

Контрольные вопросы

  1. Что такое процесс в операционной системе Linux?
  2. Каковы основные характеристики процесса (идентификатор процесса, родительский процесс, состояние процесса)?
  3. Как создать новый процесс с использованием системного вызова fork()?
  4. Как процесс может выполнить другую программу с помощью системного вызова exec()?
  5. Какие команды и утилиты Linux используются для работы с процессами (например, ps, top, kill) и какие ключи часто используются с ними?
  6. Какие механизмы коммуникации между процессами существуют в Linux?
  7. Какие действия могут быть выполнены над процессом с помощью команды kill?
  8. Какие утилиты мониторинга можно использовать для анализа работы процессов в реальном времени?
  9. Как можно определить, сколько ресурсов (памяти, CPU и т. д.) потребляет определенный процесс?
  10. Какие меры безопасности могут быть применены при работе с процессами в Linux?
← Лаб. работа "Работа с файлами в Linux"