Создание демонов запуска и агентов

При разработке демонов для работы OS X, он настоятельно рекомендован это, Вы разрабатываете своих демонов, чтобы быть launchd совместимый. Используя launchd обеспечивает лучшую производительность и гибкость для демонов. Это также улучшает возможность администраторов управлять демонами, работающими на данной системе.

Если Вы выполняете фоновые процессы в расчете на пользователя для OS X, launchd также предпочтительный способ запустить эти процессы. Эти процессы в расчете на пользователя упоминаются как агенты пользователя. Агент пользователя является чрезвычайно идентичным демону, но является определенным для данного, вошел в систему пользователь и выполняется только, в то время как зарегистрирован тот пользователь.

Если не указано иное, в целях этой главы, термины «демон» и «агент» могут быть использованы взаимозаменяемо. Таким образом термин «демон» используется в общем в этом разделе для затрагивания и демонов системного уровня и агентов пользователя кроме, где иначе отмечено.

Существует четыре способа запустить демонов, использующих launchd. Предпочтительный метод по требованию запускается, но launchd может запустить демонов, которые работают постоянно и могут заменить inetdдля запуска inetd- демоны стиля. Кроме того, launchd может запустить задания в синхронизированных интервалах.

Несмотря на то, что launchd поддержки не запускают по требованию демонов, это использование не рекомендуется. launchd демон был разработан для устранения необходимости упорядочивания зависимости среди демонов. Если Вы не делаете своего демона, запускаются по требованию, необходимо будет обработать эти зависимости в другом отношении, такой как при помощи устаревшего механизма элемента запуска.

Запуск Пользовательских Демонов Используя launchd

С введением launchd в OS X v10.4, усилие было приложено для улучшения, шаги должны были запустить и поддержать демонов. Что launchd обеспечивает ремень безопасности для запуска Вашего демона по мере необходимости. К клиентским программам порт, представляющий службу Вашего демона, всегда доступен и готов обработать запросы. В действительности демон может или может не работать. Когда клиент отправляет запрос к порту, launchd вероятно, придется запустить демона так, чтобы это могло обработать запрос. После того, как запущенный, демон может продолжать работать или завершить работу себя для высвобождения памяти и ресурсов, которые она содержит. Если демон завершает работу себя, launchd еще раз перезапуски это по мере необходимости для обработки запросов.

В дополнение к функции запуска по требованию, launchd предоставляет следующие преимущества разработчикам демона:

Процесс Запуска launchd

После того, как система загружается, и ядро работает, launchd выполняется для окончания системной инициализации. Как часть той инициализации, это проходит через следующие шаги:

  1. Это загружает параметры для каждого демона системного уровня запуска по требованию от файлов списка свойств, найденных в /System/Library/LaunchDaemons/ и /Library/LaunchDaemons/.

  2. Это регистрирует сокеты и дескрипторы файлов, которые требуют те демоны.

  3. Это запускает любых демонов, запросивших работать все время.

  4. Когда запросы на определенную службу поступают, она запускает соответствующего демона и передает запрос ему.

  5. Когда система закрывается, она отправляет a SIGTERM сигнализируйте всем демонам, что это запустилось.

Процесс для агентов в расчете на пользователя подобен. Когда пользователь входит в систему, в расчете на пользователя launchd запускается. Это делает следующее:

  1. Это загружает параметры для каждого агента пользователя запуска по требованию от файлов списка свойств, найденных в /System/Library/LaunchAgents, /Library/LaunchAgents, и частное лицо пользователя Library/LaunchAgents каталог.

  2. Это регистрирует сокеты и дескрипторы файлов, которые требуют те агенты пользователя.

  3. Это запускает любые агенты пользователя, запросившие работать все время.

  4. Когда запросы на определенную службу поступают, она запускает соответствующий агент пользователя и передает запрос ему.

  5. Когда пользователь выходит из системы, это отправляет a SIGTERM сигнализируйте ко всем агентам пользователя, что это запустилось.

Поскольку launchd регистрирует сокеты и дескрипторы файлов, используемые всеми демонами, прежде чем это запустит любой из них, демоны могут быть запущены в любом порядке. Если запрос наталкивается на демона, еще не работающего, процесс запроса приостановлен, пока целевой демон не заканчивает запускаться и отвечает.

Если демон не получает запросов за определенный промежуток времени, он может принять решение завершить работу себя и высвободить средства, которые он содержит. Когда это происходит, launchd когда будущие запросы поступают, контролирует завершение работы и записывает для запуска демона снова.

Создание launchd Файла Списка свойств

