← Назад к вопросам

Какие интерфейсы предоставляет ядро Linux?

2.2 Middle🔥 132 комментариев
#Linux и операционные системы

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Интерфейсы, предоставляемые ядром Linux

Linux предоставляет мощный и гибкий набор интерфейсов (системных вызовов, API) для взаимодействия с ядром из пользовательского пространства. Понимание этих интерфейсов критично для backend-разработчиков.

1. Системные вызовы (System Calls - syscalls)

Определение: Функции, которые переводят процесс из пользовательского режима в режим ядра для выполнения привилегированной операции.

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

int main() {
    // open() — системный вызов для открытия файла
    int fd = open("/etc/passwd", O_RDONLY);
    if (fd == -1) {
        perror("open");  // Обработка ошибки
        return 1;
    }
    
    // read() — системный вызов для чтения из файла
    char buffer[1024];
    ssize_t bytes = read(fd, buffer, sizeof(buffer));
    
    // close() — системный вызов для закрытия файла
    close(fd);
    
    return 0;
}

Переход между режимами

Пользовательский режим (User Mode)
    |
    | syscall (например, read)
    v
Режим ядра (Kernel Mode)
    | (ядро выполняет операцию)
    v
Пользовательский режим

Основные категории системных вызовов

2. Файловые операции

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

// Создание файла
int fd = open("/tmp/file.txt", O_CREAT | O_WRONLY, 0644);

// Чтение
char buf[256];
read(fd, buf, 256);

// Запись
write(fd, "Hello", 5);

// Переместить указатель
lseek(fd, 0, SEEK_SET);  // На начало файла

// Получить информацию о файле
struct stat st;
fstat(fd, &st);
printf("File size: %ld bytes\n", st.st_size);

// Закрыть файл
close(fd);

// Удалить файл
unlink("/tmp/file.txt");

// Список файлов в директории
DIR* dir = opendir("/tmp");
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
    printf("%s\n", entry->d_name);
}
closedir(dir);

3. Процессы и потоки

#include <unistd.h>
#include <sys/wait.h>

// Создание нового процесса
pid_t pid = fork();  // Дублирует текущий процесс

if (pid == 0) {
    // Код в дочернем процессе
    execvp("ls", argv);  // Замещает этот процесс на ls
} else if (pid > 0) {
    // Код в родительском процессе
    int status;
    waitpid(pid, &status, 0);  // Ждём завершения дочернего процесса
    printf("Child exited with status %d\n", WEXITSTATUS(status));
}

// Получить ID текущего процесса
pid_t my_pid = getpid();

// Получить ID родительского процесса
pid_t parent_pid = getppid();

// Завершить процесс
exit(0);  // или _exit(0) в дочернем процессе

Потоки через POSIX

#include <pthread.h>
#include <unistd.h>

void* thread_func(void* arg) {
    printf("Thread ID: %ld\n", (long)arg);
    return NULL;
}

int main() {
    // Создать поток
    pthread_t thread_id;
    pthread_create(&thread_id, NULL, thread_func, (void*)1);
    
    // Ждать завершения потока
    pthread_join(thread_id, NULL);
    
    return 0;
}

4. Сигналы

#include <signal.h>
#include <unistd.h>

// Обработчик сигнала
void signal_handler(int signum) {
    printf("Received signal %d\n", signum);
}

int main() {
    // Установить обработчик для SIGTERM
    signal(SIGTERM, signal_handler);
    
    // Бесконечный цикл
    while (1) {
        sleep(1);
    }
    
    return 0;
}

// В другом терминале: kill -TERM <pid>

Основные сигналы

  • SIGTERM (15): Graceful shutdown
  • SIGKILL (9): Принудительное завершение (нельзя обработать)
  • SIGINT (2): Ctrl+C
  • SIGCHLD: Дочерний процесс завершился
  • SIGHUP: Обрыв соединения

5. Память

#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>

// Выделить память с выравниванием страницы
size_t size = 4096;  // 1 страница (обычно)
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

