← Назад к вопросам
Какие интерфейсы предоставляет ядро 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
- Файловый I/O — используй std::fstream (обёртка вокруг read/write)
- Сокеты — используй стандартные библиотеки (boost::asio, или современные async фреймворки)
- Процессы — используй с осторожностью, предпочитай потоки
- Сигналы — избегай в многопоточной программе, используй signalfd()
- epoll/poll — для высоконагруженных серверов (nginx использует epoll)
- mmap — для работы с большими файлами
В production обычно используют высокоуровневые библиотеки, которые скрывают сложность системных вызовов, но понимание низкого уровня помогает при оптимизации и отладке.