Подробный гайд: `ncurses` и кастомное ядро Linux
Прежде чем углубляться в технические детали, важно сразу прояснить фундаментальный архитектурный момент, который часто вызывает путаницу:
ncurses
- это пользовательская (user-space) библиотека. Ядро Linux работает в kernel space и не может линковаться ни с
libncurses, ни сglibc, ни с любой другой стандартной библиотекой пользовательского пространства.
В зависимости от вашей реальной цели, решение будет кардинально отличаться. Ниже разобраны оба сценария с пошаговыми инструкциями.
Сценарий 1: Вы хотите текстовый/консольный интерфейс внутри самого ядра
Почему ncurses не подойдёт
- Ядро не имеет динамического линкера (
ld.so) - Отсутствует
stdio,malloc,signal,termiosв привычном виде - Терминал управляется через подсистемы
tty,vt,console, а не через ANSI-эскейп последовательности в пользовательском стиле - Компилятор выдаст ошибки на
#include <ncurses.h>ещё на этапе сборки ядра
Что использовать вместо ncurses в ядре
| Задача | Встроенный механизм ядра |
|---|---|
| Вывод текста в консоль | printk(), console->write(), vc_scrolldelta |
| Интерактивный ввод в консоли | tty_input(), kbd_event(), input subsystem |
| Псевдографика/курсор | con_write(), update_screen(), fbcon / drm |
| Отладочные интерфейсы | debugfs, sysfs, procfs, seq_file |
| Простое меню в консоли | Самописный обработчик на основе tty + ANSI-последовательности (ограниченно) |
Обязательные опции ядра для полноценной работы консоли
Если вы собираете кастомное ядро и хотите, чтобы в user-space корректно работали терминалы (включая ncurses-приложения), убедитесь, что включены:
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_HW_CONSOLE=y
CONFIG_LEGACY_PTYS=y # если нужны /dev/pty*
CONFIG_SERIAL_8250_CONSOLE=y # для serial-консоли
CONFIG_FB=y или CONFIG_DRM_FBDEV_EMULATION=y # для framebuffer
CONFIG_TTY=y
CONFIG_UNIX98_PTYS=y
Эти опции не добавляют
ncursesв ядро, а обеспечивают корректную работу/dev/tty*и терминальных драйверов, от которых зависят пользовательские приложения.
Сценарий 2: Вы запускаете ncurses-приложения поверх кастомного ядра
Это штатный сценарий. Ядро само по себе не требует библиотек для ncurses. Всё зависит от вашего rootfs / toolchain / user-space окружения.
Зависимости ncurses (компиляция и запуск)
| Тип | Зависимость | Примечание |
|---|---|---|
| Compile-time | gcc / clang, make, pkg-config, bash |
Стандартный тулчейн |
| Compile-time | Заголовки libc (stdio.h, unistd.h, termios.h, dlfcn.h) |
Обычно в sysroot |
| Run-time | libc.so (glibc / musl / uClibc) |
Динамический линкер + базовая libc |
| Run-time | libtinfo.so (или libncurses.so с встроенным termlib) |
Современный ncurses разделён на libncurses + libtinfo |
| Run-time | terminfo база (/usr/share/terminfo) |
Без неё ncurses не сможет рисовать интерфейсы |
| Run-time | /dev/tty*, /dev/ptmx |
Предоставляются ядром + devtmpfs / udev |
Пошаговая сборка ncurses под кастомную систему
1. Подготовка тулчейна
Если вы собираете rootfs с нуля, используйте один из подходов:
- Buildroot:
make menuconfig→Target packages → Libraries → ncurses(всё настроится автоматически) - Yocto/OpenEmbedded:
IMAGE_INSTALL += "ncurses terminfo"
- Вручную (кросс-компиляция):
export CROSS_COMPILE=arm-linux-gnueabihf-
export SYSROOT=/path/to/your/sysroot
./configure \
--host=arm-linux-gnueabihf \
--prefix=/usr \
--with-shared \
--with-termlib \
--without-debug \
--without-ada \
--enable-widec \
--with-default-terminfo-dir=/usr/share/terminfo
make -j$(nproc)
make install DESTDIR=$SYSROOT
2. Проверка зависимостей собранной библиотеки
$ file $SYSROOT/usr/lib/libncurses.so.6
$ ldd $SYSROOT/usr/lib/libncurses.so.6
linux-vdso.so.1 => (0x...)
libtinfo.so.6 => /usr/lib/libtinfo.so.6
libc.so.6 => /lib/libc.so.6
/lib/ld-linux-armhf.so.3 => /lib/ld-linux-armhf.so.3
Убедитесь, что все пути указывают на корректный sysroot или rootfs.
3. Копирование terminfo
База терминов обязательна.
Без неё initscr() вернёт NULL или будет использовать сырой режим:
cp -r /usr/share/terminfo $SYSROOT/usr/share/
4. Тестовое приложение
// test_ncurses.c
#include <ncurses.h>
int main() {
initscr();
printw("Hello from custom kernel space!\n");
refresh();
getch();
endwin();
return 0;
}
Сборка:
$CC test_ncurses.c -o test_ncurses -lncurses -ltinfo
Копирование в rootfs + проверка:
$ ldd test_ncurses
$ chroot $SYSROOT /usr/bin/test_ncurses
Чек-лист готовности системы к ncurses
| Компонент | Статус | Как проверить |
|---|---|---|
Ядро: CONFIG_VT=y, CONFIG_TTY=y |
OK/FAIL | zcat /proc/config.gz | grep -E 'VT|TTY' |
| Динамический линкер в rootfs | OK/FAIL | ls /lib/ld-*.so* |
libc + libtinfo + libncurses |
OK/FAIL | ldd /usr/bin/ncurses_app |
База terminfo |
OK/FAIL | ls /usr/share/terminfo/x/xterm |
/dev/tty*, /dev/ptmx доступны |
OK/FAIL | ls -l /dev/tty* /dev/ptmx |
Переменная TERM установлена |
OK/FAIL | echo $TERM (обычно xterm-256color или linux) |
Альтернативы и рекомендации
| Задача | Рекомендуемое решение |
|---|---|
| Лёгкий текстовый UI в embedded | newt, slang, или прямой вывод через termios + ANSI |
| Консольное меню в initramfs | busybox ash + dialog/whiptail (собраны статически) |
| Графический framebuffer UI | fbterm, psplash, directfb или SDL2 с fbdev |
| Минимальный rootfs для ncurses | musl + ncurses (static) + busybox → ~2-4 МБ |
Важно:
- Если вы собираете ядро для embedded/IoT и хотите сэкономить место, рассмотрите статическую сборку
ncursesс флагом--with-shared=no. Это увеличит размер бинарника, но уберёт зависимость от.soв runtime.
Итог
ncursesне линкуется с ядром и не может использоваться внутриkernel space.- Для работы
ncurses-приложений кастомному ядру нужны только стандартные подсистемы терминала (CONFIG_VT,CONFIG_TTY,CONFIG_FB/DRM). - Все библиотеки (
libc,libtinfo,libncurses,terminfo) собираются и разворачиваются в user-space rootfs. - При кросс-компиляции обязательно указывайте
--host,--prefix,--with-termlibи копируйтеterminfo. - Используйте Buildroot/Yocto для автоматизации, если собираете систему с нуля.