Работать под launchd, необходимо предоставить файл списка свойств конфигурации демону. Этот файл содержит информацию о Вашем демоне, включая список сокетов или дескрипторов файлов, которые это использует для обработки запросов. Указание этой информации в файле списка свойств позволяет launchd зарегистрируйте соответствующие дескрипторы файлов и запустите Вашего демона только после того, как запрос поступит для служб Вашего демона. Таблица 5-1 перечисляет требуемые и рекомендуемые ключи для всех демонов.

Файл списка свойств структурирован то же и для демонов и для агентов. Вы указываете, описывает ли это демона или агент каталогом, в который Вы помещаете его. Файлы списка свойств, описывающие демонов, установлены в /Library/LaunchDaemons, и те, которые описывают агенты, установлены в /Library/LaunchAgents или в LaunchAgents подкаталог отдельного пользователя Library каталог. (Надлежащее расположение для исполнимых программ, которые Вы запускаете от своего задания, /usr/local/libexec.)

Таблица 5-1  Требуемые и рекомендуемые ключи списка свойств

Ключ

Описание

Label

Содержит уникальную строку, идентифицирующую Вашего демона для launchd. (требуемый)

ProgramArguments

Содержит параметры, используемые для запуска демона. (требуемый)

inetdCompatibility

Указывает, что Ваш демон требует отдельного экземпляра на входящее соединение. Это вызывает launchd вести себя как inetd, передача каждого демона единственный сокет, уже подключенный к входящему клиенту. (требуемый, если Ваш демон был разработан, чтобы быть запущенным inetd; иначе, не должен быть включен),

KeepAlive

Этот ключ указывает, запускается ли Ваш демон по требованию или должен всегда работать. Рекомендуется разработать демона, чтобы быть запущенными по требованию.

Запись “Привет Мир!” Задание launchd

Следующий простой пример запускается, демон назвал hello, передача world как отдельный аргумент, и дает launchd команду сохранять выполнение задания:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.hello</string>
    <key>ProgramArguments</key>
    <array>
        <string>hello</string>
        <string>world</string>
    </array>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>
 

В этом примере существует три ключа в высокоуровневом словаре. Первое Label, который однозначно определяет задание. когда. Второе ProgramArguments который имеет значение массива строк, представляющих маркируемые параметры и программу для выполнения. Третий и заключительный ключ KeepAlive который указывает, что это задание должно работать в любом случае, а не поведение запуска по требованию по умолчанию, таким образом, launchd должен всегда пытаться сохранить это выполнение задания.

Слушание на сокетах

Можно также включать другие ключи файле списка свойств конфигурации. Например, если Ваш демон контролирует стандартный порт (один из портов, перечисленных в /etc/services), добавьте a Sockets запись следующим образом:

<key>Sockets</key>
<dict>
    <key>Listeners</key>
    <dict>
        <key>SockServiceName</key>
        <string>bootps</string>
        <key>SockType</key>
        <string>dgram</string>
        <key>SockFamily</key>
        <string>IPv4</string>
    </dict>
</dict>

Строка для SockServiceName обычно прибывает из крайнего левого столбца в /etc/services. SockType один из dgram (UDP) или stream (TCP/IP). Если необходимо передать номер порта, не перечисленный в списке стандартных портов, формат является тем же, кроме строки содержит число вместо имени. Например:

<key>SockServiceName</key>
<string>23</string>

Отладка launchd Джобс

Существуют некоторые опции, которые полезны для отладки Вашего launchd задания.

Следующий пример включает дампы ядра, устанавливает норму и ошибку перейти к файлу журнала, и дает launchd команду временно увеличиваться, уровень отладки его журналирования при действии от имени задания (не забудьте корректировать syslog.conf соответственно):

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.sleep</string>
    <key>ProgramArguments</key>
    <array>
        <string>sleep</string>
        <string>100</string>
    </array>
    <key>StandardOutPath</key>
    <string>/var/log/myjob.log</string>
    <key>StandardErrorPath</key>
    <string>/var/log/myjob.log</string>
    <key>Debug</key>
    <true/>
    <key>SoftResourceLimits</key>
    <dict>
        <key>Core</key>
        <integer>9223372036854775807</integer>
    </dict>
    <key>HardResourceLimits</key>
    <dict>
        <key>Core</key>
        <integer>9223372036854775807</integer>
    </dict>
</dict>
</plist>

Выполнение задания периодически

Следующий пример создает рабочее место, выполняющееся каждые пять минут (300 секунд):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.touchsomefile</string>
    <key>ProgramArguments</key>
    <array>
        <string>touch</string>
        <string>/tmp/helloworld</string>
    </array>
    <key>StartInterval</key>
    <integer>300</integer>
