diff options
Diffstat (limited to 'documentation/content/ru/books')
7 files changed, 0 insertions, 2285 deletions
diff --git a/documentation/content/ru/books/arch-handbook/driverbasics/chapter.adoc b/documentation/content/ru/books/arch-handbook/driverbasics/chapter.adoc deleted file mode 100644 index d845e849be..0000000000 --- a/documentation/content/ru/books/arch-handbook/driverbasics/chapter.adoc +++ /dev/null @@ -1,512 +0,0 @@ ---- -title: Глава 9. Написание драйверов устройств для FreeBSD -authors: ---- - -[[driverbasics]] -= Написание драйверов устройств для FreeBSD -:doctype: book -:toc: macro -:toclevels: 1 -:icons: font -:sectnums: -:sectnumlevels: 6 -:sectnumoffset: 9 -:partnums: -:source-highlighter: rouge -:experimental: - -ifdef::env-beastie[] -ifdef::backend-html5[] -:imagesdir: ../../../../images/{images-path} -endif::[] -ifndef::book[] -include::shared/authors.adoc[] -include::shared/mirrors.adoc[] -include::shared/releases.adoc[] -include::shared/attributes/attributes-{{% lang %}}.adoc[] -include::shared/{{% lang %}}/teams.adoc[] -include::shared/{{% lang %}}/mailing-lists.adoc[] -include::shared/{{% lang %}}/urls.adoc[] -toc::[] -endif::[] -ifdef::backend-pdf,backend-epub3[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] -endif::[] - -ifndef::env-beastie[] -toc::[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] - -Эту главу написал {murray} на основе множества источников, включая справочную страницу intro(4), которую создал {joerg}. - -[[driverbasics-intro]] -== Введение - -Эта глава является кратким введением в процесс написания драйверов устройств для FreeBSD. В этом контексте термин устройство используется в основном для вещей, связанных с оборудованием, относящимся к системе, таких, как диски, печатающие устройства или графические дисплеи с клавиатурами. Драйвер устройства является программной компонентой операционной системы, управляющей некоторым устройством. Имеются также так называемые псевдо-устройства, в случае которых драйвер устройства эмулирует поведение устройства программно, без наличия какой-либо соответствующей аппаратуры. Драйверы устройств могут быть вкомпилированы в систему статически или могут загружаться по требованию при помощи механизма динамического компоновщика ядра `kld`. - -Большинство устройств в Unix-подобной операционной системе доступны через файлы устройств (device-nodes), иногда также называемые специальными файлами. В иерархии файловой системы эти файлы обычно находятся в каталоге [.filename]#/dev#. В версиях FreeBSD, более старых, чем 5.0-RELEASE, в которых поддержка man:devfs[5] не интегрирована в систему, каждый файл устройства должен создаваться статически и вне зависимости от наличия соответствующего драйвера устройства. Большинство файлов устройств в системе создаются при помощи команды `MAKEDEV`. - -Драйверы устройств могут быть условно разделены на две категории; драйверы символьных и сетевых устройств. - -[[driverbasics-kld]] -== Механизм динамического компоновщика ядра - KLD - -Интерфейс kld позволяет системным администраторам динамически добавлять и убирать функциональность из работающей системы. Это позволяет разработчикам драйверов устройств загружать собственные изменения в работающее ядро без постоянных перезагрузок для тестирования изменений. - -Для работы с интерфейсом kld используются следующие команды привилегированного режима: - -* `kldload` - загружает новый модуль ядра -* `kldunload` - выгружает модуль ядра -* `kldstat` - выводит список загруженных в данный момент модулей - -Скелет модуля ядра - -[.programlisting] -.... -/* - * KLD Skeleton - * Inspired by Andrew Reiter's Daemonnews article - */ - -#include <sys/types.h> -#include <sys/module.h> -#include <sys/systm.h> /* uprintf */ -#include <sys/errno.h> -#include <sys/param.h> /* defines used in kernel.h */ -#include <sys/kernel.h> /* types used in module initialization */ - -/* - * Load handler that deals with the loading and unloading of a KLD. - */ - -static int -skel_loader(struct module *m, int what, void *arg) -{ - int err = 0; - - switch (what) { - case MOD_LOAD: /* kldload */ - uprintf("Skeleton KLD loaded.\n"); - break; - case MOD_UNLOAD: - uprintf("Skeleton KLD unloaded.\n"); - break; - default: - err = EINVAL; - break; - } - return(err); -} - -/* Declare this module to the rest of the kernel */ - -static moduledata_t skel_mod = { - "skel", - skel_loader, - NULL -}; - -DECLARE_MODULE(skeleton, skel_mod, SI_SUB_KLD, SI_ORDER_ANY); -.... - -=== Makefile - -Во FreeBSD имеются заготовки для включения в make-файлы, которые вы можете использовать для быстрой компиляции собственных дополнений к ядру. - -[.programlisting] -.... -SRCS=skeleton.c -KMOD=skeleton - -.include <bsd.kmod.mk> -.... - -Простой запуск команды `make` с этим make-файлом приведет к созданию файла [.filename]#skeleton.ko#, который можно загрузить в вашу систему, набрав: - -[source,shell] -.... -# kldload -v ./skeleton.ko -.... - -[[driverbasics-access]] -== Обращение к драйверу устройства - -Unix дает некоторый общий набор системных вызовов для использования в пользовательских приложениях. Когда пользователь обращается к файлу устройства, высокие уровни ядра перенаправляют эти обращения к соответствующему драйверу устройства. Скрипт `/dev/MAKEDEV` создает большинство файлов устройств в вашей системе, однако если вы ведете разработку своего собственного драйвера, то может появиться необходимость в создании собственных файлов устройств при помощи команды `mknod`. - -=== Создание статических файлов устройств - -Для создания файла устройства команде `mknod` требуется указать четыре аргумента. Вы должны указать имя файла устройства, тип устройства, старшее число устройства и младшее число устройства. - -=== Динамические файлы устройств - -Файловая система устройств, devfs, предоставляет доступ к пространству имен устройств ядра из глобального пространства имен файловой системы. Это устраняет потенциальную проблемы наличия драйвера без статического файла устройства или файла устройства без установленного драйвера устройства. Devfs все еще находится в разработке, однако она уже достаточно хорошо работает. - -[[driverbasics-char]] -== Символьные устройства - -Драйвер символьного устройства передает данные непосредственно в или из процесса пользователя. Это самый распространенный тип драйвера устройства и в дереве исходных текстов имеется достаточно простых примеров таких драйверов. - -В этом простом примере псевдо-устройство запоминает какие угодно значения, которые вы в него записываете, и затем может выдавать их назад при чтении из этого устройства. Приведены две версии, одна для FreeBSD 4.X, а другая для FreeBSD 5.X. - -.Пример драйвера псевдо-устройства Echo для FreeBSD 4.X -[example] -==== -[.programlisting] -.... -/* - * Simple `echo' pseudo-device KLD - * - * Murray Stokely - */ - -#define MIN(a,b) (((a) (b)) ? (a) : (b)) - -#include sys/types.h -#include sys/module.h -#include sys/systm.h /* uprintf */ -#include sys/errno.h -#include sys/param.h /* defines used in kernel.h */ -#include sys/kernel.h /* types used in module initialization */ -#include sys/conf.h /* cdevsw struct */ -#include sys/uio.h /* uio struct */ -#include sys/malloc.h - -#define BUFFERSIZE 256 - -/* Function prototypes */ -d_open_t echo_open; -d_close_t echo_close; -d_read_t echo_read; -d_write_t echo_write; - -/* Character device entry points */ -static struct cdevsw echo_cdevsw = { - echo_open, - echo_close, - echo_read, - echo_write, - noioctl, - nopoll, - nommap, - nostrategy, - "echo", - 33, /* reserved for lkms - /usr/src/sys/conf/majors */ - nodump, - nopsize, - D_TTY, - -1 -}; - -struct s_echo { - char msg[BUFFERSIZE]; - int len; -} t_echo; - -/* vars */ -static dev_t sdev; -static int len; -static int count; -static t_echo *echomsg; - -MALLOC_DECLARE(M_ECHOBUF); -MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module"); - -/* - * This function is called by the kld[un]load(2) system calls to - * determine what actions to take when a module is loaded or unloaded. - */ - -static int -echo_loader(struct module *m, int what, void *arg) -{ - int err = 0; - - switch (what) { - case MOD_LOAD: /* kldload */ - sdev = make_dev(echo_cdevsw, - 0, - UID_ROOT, - GID_WHEEL, - 0600, - "echo"); - /* kmalloc memory for use by this driver */ - MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK); - printf("Echo device loaded.\n"); - break; - case MOD_UNLOAD: - destroy_dev(sdev); - FREE(echomsg,M_ECHOBUF); - printf("Echo device unloaded.\n"); - break; - default: - err = EINVAL; - break; - } - return(err); -} - -int -echo_open(dev_t dev, int oflags, int devtype, struct proc *p) -{ - int err = 0; - - uprintf("Opened device \"echo\" successfully.\n"); - return(err); -} - -int -echo_close(dev_t dev, int fflag, int devtype, struct proc *p) -{ - uprintf("Closing device \"echo.\"\n"); - return(0); -} - -/* - * The read function just takes the buf that was saved via - * echo_write() and returns it to userland for accessing. - * uio(9) - */ - -int -echo_read(dev_t dev, struct uio *uio, int ioflag) -{ - int err = 0; - int amt; - - /* How big is this read operation? Either as big as the user wants, - or as big as the remaining data */ - amt = MIN(uio->uio_resid, (echomsg->len - uio->uio_offset > 0) ? echomsg->len - uio->uio_offset : 0); - if ((err = uiomove(echomsg->msg + uio->uio_offset,amt,uio)) != 0) { - uprintf("uiomove failed!\n"); - } - - return err; -} - -/* - * echo_write takes in a character string and saves it - * to buf for later accessing. - */ - -int -echo_write(dev_t dev, struct uio *uio, int ioflag) -{ - int err = 0; - - /* Copy the string in from user memory to kernel memory */ - err = copyin(uio->uio_iov->iov_base, echomsg->msg, MIN(uio->uio_iov->iov_len,BUFFERSIZE)); - - /* Now we need to null terminate */ - *(echomsg->msg + MIN(uio->uio_iov->iov_len,BUFFERSIZE)) = 0; - /* Record the length */ - echomsg->len = MIN(uio->uio_iov->iov_len,BUFFERSIZE); - - if (err != 0) { - uprintf("Write failed: bad address!\n"); - } - - count++; - return(err); -} - -DEV_MODULE(echo,echo_loader,NULL); -.... - -==== - -.Пример драйвера псевдо-устройства Echo для FreeBSD 5.X -[example] -==== -[.programlisting] -.... -/* - * Simple `echo' pseudo-device KLD - * - * Murray Stokely - * - * Converted to 5.X by Sren (Xride) Straarup - */ - -#include sys/types.h -#include sys/module.h -#include sys/systm.h /* uprintf */ -#include sys/errno.h -#include sys/param.h /* defines used in kernel.h */ -#include sys/kernel.h /* types used in module initialization */ -#include sys/conf.h /* cdevsw struct */ -#include sys/uio.h /* uio struct */ -#include sys/malloc.h - -#define BUFFERSIZE 256 -#define CDEV_MAJOR 33 - -/* Function prototypes */ -static d_open_t echo_open; -static d_close_t echo_close; -static d_read_t echo_read; -static d_write_t echo_write; - -/* Character device entry points */ -static struct cdevsw echo_cdevsw = { - .d_open = echo_open, - .d_close = echo_close, - .d_maj = CDEV_MAJOR, - .d_name = "echo", - .d_read = echo_read, - .d_write = echo_write -}; - -typedef struct s_echo { - char msg[BUFFERSIZE]; - int len; -} t_echo; - -/* vars */ -static dev_t echo_dev; -static int count; -static t_echo *echomsg; - -MALLOC_DECLARE(M_ECHOBUF); -MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module"); - -/* - * This function is called by the kld[un]load(2) system calls to - * determine what actions to take when a module is loaded or unloaded. - */ - -static int -echo_loader(struct module *m, int what, void *arg) -{ - int err = 0; - - switch (what) { - case MOD_LOAD: /* kldload */ - echo_dev = make_dev(echo_cdevsw, - 0, - UID_ROOT, - GID_WHEEL, - 0600, - "echo"); - /* kmalloc memory for use by this driver */ - MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK); - printf("Echo device loaded.\n"); - break; - case MOD_UNLOAD: - destroy_dev(echo_dev); - FREE(echomsg,M_ECHOBUF); - printf("Echo device unloaded.\n"); - break; - default: - err = EINVAL; - break; - } - return(err); -} - -static int -echo_open(dev_t dev, int oflags, int devtype, struct thread *p) -{ - int err = 0; - - uprintf("Opened device \"echo\" successfully.\n"); - return(err); -} - -static int -echo_close(dev_t dev, int fflag, int devtype, struct thread *p) -{ - uprintf("Closing device \"echo.\"\n"); - return(0); -} - -/* - * The read function just takes the buf that was saved via - * echo_write() and returns it to userland for accessing. - * uio(9) - */ - -static int -echo_read(dev_t dev, struct uio *uio, int ioflag) -{ - int err = 0; - int amt; - - /* - * How big is this read operation? Either as big as the user wants, - * or as big as the remaining data - */ - amt = MIN(uio->uio_resid, (echomsg->len - uio->uio_offset > 0) ? - echomsg->len - uio->uio_offset : 0); - if ((err = uiomove(echomsg->msg + uio->uio_offset,amt,uio)) != 0) { - uprintf("uiomove failed!\n"); - } - return(err); -} - -/* - * echo_write takes in a character string and saves it - * to buf for later accessing. - */ - -static int -echo_write(dev_t dev, struct uio *uio, int ioflag) -{ - int err = 0; - - /* Copy the string in from user memory to kernel memory */ - err = copyin(uio->uio_iov->iov_base, echomsg->msg, - MIN(uio->uio_iov->iov_len,BUFFERSIZE - 1)); - - /* Now we need to null terminate, then record the length */ - *(echomsg->msg + MIN(uio->uio_iov->iov_len,BUFFERSIZE - 1)) = 0; - echomsg->len = MIN(uio->uio_iov->iov_len,BUFFERSIZE); - - if (err != 0) { - uprintf("Write failed: bad address!\n"); - } - count++; - return(err); -} - -DEV_MODULE(echo,echo_loader,NULL); -.... - -==== - -Для установки этого драйвера во FreeBSD 4.X сначала вам нужно создать файл устройства в вашей файловой системе по команде типа следующей: - -[source,shell] -.... -# mknod /dev/echo c 33 0 -.... - -Когда этот драйвер загружен, вы можете выполнять следующие действия: - -[source,shell] -.... -# echo -n "Test Data" > /dev/echo -# cat /dev/echo -Test Data -.... - -Устройства, обслуживающие реальное оборудование, описываются в следующей главе. - -Дополнительные источники информации - -* http://www.daemonnews.org/200010/blueprints.html[Учебник по программированию механизма динамического компоновщика ядра (KLD)] - http://www.daemonnews.org/[Daemonnews] Октябрь 2000 -* http://www.daemonnews.org/200007/newbus-intro.html[Как писать драйверы ядра в парадигме NEWBUS] - http://www.daemonnews.org/[Daemonnews] Июль 2000 - -[[driverbasics-block]] -== Блочные устройства (которых больше нет) - -Другие UNIX(R)-системы могут поддерживать со вторым типом дисковых устройств, так называемых устройств с блочной организацией. Блочные устройства являются дисковыми устройствами, для которых ядро организует кэширование. Такое кэширование делает блочные устройства практически бесполезными, или по крайней мере ненадёжными. Кэширование изменяет последовательность операций записи, лишая приложение возможности узнать реальное содержимое диска в любой момент времени. Это делает предсказуемое и надежное восстановление данных на диске (файловые системы, базы данных и прочее) после сбоя невозможным. Так как запись может быть отложенной, то нет способа сообщить приложению, при выполнении какой именно операции записи ядро встретилось с ошибкой, что таким образом осложняет проблему целостности данных. По этой причине серьёзные приложения не полагаются на блочные устройства, и, на самом деле практически во всех приложениях, которые работают с диском напрямую, имеется большая проблема выбора устройств с последовательным доступом (или "raw"), которые должны использоваться. Из-за реализации отображения каждого диска (раздела) в два устройства с разными смыслами, которая усложняет соответствующий код ядра, во FreeBSD поддержка дисковых устройств с кэшированием была отброшена в процессе модернизации инфраструктуры I/O-операций с дисками. - -[[driverbasics-net]] -== Сетевые драйверы - -В случае драйверов сетевых устройств файлы устройств для доступа к ним не используются. Их выбор основан на другом механизме, работающем в ядре, и не использующем вызов open(); об использование сетевых устройств в общем случае рассказано в описании системного вызова socket(2). - -Почитайте справочную информацию о вызове ifnet(), устройстве loopback, почитайте драйверы Билла Пола (Bill Paul), и так далее.. diff --git a/documentation/content/ru/books/arch-handbook/locking/chapter.adoc b/documentation/content/ru/books/arch-handbook/locking/chapter.adoc deleted file mode 100644 index bdb37f8e29..0000000000 --- a/documentation/content/ru/books/arch-handbook/locking/chapter.adoc +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: Глава 2. Замечания по блокировке -authors: ---- - -[[locking]] -= Замечания по блокировке -:doctype: book -:toc: macro -:toclevels: 1 -:icons: font -:sectnums: -:sectnumlevels: 6 -:sectnumoffset: 2 -:partnums: -:source-highlighter: rouge -:experimental: - -ifdef::env-beastie[] -ifdef::backend-html5[] -:imagesdir: ../../../../images/{images-path} -endif::[] -ifndef::book[] -include::shared/authors.adoc[] -include::shared/mirrors.adoc[] -include::shared/releases.adoc[] -include::shared/attributes/attributes-{{% lang %}}.adoc[] -include::shared/{{% lang %}}/teams.adoc[] -include::shared/{{% lang %}}/mailing-lists.adoc[] -include::shared/{{% lang %}}/urls.adoc[] -toc::[] -endif::[] -ifdef::backend-pdf,backend-epub3[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] -endif::[] - -ifndef::env-beastie[] -toc::[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] - -_Эта глава поддерживается проектом FreeBSD SMP Next Generation Project. Комментарии и пожелания направляйте в link:{freebsd-smp}._ - -Этот документ описывает механизм блокировки, используемый в ядре FreeBSD для обеспечения эффективной поддержки нескольких процессоров в ядре. Блокировку можно рассматривать с нескольких точек зрения. Структуры данных могут быть защищены с помощью блокировок mutex или man:lockmgr[9]. Несколько переменных защищены просто в силу атомарности используемых для доступа к ним операций. - -[[locking-mutexes]] -== Мьютексы - -Мьютекс (mutex) - это просто блокировка, используемая для реализации гарантированной исключительности. В частности, в каждый момент времени мьютексом может владеть только один объект. Если какой-то объект хочет получить мьютекс, который уже кто-то занял, он должен дождаться момента его освобождения. В ядре FreeBSD владельцами мьютексов являются процессы. - -Мьютексы могут быть затребованы рекурсивно, но предполагается, что они занимаются на короткое время. В частности, владельцу мьютекса нельзя выдерживать паузу. Если вам нужно выполнить блокировку на время паузы, используйте блокировку через man:lockmgr[9]. - -Каждый мьютекс имеет несколько представляющих интерес характеристик: - -Имя переменной:: -Имя переменной [type]#struct mtx# в исходных текстах ядра. - -Логическое имя:: -Имя мьютекса, назначенное ему через `mtx_init`. Это имя выводится в сообщениях трассировки KTR и диагностических предупреждающих и ошибочных сообщениях и используется для идентификации мьютексов в отладочном коде. - -Тип:: -Тип мьютекса в терминах флагов [constant]#MTX_*#. Значение каждого флага связано с его смыслом так, как это описано в man:mutex[9]. - -[constant]#MTX_DEF#::: -Sleep-мьютекс - -[constant]#MTX_SPIN#::: -Spin-мьютекс - -[constant]#MTX_RECURSE#::: -Этому мьютексу разрешается блокировать рекурсивно. - -Защиты:: -Список структур данных или членов структур данных, которые защищает этот мьютекс. Для членов структур данных имя будет в форме . члена структуры/. - -Зависимые функции:: -Функции, которые можно вызвать, если этот мьютекс занят. - -.Список мьютексов -[cols="15%,10%,10%,55%,20%", frame="all", options="header"] -|=== -| Variable Name -| Logical Name -| Type -| Protectees -| Dependent Functions - -|sched_lock -|"sched lock" -|`MTX_SPIN` \| `MTX_RECURSE` -|`_gmonparam`, `cnt.v_swtch`, `cp_time`, `curpriority`, `mtx`.`mtx_blocked`, `mtx`.`mtx_contested`, `proc`.`p_procq`, `proc`.`p_slpq`, `proc`.`p_sflag`, `proc`.`p_stat`, `proc`.`p_estcpu`, `proc`.`p_cpticks` `proc`.`p_pctcpu`, `proc`.`p_wchan`, `proc`.`p_wmesg`, `proc`.`p_swtime`, `proc`.`p_slptime`, `proc`.`p_runtime`, `proc`.`p_uu`, `proc`.`p_su`, `proc`.`p_iu`, `proc`.`p_uticks`, `proc`.`p_sticks`, `proc`.`p_iticks`, `proc`.`p_oncpu`, `proc`.`p_lastcpu`, `proc`.`p_rqindex`, `proc`.`p_heldmtx`, `proc`.`p_blocked`, `proc`.`p_mtxname`, `proc`.`p_contested`, `proc`.`p_priority`, `proc`.`p_usrpri`, `proc`.`p_nativepri`, `proc`.`p_nice`, `proc`.`p_rtprio`, `pscnt`, `slpque`, `itqueuebits`, `itqueues`, `rtqueuebits`, `rtqueues`, `queuebits`, `queues`, `idqueuebits`, `idqueues`, `switchtime`, `switchticks` -|`setrunqueue`, `remrunqueue`, `mi_switch`, `chooseproc`, `schedclock`, `resetpriority`, `updatepri`, `maybe_resched`, `cpu_switch`, `cpu_throw`, `need_resched`, `resched_wanted`, `clear_resched`, `aston`, `astoff`, `astpending`, `calcru`, `proc_compare` - -|vm86pcb_lock -|"vm86pcb lock" -|`MTX_DEF` -|`vm86pcb` -|`vm86_bioscall` - -|Giant -|"Giant" -|`MTX_DEF` \| `MTX_RECURSE` -|nearly everything -|lots - -|callout_lock -|"callout lock" -|`MTX_SPIN` \| `MTX_RECURSE` -|`callfree`, `callwheel`, `nextsoftcheck`, `proc`.`p_itcallout`, `proc`.`p_slpcallout`, `softticks`, `ticks` -| -|=== - -[[locking-sx]] -== Разделяемые эксклюзивные блокировки - -Эти блокировки обеспечивают базовый тип функциональности - на чтение/запись и могут поддерживаться процессами, находящимся в состоянии ожидания. На текущий момент они реализованы в man:lockmgr[9]. - -.Список разделяемых эксклюзивных блокировок -[cols="1,1", options="header"] -|=== -| Имя переменной -| Защиты - -|`allproc_lock` -|`allproc` `zombproc` `pidhashtbl` `proc`.`p_list` `proc`.`p_hash` `nextpid` - -|`proctree_lock` -|`proc`.`p_children` `proc`.`p_sibling` -|=== - -[[locking-atomic]] -== Атомарно защищенные переменные - -Переменной, защищенной атомарно, является особая переменная, которая не защищается явной блокировкой. Вместо этого для доступа к данным переменных используются специальные атомарные операции, как описано в man:atomic[9]. Лишь несколько переменных используются таким образом, хотя другие примитивы синхронизации, такие как мьютексы, реализованы с атомарно защищенными переменными. - -* `mtx`.`mtx_lock` diff --git a/documentation/content/ru/books/arch-handbook/sound/chapter.adoc b/documentation/content/ru/books/arch-handbook/sound/chapter.adoc deleted file mode 100644 index 4bb8b82ea9..0000000000 --- a/documentation/content/ru/books/arch-handbook/sound/chapter.adoc +++ /dev/null @@ -1,432 +0,0 @@ ---- -title: Глава 15. Подсистема звука -authors: ---- - -[[oss]] -= Подсистема звука -:doctype: book -:toc: macro -:toclevels: 1 -:icons: font -:sectnums: -:sectnumlevels: 6 -:sectnumoffset: 15 -:partnums: -:source-highlighter: rouge -:experimental: - -ifdef::env-beastie[] -ifdef::backend-html5[] -:imagesdir: ../../../../images/{images-path} -endif::[] -ifndef::book[] -include::shared/authors.adoc[] -include::shared/mirrors.adoc[] -include::shared/releases.adoc[] -include::shared/attributes/attributes-{{% lang %}}.adoc[] -include::shared/{{% lang %}}/teams.adoc[] -include::shared/{{% lang %}}/mailing-lists.adoc[] -include::shared/{{% lang %}}/urls.adoc[] -toc::[] -endif::[] -ifdef::backend-pdf,backend-epub3[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] -endif::[] - -ifndef::env-beastie[] -toc::[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] - -[[oss-intro]] -== Введение - -Перевод на русский язык: Виталий Богданов (mailto:gad@gad.glazov.net[gad@gad.glazov.net]) - -В подсистеме звука FreeBSD существует чёткое разделение между частью, поддерживающей общие звуковые возможности и аппаратно зависимой частью. Данная особенность делает более простым добавление поддержки новых устройств. - -man:pcm[4] занимает центральное место в подсистеме звука. Его основными элементами являются: - -* Интерфейс системных вызовов (read, write, ioctls) к функциям оцифрованного звука и микшера. Командный набор ioctl совместим с интерфейсом _OSS_ или _Voxware_, позволяя тем самым портирование мультимедиа приложений без дополнительной модификации. -* Общий код обработки звуковых данных (преобразования форматов, виртуальные каналы). -* Единый программный интерфейс к аппаратно-зависимым модулям звукового интерфейса. -* Дополнительная поддержка нескольких общих аппаратных интерфейсов (ac97) или разделяемого аппаратно-специфичного кода (например: функции ISA DMA). - -Поддержка отдельных звуковых карт осуществляется с помощью аппаратно-специфичных драйверов, обеспечивающих канальные и микшерные интерфейсы, включаемые в общий код. - -В этой главе термином мы будем называть центральную, общую часть звукового драйвера, как противопоставление аппаратно-специфичным модулям. - -Человек, решающий написать драйвер наверняка захочет использовать в качестве шаблона уже существующий код. Но, если звуковой код хорош и чист, он также в основном лишён комментариев. Этот документ - попытка рассмотрения базового интерфейса и попытка ответить на вопросы, возникшие при адаптировании существующего кода. - -Для старта с рабочего примера, вы можете найти шаблон драйвера, оснащенного комментариями на http://people.FreeBSD.org/\~cg/template.c[ http://people.FreeBSD.org/~cg/template.c] - -[[oss-files]] -== Файлы - -Весь исходный код, на сегодняшний момент (FreeBSD 4.4), содержится в каталоге [.filename]#/usr/src/sys/dev/sound/#, за исключением публичных определений интерфейса ioctl, находящихся в [.filename]#/usr/src/sys/sys/soundcard.h# - -В подкаталоге [.filename]#pcm/# родительского каталога [.filename]#/usr/src/sys/dev/sound/# находится главный код, а в каталогах [.filename]#isa/# и [.filename]#pci/# содержатся драйвера для ISA и PCI карт. - -[[pcm-probe-and-attach]] -== Обнаружение, подключение, и т.д. - -Обнаружение и подключение звуковых драйверов во многом схоже с драйвером любого другого устройства. За дополнительной информацией вы можете обратиться к главам <<isa-driver, ISA>> или <<pci,PCI>> данного руководства. - -Но всё же, звуковые драйвера немного отличаются: - -* Они объявляют сами себя, как устройства класса , с частной структурой устройства : -+ -[.programlisting] -.... - static driver_t xxx_driver = { - "pcm", - xxx_methods, - sizeof(struct snddev_info) - }; - - DRIVER_MODULE(snd_xxxpci, pci, xxx_driver, pcm_devclass, 0, 0); - MODULE_DEPEND(snd_xxxpci, snd_pcm, PCM_MINVER, PCM_PREFVER,PCM_MAXVER); -.... -+ -Большинство звуковых драйверов нуждаются в сохранении личной информации, касающейся их устройства. Структура с личными данными обычно выделяется при вызове функции attach. Её адрес передаётся посредством вызовов `pcm_register()` и `mixer_init()`. Позже передаёт назад этот адрес, в качестве параметра в вызовах к интерфейсам звукового драйвера. -* Функция подключения звукового драйвера должна объявлять её микшерный или AC97 интерфейс посредством вызова `mixer_init()`. Для микшерного интерфейса это взамен вернёт вызов <<xxxmixer-init,`xxxmixer_init()`>>. -* Функция подключения звукового драйвера передаёт общие настройки каналов посредством вызова `pcm_register(dev, sc, nplay, nrec)`, где `sc` - адрес структуры данных устройства, используемой в дальнейших вызовах от , а `nplay` и `nrec` - количество каналов проигрывания и записи. -* Функция подключения звукового драйвера объявляет каждый из её каналов с помощью вызовов `pcm_addchan()`. Это установит занятость канала в и вызовет взамен вызов <<xxxchannel-init,`xxxchannel_init()`>>. -* Функция отключения должна вызывать `pcm_unregister()` перед объявлением её ресурсов свободными. - -Существует два метода работы с не PnP устройствами: - -* Использование метода `device_identify()` (пример смотрите в: [.filename]#sound/isa/es1888.c#). `device_identify()` пытается обнаружить оборудование, использующее известные адреса, и если найдёт поддерживаемое устройство, то создаст новое pcm устройство, которое затем будет передано процессу обнаружения/подключения. -* Использование выборочной конфигурации ядра с соответствующими хинтами для pcm устройств (пример: [.filename]#sound/isa/mss.c#). - -драйверы должны поддерживать `device_suspend`, `device_resume` и `device_shutdown` функции, для корректного функционирования управления питанием и процесса выгрузки модуля. - -[[oss-interfaces]] -== Интерфейсы - -Интерфейс между и звуковыми драйверами определён в терминах <<kernel-objects,объектов ядра>>. - -Есть 2 основных интерфейса, которые обычно обеспечивает звуковой драйвер: _канальный_ и, либо _микшерный_ либо _AC97_. - -Интерфейс _AC97_ довольно мало использует доступ к ресурсам оборудования (чтение/запись регистров). Данный интерфейс реализован в драйверах для карт с кодеком AC97. В этом случае фактический микшерный интерфейс обеспечивается разделяемым кодом AC97 в . - -=== Канальный интерфейс - -==== Общие заметки о параметрах функций - -Звуковые драйверы обычно имеют структуру с личными данными для описания их устройства и по одной структуре на каждый поддерживаемый канал проигрывания или записи данных. - -Для всех функций канального интерфейса первый параметр - непрозрачный указатель. - -Второй параметр это указатель на структуру с данными канала. Исключение: У ``channel_init()`` это указатель на частную структуру устройства (данная функция возвращает указатель на канал для дальнейшего использования в ). - -==== Обзор операций передачи данных - -Для передачи данных, и звуковые драйвера используют разделяемую область памяти, описанную в . - -принадлежит , и звуковые драйверы получают нужные значения с помощью вызовов функций (`sndbuf_getxxx()`). - -Область разделяемой памяти имеет размер, определяемый с помощью `sndbuf_getsize()` и разделён на блоки фиксированного размера, определённого в `sndbuf_getblksz()` количества байт. - -При проигрывании, общий механизм передачи данных примерно следующий (обратный механизму, используемому при записи): - -* В начале, заполняет буфер, затем вызывает функцию звукового драйвера <<channel-trigger,``xxxchannel_trigger()``>> с параметром PCMTRIG_START. -* Затем звуковой драйвер многократно передаёт всю область памяти (`sndbuf_getbuf()`, `sndbuf_getsize()`) устройству, с количеством байт, определённым в `sndbuf_getblksz()` . Взамен это вызовет `chn_intr()` функцию для каждого переданного блока (это обычно происходит во время прерывания). -* `chn_intr()` копирует новые данные в область, которая была передана устройству (сейчас свободная) и вносит соответствующие изменения в структуру . - -[[xxxchannel-init]] -=== channel_init - -`xxxchannel_init()` вызывается для инициализации каждого из каналов проигрывания или записи. Вызовы инициируются функцией подключения звукового драйвера. (Подробнее в главе <<pcm-probe-and-attach, Обнаружение и подключение>>). - -[.programlisting] -.... - static void * - xxxchannel_init(kobj_t obj, void *data, - struct snd_dbuf *b, struct pcm_channel *c, int dir) - { - struct xxx_info *sc = data; - struct xxx_chinfo *ch; - ... - return ch; - } - - b - это адрес канальной - struct snd_dbuf. Она должна - быть инициализирована в функции посредством - вызова sndbuf_alloc(). Нормальный - размер буфера для использования - наименьшее кратное - размера передаваемого блока данных для вашего устройства. - - c - это - указатель на структуру - контроля pcm канала. Это не прозрачный - объект. Функция должна хранить его в локальной структуре - канала, для дальнейшего использования в вызовах к - pcm (например в: - chn_intr(c)). - - dir определяет для каких целей - используется канал - (PCMDIR_PLAY или - PCMDIR_REC). - - Функция должна возвращать указатель на личную, - область, используемую для контроля этого - канала. Он будет передаваться в качестве параметра в - других вызовах канального интерфейса. - - channel_setformat - - xxxchannel_setformat() настраивает - устройство на конкретный канал определённого формата звука. - - static int - xxxchannel_setformat(kobj_t obj, void *data, u_int32_t format) - { - struct xxx_chinfo *ch = data; - ... - return 0; - } - - format используется, как - AFMT_XXX значение - (soundcard.h). - - channel_setspeed - - xxxchannel_setspeed() устанавливает - оборудование канала на определённую шаблонную скорость и возвращает - возможную корректирующую скорость. - - static int - xxxchannel_setspeed(kobj_t obj, void *data, u_int32_t speed) - { - struct xxx_chinfo *ch = data; - ... - return speed; - } - - channel_setblocksize - - xxxchannel_setblocksize() устанавливает - размер передаваемого блока между - pcm и звуковым драйвером, и между - звуковым драйвером и устройством. Обычно это будет количество - переданных байт перед прерыванием. Во время трансфера звуковой - драйвер должен должен вызывать - pcm функцию chn_intr() каждый - раз при передаче блока данных такого размера. - - Большинство звуковых драйверов только берут на заметку - размер блока для использования во время передачи данных. - - static int - xxxchannel_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) - { - struct xxx_chinfo *ch = data; - ... - return blocksize; - } - - Функция возвращает возможно согласованный размер - блока. В случае, если размер блока действительно - изменился должен быть произведён вызов - sndbuf_resize() для корректирования - буфера. - - channel_trigger - - xxxchannel_trigger() вызывается - pcm для контроля над трансферными - операциями в драйвере. - - static int - xxxchannel_trigger(kobj_t obj, void *data, int go) - { - struct xxx_chinfo *ch = data; - ... - return 0; - } - - go определяет действие для - текущего вызова. Возможные значения: - - PCMTRIG_START: драйвер - должен начать передачу данных из или в канальный - буфер. Буфер и его размер могут быть получены через - вызов sndbuf_getbuf() и - sndbuf_getsize(). - - PCMTRIG_EMLDMAWR / - PCMTRIG_EMLDMARD: говорит - драйверу, что входной или выходной буфер возможно - был обновлён. Большинство драйверов игнорируют - эти вызовы. - - PCMTRIG_STOP / - PCMTRIG_ABORT: драйвер должен - остановить текущую передачу данных. - - Если драйвер использует ISA DMA, - sndbuf_isadma() должна вызываться - перед выполнением действий над устройством, она также - позаботится о вещах со стороны DMA чипа. - - channel_getptr - - xxxchannel_getptr() возвращает - текущее смещение в передаваемом буфере. Обычно вызывается - в chn_intr(), и так - pcm узнаёт, где брать данные для - новой передачи. - - channel_free - - xxxchannel_free() вызывается для - освобождения ресурсов канала. Например: должна вызываться, - при выгрузке драйвера, если структуры данных канала - распределялись динамично или, если - sndbuf_alloc() не использовалась - для выделения памяти под буфер. - - channel_getcaps - - struct pcmchan_caps * - xxxchannel_getcaps(kobj_t obj, void *data) - { - return xxx_caps; - } - - Подпрограмма возвращает указатель на (обычно - статически-определяемую) структуру - pcmchan_caps (описанную в - sound/pcm/channel.h. Структура содержит - данные о минимуме и максимуме шаблонных частот и - воспринимаемых звуковых форматах. Для примера смотрите - исходный код любого звукового драйвера. - - Другие функции - - channel_reset(), - channel_resetdone(), и - channel_notify() предназначены для - специальных целей и не должны употребляться в драйвере - без обсуждения с авторами ({cg}). - - channel_setdir() is deprecated. - - Микшерный интерфейс - - mixer_init - - xxxmixer_init() инициализирует - оборудование и говорит pcm какие микшерные - устройства доступны для проигрывания и записи - - static int - xxxmixer_init(struct snd_mixer *m) - { - struct xxx_info *sc = mix_getdevinfo(m); - u_int32_t v; - - [Initialize hardware] - - [Set appropriate bits in v for play mixers] - mix_setdevs(m, v); - [Set appropriate bits in v for record mixers] - mix_setrecdevs(m, v) - - return 0; - } - - Устанавливает биты в целом значении и вызывает - mix_setdevs() и - mix_setrecdevs() чтобы сообщить - pcm какие устройства существуют. - - Определения битов микшера могут быть найдены в - soundcard.h - (SOUND_MASK_XXX значения и - SOUND_MIXER_XXX битовые сдвиги). - - mixer_set - - xxxmixer_set() устанавливает уровень - громкости для одного микшерного устройства. - - static int - xxxmixer_set(struct snd_mixer *m, unsigned dev, - unsigned left, unsigned right) - { - struct sc_info *sc = mix_getdevinfo(m); - [set volume level] - return left | (right 8); - } - - Устройство определяется, как SOUND_MIXER_XXX - значение Допустимые значения уровней громкости лежат - в пределах [0-100]. Равное нулю значение должно выключать звук - устройства. - - Вероятно уровни оборудования не будут совпадать с - входной шкалой, и будет происходить некоторое округление, подпрограмма - будет возвращает точные значения (в промежутке 0-100), как уже - было сказано. - - mixer_setrecsrc - - xxxmixer_setrecsrc() устанавливает - исходное записывающее устройство. - - static int - xxxmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) - { - struct xxx_info *sc = mix_getdevinfo(m); - - [look for non zero bit(s) in src, set up hardware] - - [update src to reflect actual action] - return src; - } - - Желаемые записывающие устройства указываются в битовом поле - - Возвращается фактический набор устройств для записи. - Некоторые драйверы могут устанавливать только одно устройство для - записи. Функция должна возвращать -1, в случае возникновения - ошибки. - - mixer_uninit, mixer_reinit - - xxxmixer_uninit() должна проверить, - что все звуки выключены (mute), и, если возможно выключить - оборудование микшера - - xxxmixer_reinit() должна удостовериться, - что оборудование микшера включено и все установки, неконтролируемые - mixer_set() или - mixer_setrecsrc() восстановлены. - - Интерфейс AC97 - - AC97 - - Поддержка интерфейса AC97 осуществляется - драйверами с кодеком AC97. Он поддерживает только три метода: - - xxxac97_init() возвращает - количество найденных ac97 кодеков. - - ac97_read() и - ac97_write() читают или записывают - данные определенного регистра. - - Интерфейс AC97 используется кодом - AC97 в pcm для выполнения операций - более высокого уровня. За примером обращайтесь к - sound/pci/maestro3.c или к другим - файлам из каталога sound/pci/. -.... diff --git a/documentation/content/ru/books/developers-handbook/introduction/chapter.adoc b/documentation/content/ru/books/developers-handbook/introduction/chapter.adoc deleted file mode 100644 index c9cab95af5..0000000000 --- a/documentation/content/ru/books/developers-handbook/introduction/chapter.adoc +++ /dev/null @@ -1,140 +0,0 @@ ---- -title: Глава 1. Введение -authors: - - author: Murray Stokely - - author: Jeroen Ruigrok van der Werven ---- - -[[introduction]] -= Введение -:doctype: book -:toc: macro -:toclevels: 1 -:icons: font -:sectnums: -:sectnumlevels: 6 -:sectnumoffset: 1 -:partnums: -:source-highlighter: rouge -:experimental: -:images-path: books/developers-handbook/ - -ifdef::env-beastie[] -ifdef::backend-html5[] -:imagesdir: ../../../../images/{images-path} -endif::[] -ifndef::book[] -include::shared/authors.adoc[] -include::shared/mirrors.adoc[] -include::shared/releases.adoc[] -include::shared/attributes/attributes-{{% lang %}}.adoc[] -include::shared/{{% lang %}}/teams.adoc[] -include::shared/{{% lang %}}/mailing-lists.adoc[] -include::shared/{{% lang %}}/urls.adoc[] -toc::[] -endif::[] -ifdef::backend-pdf,backend-epub3[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] -endif::[] - -ifndef::env-beastie[] -toc::[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] - -[[introduction-devel]] -== Разработка во FreeBSD - -Итак, у нас все есть. Система полностью установлена и вы готовы начать программировать. Но с чего начать? Что предоставляет FreeBSD? Что она может дать мне как программисту? - -Вот те из некоторых вопросов, на которые пытается дать ответ эта глава. Конечно, программирование, как и любая другая область деятельности, имеет разные уровни профессионального мастерства. Для некоторых это хобби, для других это профессия. Информация в этой главе может в большей степени пригодиться начинающему программисту, но может также оказаться полезной программисту, делающему первые шаги на платформе FreeBSD. - -[[introduction-bsdvision]] -== Подход BSD - -Создать самую лучшую UNIX(R)-подобную операционную систему, благодаря оригинальной идеологии программных средств, а также полезности, производительности и надёжности. - -[[introduction-archguide]] -== Архитектурные концепции - -Наша идеология может быть описана в следующих ключевых положениях - -* Не добавлять новой функциональности, кроме случаев, когда нельзя выполнить конкретную работу без нее. -* Решить, чего в системе не будет, так же важно, как и определение того, чего в системе не будет. Не пытайтесь включить в систему все; лучше сделать систему расширяемой так, что дополнительные потребности могут быть реализованы в режиме совместимости. -* Единственное, что может быть лучше обобщения на основе одного примера, это обобщение вообще без примеров. -* Если проблема до конца не понята, наверное, лучше вовсе не давать ее решения. -* Если вы можете сделать 90 процентов результата ценой 10 процентов работы, найдите более простое решение. -* Старайтесь отделять сложные вещи. -* Дайте механизм, а не правила. В частности, оставьте соглашения по пользовательскому интерфейсу клиенту. - -Из Scheifler Gettys: "X Window System" - -[[introduction-layout]] -== Структура каталога [.filename]#/usr/src# - -Полный исходный код FreeBSD располагается в нашем общедоступном хранилище CVS. Исходный код обычно устанавливается в [.filename]#/usr/src#, который содержит следующие подкаталоги: - -[.informaltable] -[cols="1,1", frame="none", options="header"] -|=== -| Каталог -| Описание - -|[.filename]#bin/# -|Исходный код файлов из [.filename]#/bin# - -|[.filename]#contrib/# -|Исходный код файлов программного обеспечения сторонних разработчиков. - -|[.filename]#crypto/# -|Исходный код криптографической подсистемы - -|[.filename]#etc/# -|Исходный код файлов из каталога [.filename]#/etc# - -|[.filename]#games/# -|Исходный код файлов из [.filename]#/usr/games# - -|[.filename]#gnu/# -|Утилиты, подпадающие под действие GNU Public License - -|[.filename]#include/# -|Исходный код файлов из [.filename]#/usr/include# - -|[.filename]#kerberosIV/# -|Исходный код Kerberos версии IV - -|[.filename]#kerberos5/# -|Исходный код Kerberos версии 5 - -|[.filename]#lib/# -|Исходный код файлов из [.filename]#/usr/lib# - -|[.filename]#libexec/# -|Исходный код файлов из [.filename]#/usr/libexec# - -|[.filename]#release/# -|Файлы, которые требуются для создания релиза FreeBSD - -|[.filename]#sbin/# -|Исходный код файлов из [.filename]#/sbin# - -|[.filename]#secure/# -|Исходный код FreeSec - -|[.filename]#share/# -|Исходный код файлов из [.filename]#/usr/share# - -|[.filename]#sys/# -|Исходный код ядра - -|[.filename]#tools/# -|Утилиты, используемые для поддержки и тестирования FreeBSD - -|[.filename]#usr.bin/# -|Исходный код файлов из [.filename]#/usr/bin# - -|[.filename]#usr.sbin/# -|Исходный код файлов из [.filename]#/usr/sbin# -|=== diff --git a/documentation/content/ru/books/developers-handbook/kerneldebug/chapter.adoc b/documentation/content/ru/books/developers-handbook/kerneldebug/chapter.adoc deleted file mode 100644 index 6e116cfec2..0000000000 --- a/documentation/content/ru/books/developers-handbook/kerneldebug/chapter.adoc +++ /dev/null @@ -1,678 +0,0 @@ ---- -title: Глава 10. Отладка ядра -authors: - - author: Paul Richards - - author: Jörg Wunsch - - author: Robert Watson ---- - -[[kerneldebug]] -= Отладка ядра -:doctype: book -:toc: macro -:toclevels: 1 -:icons: font -:sectnums: -:sectnumlevels: 6 -:sectnumoffset: 10 -:partnums: -:source-highlighter: rouge -:experimental: -:images-path: books/developers-handbook/ - -ifdef::env-beastie[] -ifdef::backend-html5[] -:imagesdir: ../../../../images/{images-path} -endif::[] -ifndef::book[] -include::shared/authors.adoc[] -include::shared/mirrors.adoc[] -include::shared/releases.adoc[] -include::shared/attributes/attributes-{{% lang %}}.adoc[] -include::shared/{{% lang %}}/teams.adoc[] -include::shared/{{% lang %}}/mailing-lists.adoc[] -include::shared/{{% lang %}}/urls.adoc[] -toc::[] -endif::[] -ifdef::backend-pdf,backend-epub3[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] -endif::[] - -ifndef::env-beastie[] -toc::[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] - -[[kerneldebug-obtain]] -== Получение аварийного дампа ядра - -При работе ядра, находящегося в разработке (например: FreeBSD-CURRENT), при критичных условиях (к примеру: очень высокая средняя нагрузка, десятки тысяч соединений, исключительно большое количество одновременно работающих пользователей, сотни процессов man:jail[8] и так далее) или при использовании новой возможности в драйвере устройства в FreeBSD-STABLE (пример: PAE) оно иногда будет завершать свою работу аварийно. В случае, если это произошло, в этой главе показано, как извлекать полезную информацию из произошедшего сбоя. - -При аварийном завершении работы ядра перезагрузка системы неизбежна. После перезагрузки системы содержимое физической памяти системы (RAM) теряется, так же как и всё содержимое раздела подкачки перед сбоем. Для сохранения состояния физической памяти ядро использует устройство подкачки в качестве места временного хранения содержимого оперативной памяти после перезагрузки, следующей за аварийным завершением работы. С этой целью при загрузке FreeBSD после аварийного останова образ ядра может быть извлечён и применяться для отладки. - -[NOTE] -==== -Устройство подкачки, которое было отконфигурировано в качестве устройства для дампа продолжает выступать в роли устройства подкачки. В настоящее время выполнение дампов на устройства, не предназначенные для организации подкачки (например, ленты или CDRW), не поддерживается. Понятие "устройство подкачки" является синонимом "раздела подкачки." -==== - -Чтобы суметь извлечь образ, который можно использовать, требуется, чтобы по крайней мере один раздел подкачки был достаточно большим, чтобы разместить на нём весь объём физической памяти. Когда ядро аварийно завершает работу, перед перезагрузкой системы, ядро достаточно умно, чтобы проверить, было ли отконфигурировано устройство подкачки в качестве устройства для хранения дампов. Если оно является устройством, подходящим для сброса дампа, то ядро сбрасывает содержимое физической памяти на устройство подкачки. - -[[config-dumpdev]] -=== Конфигурация устройства хранения дампов - -До того, как ядро начнёт сбрасывать содержимое физической памяти на устройство хранения дампов, последнее должно быть отконфигурировано. Устройство хранения дампов задаётся при помощи команды man:dumpon[8], указывающей ядру, куда сохранять аварийные дампы ядра. Программа man:dumpon[8] должна быть вызвана после конфигурации раздела подкачки по команде man:swapon[8]. Обычно это осуществляется установкой переменной `dumpdev` в файле man:rc.conf[5] в значение, соответствующее пути к устройству подкачки (рекомендованный способ извлечения дампа ядра). - -Либо устройство для сброса образа памяти может быть задано явно в параметре `dump` строки man:config[5] конфигурационного файла вашего ядра. Такой способ использовать не рекомендуется и он должен использоваться, только если ядро аварийно завершает свою работу до того, как можно было бы запустить man:dumpon[8]. - -[TIP] -==== - -Проверьте содержимое файла [.filename]#/etc/fstab# или выдачу man:swapinfo[8] на предмет наличия устройств подкачки. -==== - -[IMPORTANT] -==== -Удостоверьтесь, что каталог `dumpdir`, указанный в man:rc.conf[5], существует до аварийного останова ядра! - -[source,shell] -.... -# mkdir /var/crash -# chmod 700 /var/crash -.... - -Запомните также, что содержимое [.filename]#/var/crash# является важной информацией, весьма вероятно, содержащей конфиденциальную информацию, в частности, пароли. -==== - -[[extract-dump]] -=== Извлечение дампа ядра - -После того, как аварийный образ был записан на соответствующее устройство, его нужно извлечь до момента монтирования устройства подкачки. Для извлечения дампа из устройства его сохранения, воспользуйтесь утилитой man:savecore[8]. Если в файле man:rc.conf[5] было задано устройство `dumpdev`, то man:savecore[8] будет запущена автоматически при первой после аварийного останова загрузке в многопользовательском режиме и до монтирования устройства подкачки. Местоположение извлечённого образа памяти определяется значением переменной `dumpdir` из файла man:rc.conf[5], которое по умолчанию указывает на каталог [.filename]#/var/crash#, а файл будет называться [.filename]#vmcore.0#. - -В случае, если в каталоге [.filename]#/var/crash# (или в том, на который указывает `dumpdir`) уже существует файл с именем [.filename]#vmcore.0#, то ядро будет увеличивать порядковый номер для каждого аварийного останова, чтобы избежать перезаписи существующих файлов [.filename]#vmcore# (к примеру, [.filename]#vmcore.1#). В процессе отладки скорее всего, в качестве нужного [.filename]#vmcore# вы будете использовать версию [.filename]#vmcore# с наибольшим номером в [.filename]#/var/crash#. - -[TIP] -==== - -Если вы тестируете новое ядро, но вам нужно загрузить и работать с другим ядром, чтобы получить нормально функционирующую систему, то загрузите его в однопользовательском режиме при помощи флага `-s`, указываемого при загрузке, а затем выполните такие шаги: - -[source,shell] -.... -# fsck -p -# mount -a -t ufs -# доступность /var/crash для записи -# savecore /var/crash /dev/ad0s1b -# exit -.... - -Эти команды указывают man:savecore[8] извлечь дамп ядра из устройства [.filename]#/dev/ad0s1b# и поместить его содержимое в каталог [.filename]#/var/crash#. Не забудьте проверить, что в целевом каталоге [.filename]#/var/crash# достаточно места для хранения дампа. Кроме того, не забудьте проверить правильность маршрута к вашему устройству подкачки, так как он, скорее всего, отличается от [.filename]#/dev/ad0s1b#! -==== - -Рекомендуемым и определённо самым простым способом автоматизации формирования аварийных образов является указание переменной `dumpdev` в файле man:rc.conf[5]. - -[[kerneldebug-gdb]] -== Отладка аварийного образа памяти ядра при помощи `kgdb` - -[NOTE] -==== -В этом разделе описывается утилита man:kgdb[1], поставляемая с FreeBSD 5.3 и более поздними версиями. В предыдущих версиях для чтения файла аварийного дампа ядра необходимо использовать команду `gdb -k`. -==== - -После извлечения дампа памяти получение из него полезной информации для решения простых проблем является сравнительно лёгкой задачей. Перед тем, как погрузиться во внутренний интерфейс man:kgdb[1] для отладки аварийного образа памяти, найдите отладочную версию вашего ядра (обычно она имеет название [.filename]#kernel.debug#) и выясните маршрут к файлам исходных текстов, использованных для построения вашего ядра (обычно это [.filename]#/usr/obj/usr/src/sys/KERNCONF#), где в качестве _KERNCONF_ выступает значение `ident`, указанное конфигуратору ядра man:config[5]). Имея на руках эти два параметра, начнём отладку! - -Чтобы войти в отладчик и начать получать информацию из дампа, как минимум необходимо сделать следующие шаги: - -[source,shell] -.... -# cd /usr/obj/usr/src/sys/KERNCONF -# kgdb kernel.debug /var/crash/vmcore.0 -.... - -Вы можете отлаживать аварийный дамп, используя исходные тексты ядра точно также, как вы это делаете с любой другой программой. - -Этот первый дамп взят из ядра 5.2-BETA, а сбой произошёл где-то глубоко внутри ядра. Нижеследующая выдача была модифицирована, в неё слева добавлены номера строк. При первой трассировке проверяется указатель команд и выдаётся обратная трассировка. Адрес, используемый в строке 41 для команды `list`, является указателем команд и он может быть найден в строке 17. Большинство разработчиков будут требовать предоставления им по крайней мере этой информации, если вы не можете отследить проблему самостоятельно. Если, однако, вы решите проблему, то обязательно добейтесь включения вашего патча в дерево исходных текстов, прислав его через сообщение об ошибке, списки рассылки или даже его непосредственным коммитом! - -[source,shell] -.... -1:# cd /usr/obj/usr/src/sys/KERNCONF - 2:# kgdb kernel.debug /var/crash/vmcore.0 - 3:GNU gdb 5.2.1 (FreeBSD) - 4:Copyright 2002 Free Software Foundation, Inc. - 5:GDB is free software, covered by the GNU General Public License, and you are - 6:welcome to change it and/or distribute copies of it under certain conditions. - 7:Type "show copying" to see the conditions. - 8:There is absolutely no warranty for GDB. Type "show warranty" for details. - 9:This GDB was configured as "i386-undermydesk-freebsd"... -10:panic: page fault -11:panic messages: -12:--- -13:Fatal trap 12: page fault while in kernel mode -14:cpuid = 0; apic id = 00 -15:fault virtual address = 0x300 -16:fault code: = supervisor read, page not present -17:instruction pointer = 0x8:0xc0713860 -18:stack pointer = 0x10:0xdc1d0b70 -19:frame pointer = 0x10:0xdc1d0b7c -20:code segment = base 0x0, limit 0xfffff, type 0x1b -21: = DPL 0, pres 1, def32 1, gran 1 -22:processor eflags = resume, IOPL = 0 -23:current process = 14394 (uname) -24:trap number = 12 -25:panic: page fault -26 cpuid = 0; -27:Stack backtrace: -28 -29:syncing disks, buffers remaining... 2199 2199 panic: mi_switch: switch in a critical section -30:cpuid = 0; -31:Uptime: 2h43m19s -32:Dumping 255 MB -33: 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 -34:--- -35:Reading symbols from /boot/kernel/snd_maestro3.ko...done. -36:Loaded symbols for /boot/kernel/snd_maestro3.ko -37:Reading symbols from /boot/kernel/snd_pcm.ko...done. -38:Loaded symbols for /boot/kernel/snd_pcm.ko -39:#0 doadump () at /usr/src/sys/kern/kern_shutdown.c:240 -40:240 dumping++; -41:(kgdb) list *0xc0713860 -42:0xc0713860 is in lapic_ipi_wait (/usr/src/sys/i386/i386/local_apic.c:663). -43:658 incr = 0; -44:659 delay = 1; -45:660 } else -46:661 incr = 1; -47:662 for (x = 0; x < delay; x += incr) { -48:663 if ((lapic->icr_lo & APIC_DELSTAT_MASK) == APIC_DELSTAT_IDLE) -49:664 return (1); -50:665 ia32_pause(); -51:666 } -52:667 return (0); -53:(kgdb) backtrace -54:#0 doadump () at /usr/src/sys/kern/kern_shutdown.c:240 -55:#1 0xc055fd9b in boot (howto=260) at /usr/src/sys/kern/kern_shutdown.c:372 -56:#2 0xc056019d in panic () at /usr/src/sys/kern/kern_shutdown.c:550 -57:#3 0xc0567ef5 in mi_switch () at /usr/src/sys/kern/kern_synch.c:470 -58:#4 0xc055fa87 in boot (howto=256) at /usr/src/sys/kern/kern_shutdown.c:312 -59:#5 0xc056019d in panic () at /usr/src/sys/kern/kern_shutdown.c:550 -60:#6 0xc0720c66 in trap_fatal (frame=0xdc1d0b30, eva=0) -61: at /usr/src/sys/i386/i386/trap.c:821 -62:#7 0xc07202b3 in trap (frame= -63: {tf_fs = -1065484264, tf_es = -1065484272, tf_ds = -1065484272, tf_edi = 1, tf_esi = 0, tf_ebp = -602076292, tf_isp = -602076324, tf_ebx = 0, tf_edx = 0, tf_ecx = 1000000, tf_eax = 243, tf_trapno = 12, tf_err = 0, tf_eip = -1066321824, tf_cs = 8, tf_eflags = 65671, tf_esp = 243, tf_ss = 0}) -64: at /usr/src/sys/i386/i386/trap.c:250 -65:#8 0xc070c9f8 in calltrap () at {standard input}:94 -66:#9 0xc07139f3 in lapic_ipi_vectored (vector=0, dest=0) -67: at /usr/src/sys/i386/i386/local_apic.c:733 -68:#10 0xc0718b23 in ipi_selected (cpus=1, ipi=1) -69: at /usr/src/sys/i386/i386/mp_machdep.c:1115 -70:#11 0xc057473e in kseq_notify (ke=0xcc05e360, cpu=0) -71: at /usr/src/sys/kern/sched_ule.c:520 -72:#12 0xc0575cad in sched_add (td=0xcbcf5c80) -73: at /usr/src/sys/kern/sched_ule.c:1366 -74:#13 0xc05666c6 in setrunqueue (td=0xcc05e360) -75: at /usr/src/sys/kern/kern_switch.c:422 -76:#14 0xc05752f4 in sched_wakeup (td=0xcbcf5c80) -77: at /usr/src/sys/kern/sched_ule.c:999 -78:#15 0xc056816c in setrunnable (td=0xcbcf5c80) -79: at /usr/src/sys/kern/kern_synch.c:570 -80:#16 0xc0567d53 in wakeup (ident=0xcbcf5c80) -81: at /usr/src/sys/kern/kern_synch.c:411 -82:#17 0xc05490a8 in exit1 (td=0xcbcf5b40, rv=0) -83: at /usr/src/sys/kern/kern_exit.c:509 -84:#18 0xc0548011 in sys_exit () at /usr/src/sys/kern/kern_exit.c:102 -85:#19 0xc0720fd0 in syscall (frame= -86: {tf_fs = 47, tf_es = 47, tf_ds = 47, tf_edi = 0, tf_esi = -1, tf_ebp = -1077940712, tf_isp = -602075788, tf_ebx = 672411944, tf_edx = 10, tf_ecx = 672411600, tf_eax = 1, tf_trapno = 12, tf_err = 2, tf_eip = 671899563, tf_cs = 31, tf_eflags = 642, tf_esp = -1077940740, tf_ss = 47}) -87: at /usr/src/sys/i386/i386/trap.c:1010 -88:#20 0xc070ca4d in Xint0x80_syscall () at {standard input}:136 -89:---Can't read userspace from dump, or kernel process--- -90:(kgdb) quit -.... - -Во второй трассировке используется более старый дамп из времён FreeBSD 2, но он более сложный и показывает больше возможностей `gdb`. Длинные строки были усечены ради повышения читабельности, а также пронумерованы для того, чтобы ссылаться на них. Кроме этих отличий, это реальная трассировка ошибки, выполненная в процессе разработки консольного драйвера pcvt. - -[source,shell] -.... -1:Script started on Fri Dec 30 23:15:22 1994 - 2:# cd /sys/compile/URIAH - 3:# gdb -k kernel /var/crash/vmcore.1 - 4:Reading symbol data from /usr/src/sys/compile/URIAH/kernel -...done. - 5:IdlePTD 1f3000 - 6:panic: because you said to! - 7:current pcb at 1e3f70 - 8:Reading in symbols for ../../i386/i386/machdep.c...done. - 9:(kgdb) backtrace -10:#0 boot (arghowto=256) (../../i386/i386/machdep.c line 767) -11:#1 0xf0115159 in panic () -12:#2 0xf01955bd in diediedie () (../../i386/i386/machdep.c line 698) -13:#3 0xf010185e in db_fncall () -14:#4 0xf0101586 in db_command (-266509132, -266509516, -267381073) -15:#5 0xf0101711 in db_command_loop () -16:#6 0xf01040a0 in db_trap () -17:#7 0xf0192976 in kdb_trap (12, 0, -272630436, -266743723) -18:#8 0xf019d2eb in trap_fatal (...) -19:#9 0xf019ce60 in trap_pfault (...) -20:#10 0xf019cb2f in trap (...) -21:#11 0xf01932a1 in exception:calltrap () -22:#12 0xf0191503 in cnopen (...) -23:#13 0xf0132c34 in spec_open () -24:#14 0xf012d014 in vn_open () -25:#15 0xf012a183 in open () -26:#16 0xf019d4eb in syscall (...) -27:(kgdb) up 10 -28:Reading in symbols for ../../i386/i386/trap.c...done. -29:#10 0xf019cb2f in trap (frame={tf_es = -260440048, tf_ds = 16, tf_\ -30:edi = 3072, tf_esi = -266445372, tf_ebp = -272630356, tf_isp = -27\ -31:2630396, tf_ebx = -266427884, tf_edx = 12, tf_ecx = -266427884, tf\ -32:_eax = 64772224, tf_trapno = 12, tf_err = -272695296, tf_eip = -26\ -33:6672343, tf_cs = -266469368, tf_eflags = 66066, tf_esp = 3072, tf_\ -34:ss = -266427884}) (../../i386/i386/trap.c line 283) -35:283 (void) trap_pfault(&frame, FALSE); -36:(kgdb) frame frame-<tf_ebp frame-<tf_eip -37:Reading in symbols for ../../i386/isa/pcvt/pcvt_drv.c...done. -38:#0 0xf01ae729 in pcopen (dev=3072, flag=3, mode=8192, p=(struct p\ -39:roc *) 0xf07c0c00) (../../i386/isa/pcvt/pcvt_drv.c line 403) -40:403 return ((*linesw[tp-<t_line].l_open)(dev, tp)); -41:(kgdb) list -42:398 -43:399 tp-<t_state |= TS_CARR_ON; -44:400 tp-<t_cflag |= CLOCAL; /* cannot be a modem (:-) */ -45:401 -46:402 #if PCVT_NETBSD || (PCVT_FREEBSD >= 200) -47:403 return ((*linesw[tp-<t_line].l_open)(dev, tp)); -48:404 #else -49:405 return ((*linesw[tp-<t_line].l_open)(dev, tp, flag)); -50:406 #endif /* PCVT_NETBSD || (PCVT_FREEBSD >= 200) */ -51:407 } -52:(kgdb) print tp -53:Reading in symbols for ../../i386/i386/cons.c...done. -54:$1 = (struct tty *) 0x1bae -55:(kgdb) print tp-<t_line -56:$2 = 1767990816 -57:(kgdb) up -58:#1 0xf0191503 in cnopen (dev=0x00000000, flag=3, mode=8192, p=(st\ -59:ruct proc *) 0xf07c0c00) (../../i386/i386/cons.c line 126) -60: return ((*cdevsw[major(dev)].d_open)(dev, flag, mode, p)); -61:(kgdb) up -62:#2 0xf0132c34 in spec_open () -63:(kgdb) up -64:#3 0xf012d014 in vn_open () -65:(kgdb) up -66:#4 0xf012a183 in open () -67:(kgdb) up -68:#5 0xf019d4eb in syscall (frame={tf_es = 39, tf_ds = 39, tf_edi =\ -69: 2158592, tf_esi = 0, tf_ebp = -272638436, tf_isp = -272629788, tf\ -70:_ebx = 7086, tf_edx = 1, tf_ecx = 0, tf_eax = 5, tf_trapno = 582, \ -71:tf_err = 582, tf_eip = 75749, tf_cs = 31, tf_eflags = 582, tf_esp \ -72:= -272638456, tf_ss = 39}) (../../i386/i386/trap.c line 673) -73:673 error = (*callp-<sy_call)(p, args, rval); -74:(kgdb) up -75:Initial frame selected; you cannot go up. -76:(kgdb) quit -.... - -Комментарии к вышеприведенному журналу: - -строка 6::: -Это дамп, взятый при помощи DDB (смотри ниже), поэтому комментарий к аварийному останову имеет именно вид "because you said to!" и трассировка стека глубока; однако изначальной причиной перехода в DDB была аварийная остановка при возникновению ошибки страницы памяти. - -строка 20::: -Это местонахождение функции `trap()` в трассировке стека. - -строка 36::: -Принудительное использование новой границы стека; теперь это не нужно. Предполагается, что границы стека указывают на правильное расположение, даже в случае аварийного останова. Глядя на строку исходного кода 403, можно сказать, что весьма вероятно, что либо виноват доступ по указателю "tp", либо был выход за границы массива. - -строка 52::: -Похоже, что виноват указатель, но он является допустимым адресом. - -строка 56::: -Однако, очевидно, что он указывает на мусор, так что мы нашли нашу ошибку! (Для тех, кто не знаком с этой частью кода: `tp->t_line` служит для хранения режима канала консольного устройства, и это должно быть достаточно маленькое целое число.) - -[TIP] -==== - -Если в вашей системе регулярно происходят аварийные остановы, и вам не хватает места на диске, удаление старых файлов [.filename]#vmcore# в каталоге [.filename]#/var/crash# может сэкономить вам значительный объём дискового пространства! -==== - -[[kerneldebug-ddd]] -== Отладка аварийного дампа с помощью DDD - -Возможно также и исследование аварийного дампа ядра при помощи такого графического отладчика, как `ddd` (вам потребуется установить порт [.filename]#devel/ddd#, чтобы использовать отладчик `ddd`). Добавьте флаг `-k` к командной строке `ddd`, которую вы обычно используете для его вызова. Например; - -[source,shell] -.... -# ddd -k /var/crash/kernel.0 /var/crash/vmcore.0 -.... - -После этого у вас должно получиться исследование аварийного дампа при помощи графического интерфейса `ddd`. - -[[kerneldebug-post-mortem]] -== Посмертный анализ дампа - -Что делать, если ядро аварийно завершает работу, хотя этого вы не хотели и поэтому командой `config -g` его не компилировали? Здесь не всё ещё потеряно. Не паникуйте! - -Конечно, вам нужно включить создание аварийных дампов. Смотрите выше, что вы должны для этого сделать. - -Перейдите в каталог конфигурации ядра ([.filename]#/usr/src/sys/arch/conf#) и отредактируйте ваш конфигурационный файл. Раскомментируйте (или добавьте, если она не существует) такую строку: - -[.programlisting] -.... -makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols -.... - -Перестройте ядро. Из-за изменения метки времени в Makefile будут перестроены и некоторые другие объектные файлы, например, [.filename]#trap.o#. К некоторому счастью, добавление опции `-g` не изменит все и вся в генерируемом коде, так что в конце концов вы получите новое ядро с тем же кодом, что и сбоящее ядро, но с отладочной информацией. По крайней мере, вы можете сравнить старый и новый размеры ядер командой man:size[1]. Если они не совпадают, то вам придется отказаться от вашей затеи. - -Исследуйте дамп так, как это описано выше. Отладочной информации может не хватать в некоторых местах, как это можно видеть в трассировке стека примера выше, когда некоторые функции выводятся без номеров строк и списка аргументов. Если вам нужно больше отладочной информации, удалите соответствующие объектные файлы, снова перекомпилируйте ядро и повторите сеанс работы `gdb -k`, пока не получите достаточно подробную информацию. - -Не гарантируется, что всё это будет работать, однако в большинстве случаев всё работает прекрасно. - -[[kerneldebug-online-ddb]] -== Отладка ядра в режиме реального времени с помощью DDB - -Хотя `gdb -k` является отладчиком не реального времени с высокоуровневым пользовательским интерфейсом, есть несколько вещей, которые он сделать не сможет. Самыми важными из них являются точки останова и пошаговое выполнение кода ядра. - -Если вам нужно выполнять низкоуровневую отладку вашего ядра, то на этот случай имеется отладчик реального времени, который называется DDB. Он позволяет устанавливать точки останова, выполнять функции ядра по шагам, исследовать и изменять переменные ядра и прочее. Однако он не может использовать исходные тексты ядра и имеет доступ только к глобальным и статическим символам, а не ко всей отладочной информации, как в `gdb`. - -Чтобы отконфигурировать ваше ядро для включения DDB, добавьте строчку с параметром - -[.programlisting] -.... -options DDB -.... - -в ваш конфигурационный файл, и перестройте ядро. (Обратитесь к extref:{handbook}[Руководству по FreeBSD] для выяснения подробностей о конфигурации ядра FreeBSD). - -[NOTE] -==== -Если у вас устаревшая версия загрузочных блоков, то отладочная информация может оказаться не загруженной. Обновите блоки загрузки; самые новые загружают символы для DDB автоматически. -==== - -После того, как ядро с DDB запущено, есть несколько способов войти в DDB. Первый, и самый простой, способ заключается в наборе флага загрузки `-d` прямо в приглашении загрузчика. Ядро будет запущено в режиме отладки и войдет в DDB до выполнения процедуры распознавания каких бы то ни было устройств. Поэтому вы можете выполнить отладку даже функций распознавания/присоединения устройств. - -Вторым способом является переход в режим отладчика сразу после загрузки системы. Есть два простых способа этого добиться. Если вы хотите перейти в отладчик из командной строки, просто наберите команду: - -[source,shell] -.... -# sysctl debug.enter_debugger=ddb -.... - -Либо, если вы работаете за системной консолью, можете воспользоваться определенной комбинацией клавиш. По умолчанию для перехода в отладчик используется комбинация kbd:[Ctrl+Alt+ESC]. Для драйвера syscons эта последовательность может быть изменена, и в некоторых распространяемых раскладках это сделано, так что обязательно выясните правильную комбинацию. Для последовательных консолей имеется параметр, позволяющий использовать последовательность BREAK на канале консоли для входа в DDB (`options BREAK_TO_DEBUGGER` в конфигурационном файле ядра). По умолчанию этого не делается, так как существует множество последовательных адаптеров, которые ошибочно генерируют последовательность BREAK, к примеру, при отключении кабеля. - -Третий способ заключается во входе в DDB при возникновении любой аварийной ситуации, если ядро его использует. По этой причине не очень умно конфигурировать ядро с DDB для машины, которая работает без присмотра. - -Команды DDB примерно повторяют некоторые команды `gdb`. Первым делом вам, наверное, нужно задать точку останова: - -[source,shell] -.... - b function-name - b address -.... - -Значения по умолчанию воспринимаются в шестнадцатеричном виде, но чтобы отличать их от имен символов; шестнадцатеричные числа, начинающиеся с букв `a-f`, должны предваряться символами `0x` (это опционально для других чисел). Разрешены простые выражения, например: `function-name + 0x103`. - -Чтобы продолжить работу прерванного ядра, просто наберите: - -[source,shell] -.... - c -.... - -Чтобы получить трассировку стека, задайте: - -[source,shell] -.... - trace -.... - -[NOTE] -==== -Заметьте, что при входе в DDB по специальной комбинации, ядро в данный момент обслуживает прерывание, так что трассировка стека может не дать вам много информации. -==== - -Если вы хотите убрать точку останова, введите - -[source,shell] -.... - del - del address-expression -.... - -В первом варианте команда будет исполнена сразу же по достижении точки останова, а текущая точка останова будет удалена. Во второй форме можно удалить любую точку останова, однако вам нужно будет указать ее точный адрес; его можно получить из: - -[source,shell] -.... - show b -.... - -Чтобы выполнить один шаг ядра, попробуйте: - -[source,shell] -.... - s -.... - -При этом будет осуществляться пошаговое выполнение функций, однако вы можете трассировать их с помощью DDB, пока не будет достигнуто соответствие возвращаемому значению: - -[source,shell] -.... - n -.... - -[NOTE] -==== -Это отличается от команды `next` отладчика `gdb`; это похоже на команду `gdb finish`. -==== - -Чтобы выводить значения в памяти, используйте, (к примеру): - -[source,shell] -.... - x/wx 0xf0133fe0,40 - x/hd db_symtab_space - x/bc termbuf,10 - x/s stringbuf -.... - -для доступа к данным типа слово/полуслово/байт и вывода в шестнадцатеричном/десятичном/символьном виде. Число после запятой означает счетчик объектов. Чтобы вывести следующие 0x10 объектов, просто укажите: - -[source,shell] -.... - x ,10 -.... - -Подобным же образом используйте - -[source,shell] -.... - x/ia foofunc,10 -.... - -для дизассемблирования и вывода первых 0x10 инструкций функции `foofunc` вместе с их адресом относительно начала `foofunc`. - -Чтобы изменить значения в памяти, используйте команду write: - -[source,shell] -.... - w/b termbuf 0xa 0xb 0 - w/w 0xf0010030 0 0 -.... - -Модификатор команды (`b`/`h`/`w`) указывает на размер записываемых данных, первое следующее за ним выражение является адресом для записи, а оставшаяся часть интерпретируется как данные для записи в доступные области памяти. - -Если вам нужно узнать текущее содержимое регистров, используйте: - -[source,shell] -.... - show reg -.... - -Альтернативно вы можете вывести содержимое одного регистра по команде, скажем, - -[source,shell] -.... - p $eax -.... - -и изменить его по: - -[source,shell] -.... - set $eax new-value -.... - -Если вам нужно вызвать некоторую функцию ядра из DDB, просто укажите: - -[source,shell] -.... - call func(arg1, arg2, ...) -.... - -Будет выведено возвращаемое значение. - -Для вывода суммарной статистики по всем работающим процессам в стиле команды man:ps[1] воспользуйтесь такой командой: - -[source,shell] -.... - ps -.... - -Теперь вы узнали, почему ядро работает с ошибками и хотите выполнить перезагрузку. Запомните, что в зависимости от влияния предыдущих ошибок, не все части ядра могут работать так, как ожидается. Выполните одно из следующих действий для закрытия и перезагрузки вашей системы: - -[source,shell] -.... - panic -.... - -Это приведет к созданию дампа ядра и перезагрузке, так что позже вы можете проанализировать дамп на более высоком уровне при помощи `gdb`. Как правило, эта команда должна следовать за другой командой `continue`. - -[source,shell] -.... - call boot(0) -.... - -Это может оказаться хорошим способом для корректного закрытия работающей системы, `sync()` для всех дисков и напоследок перезагрузка. Пока интерфейсы диска и файловой системы в ядре не повреждены, это может быть самым правильным способом закрытия системы. - -[source,shell] -.... - call cpu_reset() -.... - -Это последнее средство при аварии и практически то же самое, что нажатие Большой Красной Кнопки. - -Если вам нужен краткий справочник по командам, просто наберите: - -[source,shell] -.... - help -.... - -Однако настоятельно рекомендуем отпечатать копию страницы справочника по man:ddb[4] при подготовке к сеансу отладки. Помните, что трудно читать онлайновое руководство при пошаговом выполнении ядра. - -[[kerneldebug-online-gdb]] -== Отладка ядра в режиме реального времени при помощи удалённого GDB - -Эта возможность поддерживается во FreeBSD начиная с версии 2.2, и она на самом деле очень удобна. - -В GDB уже давно имеется поддержка _удаленной отладки_. Это делается при помощи весьма простого протокола по последовательному каналу. В отличие от других методов, описанных выше, для этого вам требуется наличие двух машин. Одна из них является хостом, предоставляющим ресурсы для отладки, включая все исходные тексты и копию ядра со всеми символами в нем, а другая является целевой машиной, на которой запущена та же копия того же ядра (но без отладочной информации). - -Вы должны настроить исследуемое ядро при помощи команды `config -g`, включить `DDB` в конфигурацию и откомпилировать его обычным образом. Это даст большой бинарный файл из-за отладочной информации. Скопируйте это ядро на целевую машину, усеките отладочную информацию командой `strip -x` и загрузите это ядро с использованием параметра загрузки `-d`. Подключите последовательный канал целевой машины, имеющий установленные флаги "flags 080" на соответствующем устройстве sio к любому последовательному каналу отладочного хоста. А теперь на отладочной машине перейдите в каталог компиляции целевого ядра и запустите `gdb`: - -[source,shell] -.... -% gdb -k kernel -GDB is free software and you are welcome to distribute copies of it - under certain conditions; type "show copying" to see the conditions. -There is absolutely no warranty for GDB; type "show warranty" for details. -GDB 4.16 (i386-unknown-freebsd), -Copyright 1996 Free Software Foundation, Inc... -(kgdb) -.... - -Проинициализируйте сеанс удаленной отладки (предполагается, что используется первый последовательный порт) такой командой: - -[source,shell] -.... -(kgdb) target remote /dev/cuaa0 -.... - -Теперь на целевом хосте (тот, который перешел в DDB даже до начала процесса обнаружения устройств) наберите: - -[source,shell] -.... -Debugger("Boot flags requested debugger") -Stopped at Debugger+0x35: movb $0, edata+0x51bc - -db> gdb -.... - -DDB ответит следующим: - -[source,shell] -.... -Next trap will enter GDB remote protocol mode -.... - -Каждый раз, когда вы будете набирать `gdb`, режим будет меняться между удаленным GDB и локальным DDB. Чтобы немедленно вызвать следующее прерывание, просто наберите `s` (step). Ваш хостирующий GDB получит управление над целевым ядром: - -[source,shell] -.... -Remote debugging using /dev/cuaa0 -Debugger (msg=0xf01b0383 "Boot flags requested debugger") - at ../../i386/i386/db_interface.c:257 -(kgdb) -.... - -Вы можете работать в этом сеансе точно также, как и в любом другом сеансе GDB, включая полный доступ к исходным текстам, запуск его в режиме gud-mode внутри окна Emacs (что даёт вам автоматический вывод исходного кода в другом окне Emacs) и тому подобное. - -[[kerneldebug-kld]] -== Отладка загружаемых модулей с помощью GDB - -При отладке аварийного останова системы, которое произошло в модуле, или при использовании GDB в режиме удаленного доступа к машине, использующей динамические модули, вам нужно указать GDB, как получить информацию о символах в этих модулях. - -Первым делом вам нужно построить модуль (или модули) с включением отладочной информации: - -[source,shell] -.... -# cd /sys/modules/linux -# make clean; make COPTS=-g -.... - -Если вы используете GDB в режиме удаленного доступа, то для определения того, куда был загружен модуль, можете запустить команду `kldstat` на целевой машине: - -[source,shell] -.... -# kldstat -Id Refs Address Size Name - 1 4 0xc0100000 1c1678 kernel - 2 1 0xc0a9e000 6000 linprocfs.ko - 3 1 0xc0ad7000 2000 warp_saver.ko - 4 1 0xc0adc000 11000 linux.ko -.... - -Если вы отлаживаете аварийный дамп, вам потребуется просмотреть список `linker_files` начиная с `linker_files->tqh_first` и следовать указателям `link.tqe_next` до тех пор, пока не найдете запись с тем `filename`, который вы ищете. Элемент `address` этой записи является адресом загрузки модуля. - -Затем вам нужно определить смещение текстового сегмента модуля: - -[source,shell] -.... -# objdump --section-headers /sys/modules/linux/linux.ko | grep text -3 .rel.text 000016e0 000038e0 000038e0 000038e0 2**2 - 10 .text 00007f34 000062d0 000062d0 000062d0 2**2 -.... - -То, что вы ищете, является секцией `.text`, в примере выше это секция 10. Четвертое числовое поле (всего шестое по счёту) является смещением текстовой секции внутри файла. Добавьте это смещение к адресу загрузки, чтобы получить адрес, на который был перемещён код модуля. В нашем примере мы получим 0xc0adc000 + 0x62d0 = c0ae22d0. Воспользуйтесь командой `add-symbol-file` в GDB для указания отладчику на модуль: - -[source,shell] -.... -(kgdb) add-symbol-file /sys/modules/linux/linux.ko 0xc0ae22d0 -add symbol table from file "/sys/modules/linux/linux.ko" at text_addr = 0xc0ae22d0? -(y or n) -(kgdb) y -Reading symbols from /sys/modules/linux/linux.ko...done. -(kgdb) -.... - -Теперь вы должны получить доступ ко всем символам в модуле. - -[[kerneldebug-console]] -== Отладка драйвера консоли - -Так как для работы DDB вам требуется драйвер консоли, то в случае неисправностей самого драйвера консоли все становится гораздо сложнее. Вы можете вспомнить об использовании последовательной консоли (либо с исправленными загрузочными блоками, либо при указании флага `-h` в приглашении `Boot:`) и подключить обычный терминал к первому последовательному порту. DDB работает с любым отконфигурированным драйвером консоли, в том числе с последовательной консолью. diff --git a/documentation/content/ru/books/developers-handbook/policies/chapter.adoc b/documentation/content/ru/books/developers-handbook/policies/chapter.adoc deleted file mode 100644 index 970a4b9921..0000000000 --- a/documentation/content/ru/books/developers-handbook/policies/chapter.adoc +++ /dev/null @@ -1,196 +0,0 @@ ---- -title: Глава 5. Рекомендации и требования к исходному коду -authors: - - author: Poul-Henning Kamp - - author: Giorgos Keramidas ---- - -[[policies]] -= Рекомендации и требования к исходному коду -:doctype: book -:toc: macro -:toclevels: 1 -:icons: font -:sectnums: -:sectnumlevels: 6 -:sectnumoffset: 5 -:partnums: -:source-highlighter: rouge -:experimental: -:images-path: books/developers-handbook/ - -ifdef::env-beastie[] -ifdef::backend-html5[] -:imagesdir: ../../../../images/{images-path} -endif::[] -ifndef::book[] -include::shared/authors.adoc[] -include::shared/mirrors.adoc[] -include::shared/releases.adoc[] -include::shared/attributes/attributes-{{% lang %}}.adoc[] -include::shared/{{% lang %}}/teams.adoc[] -include::shared/{{% lang %}}/mailing-lists.adoc[] -include::shared/{{% lang %}}/urls.adoc[] -toc::[] -endif::[] -ifdef::backend-pdf,backend-epub3[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] -endif::[] - -ifndef::env-beastie[] -toc::[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] - -В этой главе описываются различные рекомендации и требования, которые должны соблюдаться в дереве исходных текстов FreeBSD. - -[[policies-maintainer]] -== в make-файлах - -Если некоторая часть дистрибутива FreeBSD поддерживается некоторым человеком или группой людей, они могут сообщить об этом миру, добавив строчку - -[.programlisting] -.... -MAINTAINER= email-addresses -.... - -в файл [.filename]#Makefile#, соответствующий этой части исходного кода. - -Смысл этого в следующем: - -Сопровождающий владеет кодом и отвечает за него. Это означает, что он несет ответственность за исправление ошибок и закрывает сообщения о проблемах, имеющих отношение к этой части кода, а в случае программного обеспечения, взятого из третьих источников, соответственно отвечает за отслеживание новых версий. - -Изменения в каталогах, для которых известен сопровождающий, прежде чем они будут внесены, должны быть посланы ему на рассмотрение. Только если сопровождающий не отвечает в течение достаточно большого периода времени на несколько посланий по электронной почте, разрешается внести изменения без участия сопровождающего. Однако рекомендуется, чтобы вы попытались передать изменения на рассмотрение кому-либо еще, если это вообще возможно. - -Конечно же, нельзя назначать человека или группу лиц сопровождающими, если они не согласны выполнять эту работу. С другой стороны, необязательно это должен быть конкретный коммиттер, это может быть и группа людей. - -[[policies-contributed]] -== Программное обеспечение сторонних производителей - -Некоторые части дистрибутива FreeBSD состоят из программного обеспечения, которое сопровождается вне проекта FreeBSD. По историческим причинам мы называем такое программное обеспечение _контрибуцированным_ (contributed), или третьих сторон. Примерами этого могут служить утилиты sendmail, gcc и patch. - -За последние несколько лет для работы с таким программным обеспечением использовались различные методы, и все они имели свои достоинства и недостатки. Абсолютно подходящего метода так и не нашлось. - -По этой причине после некоторых дебатов был выбран и признан "официальным" один из этих методов, который необходимо применять в будущем при импортировании такого рода программного обеспечения. Более того, настоятельно рекомендуется с течением времени перевести существующее программное обеспечение третьих сторон на этот метод, так как он имеет значительные преимущества перед старым методом, включая возможность легкого получения diff-файлов относительно "официальных" версий исходных текстов кем угодно (даже не имеющим доступа к cvs). Это делает данный метод гораздо проще в использовании при необходимости выдачи изменений изначальным разработчикам такого программного обеспечения. - -В конце концов, однако, это касается тех, кто делает реальную работу. Если использование этой модели в конкретном случае не подходит для пакета, с которым работает человек, могут быть сделаны и исключения только с согласия основной команды разработчиков и при общем одобрении других разработчиков. Возможность сопровождения пакета в будущем будет являться ключевым моментом при принятии решений. - -[NOTE] -==== -Из-за досадных ограничений в дизайне формата файлов RCS и использовании веток поставщика в CVS, мелкие, тривиальные и/или косметические изменения _сильно не рекомендуется_ в файлах, которые все еще отслеживаются в ветке поставщика. Это касается и "исправления орфографических ошибок" как относящихся к категории "косметических" и избегаемых для файлов с версиями 1.1.x.x. Рост объема хранилища, вызванный изменением в один символ, может оказаться весьма большим. -==== - -В качестве примера того, как работает эта модель, будем использовать встраиваемый язык программирования TCL: - -Каталог [.filename]#src/contrib/tcl# содержит исходные тексты пакета в том виде, в котором они распространяются его создателями. Части, которые полностью не применимы во FreeBSD, могут быть удалены. В случае Tcl подкаталоги [.filename]#mac#, [.filename]#win# и [.filename]#compat# были удалены перед операцией импортирования - -Каталог [.filename]#src/lib/libtcl# содержит только файл [.filename]#Makefile# в стиле bmake, который использует стандартные правила [.filename]#bsd.lib.mk# make-файла для построения библиотеки и установки документации. - -В каталоге [.filename]#src/usr.bin/tclsh# размещаются make-файлы в стиле bmake, которые отвечают за построение и установку программы `tclsh` и связанных с ней справочных страниц при помощи стандартных правил из [.filename]#bsd.prog.mk#. - -Каталог [.filename]#src/tools/tools/tcl_bmake# содержит несколько shell-скриптов, которые могут помочь при обновлении программного обеспечения tcl. Они не являются частью строящегося и инсталлируемого программного обеспечения. - -Здесь важно то, что каталог [.filename]#src/contrib/tcl# создавался в соответствии с правилами: Предполагается, что он содержит исходные тексты в том виде, в котором они распространяются (в соответствующей ветви поставщика CVS и без расширения ключевых слов RCS) с максимально малым количеством изменений, специфичных для FreeBSD. Утилита 'easy-import' на машине поможет в импортировании, но если есть сомнения по поводу выполнения этой операции, то обязательно спросите совета и не действуйте слепо в расчете на то, что "все сработает". CVS не прощает ошибок импортирования и для ликвидации последствий больших ошибок требуются значительные усилия. - -Из-за ранее отмеченных ограничений дизайна веток поставщиков в CVS требуется, чтобы "официальные" патчи от разработчика были сначала применены к распространяемым исходным текстам, а затем результат снова импортирован в ветку поставщика. Официальные патчи никогда не должны применяться к версии, извлеченной из хранилища FreeBSD, а затем "коммититься", так как это приведет к рассинхронизации дерева производителя и усложнит импортирование будущих версий, так как возникнут конфликты. - -Так как многие пакеты содержат файлы, имеющие значение при обеспечении совместимости с другими, отличными от FreeBSD архитектурами и окружениями, то разрешается удалять части дистрибутивного дерева, не представляющие интереса для FreeBSD в целях уменьшения занимаемого дискового пространства. Файлы, содержащие замечания о юридических правах и информацию о релизе, касающуюся остальных файлов, удаляться _не_ должны. - -Если это видится легким, то файлы [.filename]#Makefile# в стиле `bmake` могут быть сгенерированы из дистрибутивного дерева автоматически некоторой утилитой, чем-то, что позволит еще проще обновляться до новой версии. Если это будет сделано, то обязательно поместите эту утилиту (если необходимо) в каталог [.filename]#src/tools# вместе с самим портом, чтобы она была доступна будущим сопровождающим лицам. - -В каталог [.filename]#src/contrib/tcl# должен быть добавлен файл [.filename]#FREEBSD-upgrade#, в котором нужно перечислить такие вещи: - -* Какие файлы были оставлены -* Где был взят оригинальный дистрибутив и/или на каком основном официальном сайте он находится. -* Куда посылать патчи для разработчиков пакета -* Возможно, обзор сделанных изменений, специфичных для FreeBSD. - -Однако, пожалуйста, не импортируйте [.filename]#FREEBSD-upgrade# вместе с исходными текстами этого программного обеспечения. Вместо этого вы должны выполнить команды `cvs add FREEBSD-upgrade ; cvs ci` после первоначального импортирования. Ниже дается пример описания из каталога [.filename]#src/contrib/cpio#: - -[.programlisting] -.... -This directory contains virgin sources of the original distribution files -on a "vendor" branch. Do not, under any circumstances, attempt to upgrade -the files in this directory via patches and a cvs commit. New versions or -official-patch versions must be imported. Please remember to import with -"-ko" to prevent CVS from corrupting any vendor RCS Ids. - -For the import of GNU cpio 2.4.2, the following files were removed: - - INSTALL cpio.info mkdir.c - Makefile.in cpio.texi mkinstalldirs - -To upgrade to a newer version of cpio, when it is available: - 1. Unpack the new version into an empty directory. - [Do not make ANY changes to the files.] - - 2. Remove the files listed above and any others that don't apply to - FreeBSD. - - 3. Use the command: - cvs import -ko -m 'Virgin import of GNU cpio v<version>' \ - src/contrib/cpio GNU cpio_<version> - - For example, to do the import of version 2.4.2, I typed: - cvs import -ko -m 'Virgin import of GNU v2.4.2' \ - src/contrib/cpio GNU cpio_2_4_2 - - 4. Follow the instructions printed out in step 3 to resolve any - conflicts between local FreeBSD changes and the newer version. - -Do not, under any circumstances, deviate from this procedure. - -To make local changes to cpio, simply patch and commit to the main -branch (aka HEAD). Never make local changes on the GNU branch. - -All local changes should be submitted to "cpio@gnu.ai.mit.edu" for -inclusion in the next vendor release. - -obrien@FreeBSD.org - 30 March 1997 -.... - -[[policies-encumbered]] -== Нежелательные файлы - -Иногда может быть необходимо включить некоторый нежелательный для нас файл в дерево исходных текстов FreeBSD. Например, если устройство требует загрузки в него некоторого маленького двоичного кода перед тем, как устройство заработает, и мы не имеем исходных текстов этого кода, то говорится, что двоичный файл является нежелательным. Для включения нежелательных файлов в дерево исходных текстов FreeBSD имеются следующие соглашения. - -. Любой файл, интерпретируемый или выполняемый системным(и) CPU, не в форме исходного кода, является нежелательным. -. Любой файл с лицензией, ограничивающей более, чем BSD или GNU, является нежелательным. -. Файл, содержащий загружаемые двоичные данные, используемые аппаратным обеспечением, не являются нежелательными, если только к нему не применимы условия (1) или (2). Он должен быть сохранен в нейтральном к архитектуре формате ASCII (рекомендуется применить утилиты file2c или uuencode). -. Любой нежелательный файл требует особого одобрения со стороны extref:{contributors}[Правления, staff-core] до того, как он будет добавлен в хранилище CVS. -. Нежелательные файлы помещаются в каталог [.filename]#src/contrib# или [.filename]#src/sys/contrib#. -. Части одного модуля должны храниться вместе. Нет необходимости разбивать их, если только нет совместного использования с кодом, не являющимся нежелательным. -. Объектные файлы именуются [.filename]#arch/filename.o.uu>#. -. Файлы ядра; -.. Должны всегда упоминаться в [.filename]#conf/files.*# (для упрощения построения). -.. Должны всегда присутствовать в [.filename]#LINT#, но extref:{contributors}[Правление, staff-core] решает в каждом конкретном случае, должны ли они быть раскомментированы или нет. Конечно, позже extref:{contributors}[Правление, staff-core] может изменить свое решение. -.. Вопрос о вхождении в состав релиза решается _Группой Выпусков Релизов_. - -. Файлы уровня пользователя: -.. extref:{contributors}[Правление, staff-core] решает, должен ли код стать частью выполнения команды `make world`. -.. extref:{contributors}[Релиз инженер, staff-who] решает, войдут ли они в релиз. - -[[policies-shlib]] -== Динамические библиотеки - -Если вы добавляете поддержку динамических библиотек к порту или другой части программного обеспечения, которая этой возможностью не обладает, то номера версий должны назначаться по нижеследующим правилам. Как правило, получающиеся номера не имеют ничего общего с номером релиза программного обеспечения. - -При построении динамической библиотеки используются три принципа: - -* Начинаем с `1.0` -* Если есть изменение, которое имеет обратную совместимость, увеличиваем младший номер версии (заметьте, что системы ELF его игнорируют) -* Если есть изменение, не соблюдающее совместимость, увеличиваем старший номер версии - -К примеру, добавление функций и исправление ошибок приводит к увеличению младшего номера версии, а удаление функций, изменение синтаксиса вызова функции и тому подобные изменения приводят к изменению старшего номера версии. - -Следуйте схеме нумерации версий в форме старший.младший (_x_._y_). Наш динамический загрузчик формата a.out не умеет нормально работать с номерами версий в форме _x_._y_._z_. Любой номер версии после _y_ (то есть третье число) полностью игнорируется при сравнении номеров версий динамических библиотек для определения того, с какой библиотекой осуществлять компоновку. Если есть две динамические библиотеки, отличающиеся только "микро"-номером версии, то `ld.so` будет осуществлять компоновку с наибольшим номером. Другими словами: если вы компонуете с [.filename]#libfoo.so.3.3.3#, то компоновщик запишет в заголовках только `3.3` и будет выполнять компоновку с любой библиотекой, начинающейся с _libfoo.so.3.(все, что >= 3).(наибольшее из доступного)_. - -[NOTE] -==== -`ld.so` всегда будет использовать наибольшую "младшую" версию. Иными словами: он будет предпочитать использовать [.filename]#libc.so.2.2#, а не [.filename]#libc.so.2.0#, даже если программа изначально была скомпонована с [.filename]#libc.so.2.0#. -==== - -Вдобавок наш динамический компоновщик ELF совсем не работает с младшими версиями. Однако все же нужно указывать старший и младший номер версии, а наши файлы [.filename]#Makefile#"сделают все как нужно" в зависимости от типа системы. - -Для библиотек не в составе портов, имеется наше соглашение на изменение номера версии динамической библиотеки только один раз между релизами. Кроме того, есть договоренность на изменение старшего номера динамической библиотеки только один раз между главными релизами ОС (например c 3.0 к 4.0). Когда вы делаете изменение в системной библиотеке, которое требует увеличения номера версии, посмотрите журналы коммитов изменений в файле [.filename]#Makefile#. Коммиттер отвечает за то, что первое такое изменение с момента релиза приведет к обновлению номера версии динамической библиотеки в файле [.filename]#Makefile#, а при других последующих изменениях этого бы не делалось. diff --git a/documentation/content/ru/books/developers-handbook/secure/chapter.adoc b/documentation/content/ru/books/developers-handbook/secure/chapter.adoc deleted file mode 100644 index 28ff3ac158..0000000000 --- a/documentation/content/ru/books/developers-handbook/secure/chapter.adoc +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: Глава 3. Безопасное программирование -authors: - - author: Murray Stokely ---- - -[[secure]] -= Безопасное программирование -:doctype: book -:toc: macro -:toclevels: 1 -:icons: font -:sectnums: -:sectnumlevels: 6 -:sectnumoffset: 3 -:partnums: -:source-highlighter: rouge -:experimental: -:images-path: books/developers-handbook/ - -ifdef::env-beastie[] -ifdef::backend-html5[] -:imagesdir: ../../../../images/{images-path} -endif::[] -ifndef::book[] -include::shared/authors.adoc[] -include::shared/mirrors.adoc[] -include::shared/releases.adoc[] -include::shared/attributes/attributes-{{% lang %}}.adoc[] -include::shared/{{% lang %}}/teams.adoc[] -include::shared/{{% lang %}}/mailing-lists.adoc[] -include::shared/{{% lang %}}/urls.adoc[] -toc::[] -endif::[] -ifdef::backend-pdf,backend-epub3[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] -endif::[] - -ifndef::env-beastie[] -toc::[] -include::../../../../../shared/asciidoctor.adoc[] -endif::[] - -[[secure-synopsis]] -== Обзор - -Эта глава описывает некоторые из проблем обеспечения безопасности, которые десятилетиями преследовали программистов UNIX(R), а также несколько новых доступных инструментов, помогающих программистам избежать написания небезопасного кода. - -[[secure-philosophy]] -== Методология обеспечения безопасности - -Написание безопасных приложений требует весьма критического и пессимистического взгляда на жизнь. Приложения должны работать по принципу "наименьших привилегий", при котором никакой процесс не должен работать с привилегиями, превышающими минимально необходимый для выполнения своих функций минимум. Ранее проверенный код должен использоваться там, где только это возможно для избежания общих ошибок, которые могли быть уже исправлены другими. - -Одной из неприятностей в среде UNIX(R) является легкость в предположении безопасности этого окружения. Приложения никогда не должны верить пользовательскому вводу (во всех его формах), ресурсам системы, межпроцессному взаимодействию или времени выполнения событий. Процессы UNIX(R) выполняются не синхронно, так что логические операции редко бывают атомарными. - -[[secure-bufferov]] -== Переполнения буфера - -Переполнения буфера появились вместе с появление архитектуры Фон-Неймана <<COD,Впервые широкую известность они получили в 1988 году вместе с Интернет-червем Морриса (Morris). К сожалению, точно такая же атака остаётся эффективной и в наши дни. Из 17 бюллетеней безопасности CERT за 1999 год, 10 были непосредственно вызваны ошибкам в программном обеспечении, связанным с переполнениями буфера. Самые распространенные типы атак с использованием переполнения буфера основаны на разрушении стека.>> - -Самые современные вычислительные системы используют стек для передачи аргументов процедурам и сохранения локальных переменных. Стек является буфером типа LIFO (последним вошел первым вышел) в верхней части области памяти процесса. Когда программа вызывает функцию, создается новая "граница стека". Эта граница состоит из аргументов, переданных в функцию, а также динамического количества пространства локальных переменных. "Указатель стека" является регистром, хранящим текущее положение вершины стека. Так как это значение постоянно меняется вместе с помещением новых значений на вершину стека, многие реализации также предусматривают "указатель границы", который расположен около начала стека, так что локальные переменные можно легко адресовать относительно этого значения. <<COD,Адрес возврата из функции также сохраняется в стеке, и это является причиной нарушений безопасности, связанных с переполнением стека, так как перезаписывание локальной переменной в функции может изменить адрес возврата из этой функции, потенциально позволяя злоумышленнику выполнить любой код.>> - -Хотя атаки с переполнением стека являются самыми распространенными, стек можно также перезаписать при помощи атаки, основанной на выделении памяти (malloc/free) из "кучи". - -Как и во многих других языках программирования, в C не выполняется автоматической проверки границ в массивах или указателях. Кроме того, стандартная библиотека C полна очень опасных функций. - -//// -[.informaltable] -[cols="", frame="none"] -|=== -|=== -//// - -== Пример переполнения буфера - -В следующем примере кода имеется ошибка переполнения буфера, предназначенная для перезаписи адреса возврата и обхода инструкции, следующей непосредственно за вызовом функции. (По мотивам <<Phrack,)>> - -[.programlisting] -.... -#include stdio.h - -void manipulate(char *buffer) { - char newbuffer[80]; - strcpy(newbuffer,buffer); -} - -int main() { - char ch,buffer[4096]; - int i=0; - - while ((buffer[i++] = getchar()) != '\n') {}; - - i=1; - manipulate(buffer); - i=2; - printf("The value of i is : %d\n",i); - return 0; -} -.... - -Давайте посмотрим, как будет выглядеть образ процесса, если в нашу маленькую программу мы введем 160 пробелов. - -[XXX figure here!] - -Очевидно, что для выполнения реальных инструкций (таких, как exec(/bin/sh)), может быть придуман более вредоносный ввод. - -=== Как избежать переполнений буфера - -Самым прямолинейным решением проблемы переполнения стека является использование только памяти фиксированного размера и функций копирования строк. Функции `strncpy` и `strncat` являются частью стандартной библиотеки C. Эти функции будут копировать не более указанного количества байт из исходной строки в целевую. Однако у этих функций есть несколько проблем. Ни одна из них не гарантирует наличие символа NUL, если размер входного буфера больше, чем целевого. Параметр длины также по-разному используется в strncpy и strncat, так что для программистов легко запутаться в правильном использовании. Есть также и значительная потеря производительности по сравнению с `strcpy` при копировании короткой строки в большой буфер, потому что `strncpy` заполняет символами NUL пространство до указанной длины. - -Для избежания этих проблем в OpenBSD была сделана другая реализация копирования памяти. Функции `strlcpy` и `strlcat` гарантируют, что они они всегда терминируют целевую строку нулевым символом, если им будет передан аргумент ненулевой длины. Более подробная информация об этом находится здесь <<OpenBSD,Инструкции OpenBSD `strlcpy` и `strlcat` существуют во FreeBSD начиная с версии 3.3.>> - -==== Вкомпилированная проверка границ во время выполнения - -К сожалению, все еще широко используется очень большой объём кода, который слепо копирует память без использования только что рассмотренных функций с проверкой границ. Однако есть другое решение. Существует несколько расширений к компилятору и библиотек для выполнения контроля границ во время выполнения (C/C++). - -Одним из таких добавлений является StackGuard, который реализован как маленький патч к генератору кода gcc. Согласно http://immunix.org/stackguard.html[web сайту StackGuard]: - -"StackGuard распознает и защищает стек от атак, не позволяя изменять адрес возврата в стеке. При вызове функции StackGuard помещает вслед за адресом возврата сигнальное слово. Если после возврата из функции оно оказывается измененным, то была попытка выполнить атаку на стек, и программа отвечает на это генерацией сообщения о злоумышленнике в системном журнале, а затем прекращает работу." - -"StackGuard реализован в виде маленького патча к генератору кода gcc, а именно процедур function_prolog() и function_epilog(). function_prolog() усовершенствована для создания пометок в стеке при начале работы функции, а function_epilog() проверяет целостность пометки при возврате из функции. Таким образом, любые попытки изменения адреса возврата определяются до возврата из функции." - -Перекомпиляция вашего приложения со StackGuard является эффективным способом остановить большинство атак переполнений буфера, но все же полностью это проблемы не решает. - -==== Проверка границ во время выполнения с использованием библиотек. - -Механизмы на основе компилятора полностью бесполезны для программного обеспечения, поставляемого в двоичном виде, которое вы не можете перекомпилировать. В этих ситуациях имеется некоторое количество библиотек, в которых реализованы небезопасные функции библиотеки C (`strcpy`, `fscanf`, `getwd`, и так далее..), обеспечивающие невозможность записи после указателя стека. - -* libsafe -* libverify -* libparanoia - -К сожалению, эти защиты имеют некоторое количество недостатков. Эти библиотеки могут защитить только против малого количества проблем, и не могут исправить реальные проблемы. Эти защиты могут не сработать, если приложение скомпилировано с параметром -fomit-frame-pointer. К тому же переменные окружения LD_PRELOAD и LD_LIBRARY_PATH могут быть переопределены/сняты пользователем. - -[[secure-setuid]] -== Проблемы с установленным битом UID - -Имеется по крайней мере 6 различных идентификаторов (ID), связанных с любым взятым процессом. Поэтому вы должны быть очень осторожны с тем, какие права имеет ваш процесс в каждый момент времени. В частности, все seteuid-приложения должны понижать свои привилегии, как только в них отпадает необходимость. - -Реальный ID пользователя может быть изменен только процессом администратора. Программа login устанавливает его, когда пользователь входит в систему, и он редко меняется. - -Эффективный ID пользователя устанавливается функциями `exec()`, если у программы установлен бит seteuidt. Приложение может выполнить вызов `seteuid()` в любой момент для установки эффективного ID пользователя в значение реального ID пользователя или сохраняемого set-user-ID. Когда эффективный ID пользователя устанавливается функциями `exec()`, его предыдущее значение сохраняется в сохраняемом set-user-ID. - -[[secure-chroot]] -== Ограничение среды работы вашей программы - -Традиционно используемым методом ограничения процесса является использование системного вызова `chroot()`. Этот системный вызов меняет корневой каталог, относительно которого определяются все остальные пути в самом процессе и всех порожденных ими процессах. Для того, чтобы этот вызов был выполнен успешно, процесс должен иметь право на выполнение (поиск) каталога, о котором идет речь. Новая среда реально не вступит в силу, пока вы не выполните вызов `chdir()` в вашей новой среде. Следует также отметить, что процесс может с легкостью выйти из chroot-среды, если он имеет привилегии администратора. Это может быть достигнуто созданием файлов устройств для чтения памяти ядра, подключением отладчика к процессу вне узницы и многими другими способами. - -Поведение системного вызова `chroot()` можно некоторым образом контролировать `sysctl`-переменной kern.chroot_allow_open_directories. Когда эта переменная установлена в 0, `chroot()` не сработает с ошибкой EPERM, если есть какие-либо открытые каталоги. Если она установлена в значение по умолчанию, равное 1, то `chroot()` не сработает с ошибкой EPERM, если есть какие-либо открытые каталоги и процесс уже подвергнут вызову `chroot()`. Для всех других значений проверка открытости каталогов будет полностью опущена. - -=== Функциональность джейлов (jail) во FreeBSD - -Концепция джейлов (Jail) расширяет возможности `chroot()`, ограничивая власть администратора созданием настоящих `виртуальных серверов'. Как только тюремная камера создана, все сетевые коммуникации должны осуществляться через выделенный адрес IP, а сила "привилегий пользователя root" в этой тюрьме довольно ограничена. - -При работе внутри тюрьмы, любые проверки силы администратора в ядре при помощи вызова `suser()` будут оканчиваться неудачно. Однако некоторые вызовы к `suser()` были изменены на новый интерфейс `suser_xxx()`. Эта функция отвечает за распознание и разрешение доступа к власти администратора для процессов, не находящихся в неволе. - -Процесс администратора внутри среды джейла имеет право: - -* Манипулировать привилегиями с помощью `setuid`, `seteuid`, `setgid`, `setegid`, `setgroups`, `setreuid`, `setregid` и `setlogin` -* Устанавливать ограничения на использование ресурсов при помощи `setrlimit` -* Модифицировать некоторые sysctl-переменные (kern.hostname) -* `chroot()` -* Устанавливать следующие флаги на vnode: `chflags`, `fchflags` -* Устанавливать такие атрибуты vnode, как права доступа к файлу, изменять его владельца, группу, размер, время доступа и модификации. -* Осуществлять привязку к привилегированному порту в области портов Интернет (порты с номерами 1024) - -`Jail` является очень полезным инструментом для запуска приложений в защищенном окружении, но есть и некоторые недостатки. На текущий момент к формату `suser_xxx` не преобразованы механизмы IPC, так что такие приложения, как MySQL, не могут работать в джейле. Права администратора могут имеет малую силу внутри джейла, но нет способа определить, что значит "малую". - -=== POSIX(R).1e возможности процессов - -POSIX(R) выпустила рабочий документ, который добавляет аудит событий, списки управления доступом, тонко настраиваемые привилегии, метки информации и жесткое управление доступом. - -Этот документ находится в работе и находится в центре внимания проекта http://www.trustedbsd.org/[TrustedBSD]. Некоторая начальная функциональность уже была добавлена во FreeBSD-CURRENT (cap_set_proc(3)). - -[[secure-trust]] -== Доверие - -Приложение никогда не должно полагать, что среда пользователя безопасна. Сюда включается (но этим не ограничено): ввод пользователя, сигналы, переменные среды, ресурсы, IPC, отображаемая в файл память (mmap), рабочий каталог файловой системы, дескрипторы файлов, число открытых файлов и прочее. - -Никогда не думайте, что сможете предусмотреть все формы неправильного ввода, который может дать пользователь. Вместо этого ваше приложение должно осуществлять позитивную фильтрацию, пропуская только конечное множество возможных вариантов ввода, которые вы считаете безопасными. Неполная проверка данных была причиной многих нарушений защиты, особенно CGI-скриптов на веб-сайтах. Для имен файлов вам нужно уделять особое внимание путям ("../", "/"), символическим ссылкам и экранирующим символам оболочки. - -В Perl имеется такая очень полезная вещь, как "безупречный" (taint) режим, который можно использовать для запрещения скриптам использовать данные, порожденные вне программы, не безопасным способом. Этот режим проверяет аргументы командной строки, переменные окружения, информацию локализации, результаты некоторых системных вызовов (`readdir()`, `readlink()`, `getpwxxx()` и весь файловый ввод. - -[[secure-race-conditions]] -== Неожиданное поведение - -Неожиданное поведение - это аномальное поведение, вызванное непредусмотренной зависимостью от относительной последовательности событий. Другими словами, программист неправильно предположил, что некоторое событие всегда случается перед другим. - -Некоторые из широко распространенных причин возникновения таких проблем являются сигналы, проверки доступа и открытия файлов. Сигналы по своей природе являются асинхронными событиями, так что по отношению к ним нужно проявлять особое внимание. Проверка доступа функцией `access(2)` с последующим вызовом `open(2)` полностью не атомарно. Пользователи могут переместить файлы в промежутке между двумя вызовами. Вместо этого привилегированное приложение должно выполнить `seteuid()`, а затем сразу вызвать `open()`. В тех же строках приложение должно всегда устанавливать явно маску прав доступа (umask) перед вызовом функции `open()` во избежание беспорядочных вызовов `chmod()`. |