if (ptr == MAP_FAILED) {
    perror("mmap");
    return 1;
}

// Использовать память
memcpy(ptr, "Hello", 5);

// Освободить
munmap(ptr, size);

6. Сокеты (Network I/O)

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

// Создать TCP сокет
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

// Адрес сервера
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "192.168.1.1", &server_addr.sin_addr);

// Подключиться
connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));

// Отправить данные
send(sock, "Hello", 5, 0);

// Получить данные
char buffer[1024];
recv(sock, buffer, sizeof(buffer), 0);

// Закрыть
close(sock);

7. Управление правами доступа и пользователями

#include <unistd.h>
#include <sys/types.h>

// Получить текущего пользователя
uid_t uid = getuid();
printf("UID: %d\n", uid);

// Получить текущую группу
gid_t gid = getgid();
printf("GID: %d\n", gid);

// Изменить права доступа файла
chmod("/tmp/file.txt", 0644);  // rw-r--r--

// Изменить владельца файла (нужны привилегии)
chown("/tmp/file.txt", 1000, 1000);

// Установить эффективный UID (для setuid программ)
setuid(1000);

8. Конфигурация процесса

#include <sys/resource.h>
#include <unistd.h>

// Получить лимиты ресурсов
struct rlimit limits;
getrlimit(RLIMIT_NOFILE, &limits);  // Максимум открытых файлов
printf("Max files: %ld\n", limits.rlim_cur);

// Установить лимит
limits.rlim_cur = 10000;
setrlimit(RLIMIT_NOFILE, &limits);

// Приоритет процесса
nice(10);  // Уменьшить приоритет (0-19)

// Или более точно
setpriority(PRIO_PROCESS, 0, -10);  // Увеличить приоритет

9. Асинхронный I/O (epoll, poll, select)

#include <sys/epoll.h>
#include <unistd.h>

// Создать epoll инстанс
int epoll_fd = epoll_create1(0);

// Добавить сокет к мониторингу
struct epoll_event event;
event.events = EPOLLIN;  // Мониторить чтение
event.data.fd = sock;    // Ассоциировать с сокетом
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &event);

// Ждать события
struct epoll_event events[10];
int nfds = epoll_wait(epoll_fd, events, 10, -1);  // Вечное ожидание

for (int i = 0; i < nfds; i++) {
    if (events[i].events & EPOLLIN) {
        // Сокет готов к чтению
        int fd = events[i].data.fd;
        char buffer[1024];
        read(fd, buffer, sizeof(buffer));
    }
}

close(epoll_fd);

10. Системная информация

#include <sys/sysinfo.h>
#include <unistd.h>

// Информация о системе
struct sysinfo info;
sysinfo(&info);
printf("Uptime: %ld seconds\n", info.uptime);
printf("Total RAM: %ld bytes\n", info.totalram);
printf("Free RAM: %ld bytes\n", info.freeram);

// Количество CPU
long cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
printf("CPU count: %ld\n", cpu_count);

// Размер страницы памяти
long page_size = sysconf(_SC_PAGE_SIZE);
printf("Page size: %ld bytes\n", page_size);

Иерархия интерфейсов

Программа C++
    |
    v
стд. библиотека C (libc, glibc)
    |
    v
Системные вызовы Linux
    |
    v
Ядро Linux
    |
    v
Аппаратная часть (CPU, диск, сеть)

Рекомендации для backend

  1. Файловый I/O — используй std::fstream (обёртка вокруг read/write)
  2. Сокеты — используй стандартные библиотеки (boost::asio, или современные async фреймворки)
  3. Процессы — используй с осторожностью, предпочитай потоки
  4. Сигналы — избегай в многопоточной программе, используй signalfd()
  5. epoll/poll — для высоконагруженных серверов (nginx использует epoll)
  6. mmap — для работы с большими файлами

В production обычно используют высокоуровневые библиотеки, которые скрывают сложность системных вызовов, но понимание низкого уровня помогает при оптимизации и отладке.

Какие интерфейсы предоставляет ядро Linux? | PrepBro