</dict>
</plist>

Поочередно, можно указать основанный на календаре интервал. Следующий пример запускает задание в 7-й день каждого месяца в 13:45 (13:45). Как подсистема крона Unix, любой недостающий ключ StartCalendarInterval словарь обрабатывается как подстановочный знак — в этом случае, месяц опущен, таким образом, задание выполняется каждый месяц.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.touchsomefile</string>
    <key>ProgramArguments</key>
    <array>
        <string>touch</string>
        <string>/tmp/helloworld</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Minute</key>
        <integer>45</integer>
        <key>Hour</key>
        <integer>13</integer>
        <key>Day</key>
        <integer>7</integer>
    </dict>
</dict>
</plist>

Контроль каталога

Следующий пример запускает задание каждый раз, когда изменился любой из наблюдаемых путей:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.watchhostconfig</string>
    <key>ProgramArguments</key>
    <array>
        <string>syslog</string>
        <string>-s</string>
        <string>-l</string>
        <string>notice</string>
        <string>somebody touched /etc/hostconfig</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/etc/hostconfig</string>
    </array>
</dict>
</plist>

Дополнительный триггер файловой системы является понятием каталога очереди. launchd демон запускает Ваше задание каждый раз, когда данные каталоги непусты, и оно сохраняет Вашу работу, работающую, пока те каталоги не пусты:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.mailpush</string>
    <key>ProgramArguments</key>
    <array>
        <string>my_custom_mail_push_tool</string>
    </array>
    <key>QueueDirectories</key>
    <array>
        <string>/var/spool/mymailqdir</string>
    </array>
</dict>
</plist>

Эмуляция inetd

launchd демон эмулирует более старое inetd- разработайте семантику демона, если Вы обеспечиваете inetdCompatibility ключ:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.telnetd</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/libexec/telnetd</string>
    </array>
    <key>inetdCompatibility</key>
    <dict>
        <key>Wait</key>
        <false/>
    </dict>
    <key>Sockets</key>
    <dict>
        <key>Listeners</key>
        <dict>
            <key>SockServiceName</key>
            <string>telnet</string>
            <key>SockType</key>
            <string>stream</string>
        </dict>
    </dict>
</dict>
</plist>

Поведение для Процессов, Управляемых launchd

Процессы, которыми управляют launchd должен следовать за определенными требованиями так, чтобы они взаимодействовали должным образом с launchd. Это включает демонов запуска и агенты запуска.

Требуемые способы поведения

Поддерживать launchd, необходимо повиноваться следующим инструкциям при записи кода демона:

  • Необходимо предоставить списку свойств некоторые основные критерии запуска по требованию демона. Посмотрите Создание launchd Файла Списка свойств.

  • Вы не должны daemonize Ваш процесс. Это включает вызов daemon функция, вызывая fork сопровождаемый exec, или вызов fork сопровождаемый exit. Если Вы делаете, launchd думает, что умер Ваш процесс. В зависимости от Ваших ключевых настроек списка свойств, launchd или продолжит пытаться повторно запустить Ваш процесс, пока он не сдается (с “перепорождением слишком быстро” сообщение об ошибке) или будет неспособен перезапустить его, если он действительно умирает.

  • Демоны и агенты, установленные глобально, должны принадлежать полностью пользователь. Агенты, установленные для текущего пользователя, должны принадлежать тому пользователю. Все демоны и агенты не должны быть перезаписываемой группой или перезаписываемый мир. (Т.е. им нужно было установить режим файла в 600 или 400.)

Рекомендуемые способы поведения

Поддерживать launchd, рекомендуется повиноваться следующим инструкциям при записи кода демона:

  • Ожидайте, пока Ваш демон не будет полностью инициализирован прежде, чем попытаться обработать запросы. Ваш демон должен всегда обеспечивать разумный ответ (а не ошибка) при обработке запросов.

  • Зарегистрируйте сокеты и дескрипторы файлов, используемые Вашим демоном в Вашем launchd файл списка свойств конфигурации.

  • Если Ваш демон распространяет сокет, регистрацию с launchd как часть Вашей инициализации демона. Для реализации в качестве примера процесса регистрации посмотрите SampleD.

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

  • Обеспечьте обработчик для ловли SIGTERM сигнал.

В дополнение к предыдущему списку следующее является списком вещей, рекомендуется избежать в коде:

  • Не устанавливайте пользователя или группу ID для Вашего демона. Включайте UserName, UID, GroupName, или GID ключи в списке свойств конфигурации Вашего демона вместо этого.

  • Не устанавливайте рабочий каталог. Включайте WorkingDirectory введите список свойств конфигурации своего демона вместо этого.

  • Не вызывать chroot изменить корневой каталог. Включайте RootDirectory введите список свойств конфигурации своего демона вместо этого.

  • Не вызывать setsid создать новый сеанс.

  • Не закрывайте случайные дескрипторы файлов.

  • Не изменяться stdio указать на /dev/null. Включайте StandardOutPath или StandardErrorPath ключи в файле списка свойств конфигурации Вашего демона вместо этого.

  • Не устанавливайте пределы ресурса с setrusage.

  • Не устанавливайте приоритет демона с setpriority

Несмотря на то, что многие предыдущие способы поведения могут быть стандартными задачами для демонов выполнить, им не рекомендуют при выполнении под launchd. Причина - это launchd конфигурирует операционную среду для демонов, которыми она управляет. Изменение этой среды могло вмешаться в нормальное функционирование Вашего демона.

Решение, когда закрыться

Если Вы не ожидаете, что Ваш демон обработает много запросов, Вы могли бы хотеть завершить работу его после предопределенной суммы времени простоя, вместо того, чтобы продолжать работать. Несмотря на то, что правильно написанный демон не использует ресурсов CPU, когда неактивный, это все еще использует память и могло быть разбито на страницы в течение периодов интенсивного использования памяти.

Синхронизация того, когда закрыться, отличается для каждого демона и зависит от нескольких факторов, включая:

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

Специальные зависимости

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

Доступность сети

Если Ваш демон зависит от сети, являющейся доступным, это не может быть обработано с зависимостями, потому что сетевые интерфейсы могут прийти и уйти в любое время в OS X. Для решения этой проблемы необходимо использовать сетевую функциональность достижимости или функциональность динамической памяти в платформе Конфигурации системы. Это документируется в Инструкции по Программированию Конфигурации системы и Ссылку Платформы Конфигурации системы. Для получения дополнительной информации о сетевой достижимости, посмотрите Определение Достижимости и быть соединенным в Инструкциях по Программированию Конфигурации системы.

Диск или доступность сервера

Если Ваш демон зависит от доступности смонтированного объема (или локальный или удаленный), можно определить состояние того объема с помощью Дисковой Арбитражной платформы. Это документируется в Дисковую Арбитражную Ссылку Платформы.

Демоны Non-launchd

Если у Вашего демона есть зависимость от не -launchd демон, необходимо проявить дополнительную заботу, чтобы гарантировать, что демон работает правильно если это не -launchd когда Ваш демон запускается, демон не запустил. Лучший способ сделать это должно включать цикл во время начала, проверяющее, чтобы видеть если не -launchd демон работает, и в противном случае спит в течение нескольких секунд прежде, чем проверить снова.

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

Пользовательское имя для входа в систему

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

Для определения, какой пользователь зарегистрирован в консоли можно использовать платформу Конфигурации системы, как описано в Технических Вопросах и ответах QA1133.

Расширения ядра

Если Ваш демон требует, чтобы определенное расширение ядра было загружено до выполнения, у Вас есть две опции: загрузите его сами или ожидайте его, чтобы быть загруженными.

Демон может вручную запросить, чтобы было загружено расширение. Чтобы сделать это, работать kextload с надлежащим использованием параметров exec или варианты этого. Расширения ядра Набора I/O не должны быть загружены kextload; Набор I/O загрузит их автоматически, когда они будут необходимы.

Также наш демон может ожидать службы ядра, чтобы быть доступным. Чтобы сделать это, необходимо сначала зарегистрироваться для уведомления изменения службы. Это далее документируется в Ссылку Платформы Набора I/O.

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

Для получения дополнительной информации о службах I/O Kit и соответствии, см. Основные принципы IOKit, Ссылка Платформы Набора I/O (ссылка пространства пользователя) и Ссылка Платформы Ядра (ссылка пространства ядра).

Для получения дополнительной информации

Страницы руководства для launchd и launchd.plist два лучших источника для получения информации о launchd.

Кроме того, можно найти исходного демона, сопровождающего launchd исходный код (доступный от http://www .macosforge.org/). Этому демону также предоставлены от Библиотеки Разработчика Mac как проект примера кода SampleD.

Техническое примечание Демонов и Агентов предоставляет дополнительную информацию о как launchd демоны и агенты работают под капотом.

Наконец, много предоставленных Apple демонов поддерживают launchd. Их файлы списка свойств могут быть найдены в /System/Library/LaunchDaemons. Некоторые из этих демонов также доступны как открытый исходный код от http://www .opensource.apple.com/или http://www .macosforge.org/.