Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Программирование под Unix/Linux > создание демона


Автор: roman83 22.12.2006, 10:37
создание демона

ну прочитал http://linuxportal.ru/entry.php/2361_0_3_0_C/
...к сожалению не совсем понял....мне нужно чтобы эта програмка автоматически стартовала при запуске компьютера...а здесь как я понял просто процесс создается
как в Linux сделать ананлог виндусовского сервиса???

Автор: block 22.12.2006, 11:24
Добавьте нужную вам команду в файл /etc/rc.d/rc.local:

echo 'echo "TEST LOCAL START" >> /var/log/messages' >> /etc/rc.d/rc.local

Автор: roman83 22.12.2006, 11:49
так...приверно про это тоже прочитал.....а вопрос такой....у меня SUSE linux...там короче не файл со скриптом, а папка где находятся линки(ярлыки) длоя запуска(по моему это идиотизм)....можнол ли как то сделать ссылку но с параметрами командной строки для передачи???....и кстати какой "свой" или рабочий каталог будет при запуске приложения подобным образом???
...и кстати эта ссылка должна быть просто на исполняемый файл или как бы на демон???

Добавлено @ 12:01 
да уж....чтото не совсем понятно, что это должна быть за ссылка....на исполняемый файл???....так не подвесит ли он у меня систему, т.к. он у меня естественно работает и работает, и управление не передает.....и интересно, проиниализированны ли уже к этому времени будут сетевые интерфейсы, а то у меня программа с них информацию тянет...

Добавлено @ 12:03 
ну и еще...наверное все таки должен быть демон....и если я не отключу(ну или отключу) вывод в стандартный вывод, то что проихойдет при попытке вывода???...

Автор: strmaks 22.12.2006, 13:00
В целом все выглядит следующим образом.

Программа (демон) запускается перехватывает все потокии ввода вывода (STDIN, STDOUT) и поток ошибок (STDERR) и либо перенаправляет все это добро в файли, либо же пишет их в сислог, если не перехватывать то при попытке чего либо вывести на STDOUT или STDERR это удет отображено на консоли из которой выполнялся запуск либо же на первом терминале. Сделать это можно либо внутри программы либо командой запуска.

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

Линки (ярлычки  smile ) делаются для того что бы файлы запускные лежали по своим иерархиям и ни в коем случае не расползались по диску. Для запуска едмонов используются так называемые стартап скрипты, которые и выполняют 2 основные команды start (запуск) и стоп (останов) твоего демона.

Автор: MAKCim 22.12.2006, 14:16
Цитата

у меня SUSE linux...там короче не файл со скриптом, а папка где находятся линки(ярлыки) длоя запуска(по моему это идиотизм)

в gentoo конечно удобнее (rc-update рулит), никто не спорит
но называть то, что вы назвали, идиотизмом, тоже не стоит
вполне все логично
init переходит от одного уровня выполнения к другому и попутно запускает все что лежит в /etc/init.d/rc<уровень>.d
Цитата

можнол ли как то сделать ссылку но с параметрами командной строки для передачи???

symbolic link содержит путь к файлу, если этот файл - исполняемый и для него предусмотрены входные параметры, то вызов его через ссылку = вызов его напрямую (вместе с возможными параметрами)
Цитата

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

пишешь своего демона
(допустим в итоге получил бинарник daemon)
создаешь файл в /etc/init.d (например daemon-run) вида
Код

#!/bin/bash
DAEMON_NAME=<имя демона>
DAEMON_PATH=<путь>$DAEMON_NAME

case $1 in
    start) $DAEMON_PATH ;;
    stop)
        pkill -15 $DAEMON_NAME ;;
esac

далее
Код

# ln -s /etc/init.d/rc3.d/S<порядковый номер><имя демона> /etc/init.d/<имя созданного файла>
# ln -s /etc/init.d/rc3.d/K<порядковый номер><имя демона> /etc/init.d/<имя созданного файла>

Автор: roman83 22.12.2006, 14:47
ну вроде все сделал по такому принципу.....вроде все работает...НО!!!.....когда его запускаешь их менеджера сервисов, то при запуске(хоть и запускается сразу) долго весит табличка о том что он запускается, а потом пишет что вернулся NIL

хотя приложение запустилось и работает

Автор: roman83 22.12.2006, 15:03
делал так
  pid_t pid, sid;

  pid = fork();
  if (pid < 0) {
    exit(EXIT_FAILURE);
  }

  if (pid > 0) {
    exit(EXIT_SUCCESS);
  }


  umask(0);

  sid = setsid();
  if (sid < 0) {
   
    exit(EXIT_FAILURE);
  }

  if ((chdir("/work")) < 0) {
    
    exit(EXIT_FAILURE);
  }


  дальше воя программа которая естественно управление не возвращает

вот скрипт :

. /etc/rc.status

rc_reset

DAEMON_NAME=Test1
DAEMON_PATH=/work/..<mypath>./$DAEMON_NAME

case "$1" in
  start)
    echo -n 'Starting !!!'
    startproc $DAEMON_PATH Test1_SID eth2 -ORBmaxGIOPVersion 1.0 -ORBInitRef NameService=corbaname::[email protected]:23456/NameService 
    rc_status -v
    ;;
  stop)
    echo -n "Shutting down !!! "
    killproc -TERM $DAEMON_NAME
    rc_status -v
    ;;
  restart)
    $0 stop
    $0 start
    rc_status
    ;;
  try-restart)
    $0 status
    if test $? = 0; then
    $0 restart
    else
    rc_reset
    fi
    rc_status
    ;;
  force-reload)
    $0 stop; sleep 1  &&  $0 start
    rc_status
    ;;
  reload)
    echo -n "Reload service  "
    killproc -HUP $DAEMON_NAME
    rc_status -v
    ;;
  status)
    echo -n "Checking  "
    checkproc $DAEMON_NAME
    rc_status -v
    ;;
  *)
    echo "Usage: $0 {start|stop|try-restart|restart|force-reload|reload|status}"
    exit 1
esac
rc_exit


кстати где прочитать про этот скрипт язык, а то делаю все по образу и подобию , это как то не очень правильно

Автор: bilbobagginz 22.12.2006, 22:36
Цитата

кстати где прочитать про этот скрипт язык, а то делаю все по образу и подобию , это как то не очень правильно

man sh, или man bash ( или поковыряй интернет на тему bash tutorial )

насчёт инициализации систем вообще, на самом деле, тема несложная, но она не из программирования. 

SuSE - из систем использующих сл. идею, пришедшую из UNIX System 5.
задача: инициализировать службы системы из режима пользователя.
решение: 
  • oпределяется особый процесс, init - праотец всех процессов, 
  • определяются "режимы запуска" "run levels"
каждый режим является спец. профилем работы. по историческим причинам есть в принципе 8 run levels: rc0, rcS.d и 6 простых режимов.
процесс init ( самый первый процесс уровня пользователя ) подцепляет этот профиль, 
запуская соответствующую папку скриптов. 
тем самым проводит инициализацию системы соответствующего режима.

в системах SUSE, скрипты каждого режима кладутся в форме символьный ссылок с особым именем в 
/etc/rc.d/rc<номер режима>.d/ ( напр. rc4.d )
сами же скрипты кладутся в /etc/rc.d/

тот скрипт, который их запускает, передаёт каждому 1 аргумент.
аргумент определяется по имени ссылки в /etc/rc.d/rcN.d/
там есть скрипты начинающиеся с S<2 цифры><название инициализационного скрипта>, напр.
S08portmap 
циферки в названии ссылки определяют порядок запуска скриптов между собой. 
если циферки идентичные - инициализация произойдёт в алфавитном порядке. 
циферки идут от 01 до 99. 
  • S->start, передаваемый аргумент - start, запускаются в порядке возрастания числа
  • K->kill, передаваемый аргумент - stop, запускаются в порядке убывания

в современных системах есть утилитки автоматического добавления скрипта инициализации из /etc/rc.d
в соответствующий каталог /etc/rc.d/rc*.d ( man chkconfig, в SUSE, man update-rc.d в Debian )

в конце концов, (в SuSE) для превращения программы в сервис, нужно создать скрипт в /etc/rc.d, который реагирует на аргументы start, stop ( как минимум, но обычно и : restart, reload etc. )
и потом создать ссылку в правильной папке режима запуска, желательно в самом скрипте указать от каких сервисов он зависит, чтобы порядковый номер сервиса был установлен утилиткой сервисов правильно. ( посмотри в другие сервисы из /etc/rc.d )

немного сумбурно, но если вчитаться, врубиться можно smile
и есть доля логики.


удачи.





Автор: Dragon 22.12.2006, 23:48
Демон, это приложение которое делает следующие вещи:
1. Форкает себя
2. Записывает свой .pid файл для скриптов конфигурации
3. Берет новый ID сессии
4. Закрывает все потоки ввода-вывода
5. Делает себе chroot("/") - у демона нет рабочей папки, все пути - только абсолютные. Конфиги держит или в известном и гарантированно существующем месте, вроде /etc/<daemon>.conf - или путь к файлу предварительно вкомпилируется на этапе конфигурирования в Autoconf/Automake проекте.
6. Устанавливает ВСЕ обработчики сигналов - для release сборок перехватываются также SIGSEGV и SIGFPE - юзеров очень огорчает вид корок, посему лучше неопределенное сообщение в логе и дополнительная отладка в подходящих условиях, чем убеждение юзверей в том что это иногда случается smile) Единственное что не стоит пытаться продолжать выполнение после таких сигналов smile
Не стоит забывать про SIGHUP - он часто используется для перегрузки конфига, это удобно: killall -1 testd
7. Входит в главный цикл (обычный цикл со sleep() или accept() и т.п.) 

Вот шаблон "культурного" демона:

Код

#include <errno.h>

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

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

#include <signal.h>
#include <syslog.h>

#include <unistd.h>

const char *pidFileName = "/var/run/testd.pid";

void sigTerminate(int)
{
    syslog(LOG_INFO, "Terminating test daemon...");
    exit(EXIT_SUCCESS);
}

void sigHup(int)
{
    syslog(LOG_INFO, "Reloading configuration...");
    // ...
}

#if !defined(_DEBUG)
void sigFloatingPointException(int)
{
    syslog(LOG_ERR, "Floating point exception raised in test daemon!");
    exit(EXIT_FAILURE);
}

void sigSegmentationFault(int)
{
    syslog(LOG_ERR, "Segmentation fault in test daemon!");
    exit(EXIT_FAILURE);
}
#endif

int main(int argc, char *argv[])
{
    // Read and parse configuration file
    syslog(LOG_INFO, "Starting test daemoni!!!");
    
    // //////////////////////// //////////////////////// //////////////////////// //////////////////////// //////////////////////// //////////////////////
    // Fork off the parent process
    pid_t pid = fork();
    if(pid < 0)            exit(EXIT_FAILURE);    
    else    if(pid > 0)        
    {
        // Create PID file of child process
        FILE *pPidFile = fopen(pidFileName, "w");
        if(pPidFile == NULL)    syslog(LOG_WARNING, "Cannot create PID file: %s", pidFileName);
        else
        {
            fprintf(pPidFile, "%d\n", pid);
            fclose(pPidFile);
        }
        
        exit(EXIT_SUCCESS);
    }
    
    // Change file mode mask
    umask(0);
    
    // Create a new SID for the child process
    pid_t sid = setsid();
    if(sid < 0)            exit(EXIT_FAILURE);
    
    // Change the current working directory
    if((chdir("/")) < 0)    exit(EXIT_FAILURE);
        
    // Close out the standard file descriptors
    close(STDIN_FILENO);    
    close(STDOUT_FILENO);    
    close(STDERR_FILENO);
    
    // Set signal handlers
    signal(SIGTERM, sigTerminate);
    signal(SIGHUP, sigHup);

#if !defined(_DEBUG)
    signal(SIGSEGV, sigSegmentationFault);
    signal(SIGFPE, sigFloatingPointException);
#else
    signal(SIGSEGV, SIG_IGN);
    signal(SIGFPE, SIG_IGN);
#endif

    signal(SIGCHLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
    signal(SIGABRT, SIG_IGN);
    signal(SIGTSTP, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGTTOU, SIG_IGN);
    
    // Daemon main loop
    while(true)
    {
        
        usleep(10000000);
    }
    
    exit(EXIT_SUCCESS);
}

Автор: MAKCim 23.12.2006, 10:42
Dragon
SIGSEGV даже перехватывать не рекомендуется - он явно вызван ошибкой при работе с памятью, т. е велика вероятность того, дальнейшие действия зависят от незавершенной операцией над ней (памятью) 
кроме того использовать signal() - тоже уставревший способ, имхо, лучший способ - sigaction()

Автор: kirjanov 23.12.2006, 11:58
sigaction() - да, эт точно, POSIX так рекомендует обрабатывать сигналы

Автор: Dragon 23.12.2006, 18:45
signal()/sigaction() - не суть важно, в принципе. По поводу SIGSEGV, он может быть и не ошибкой при работе с памятью, а ошибкой при работе с юзером, который накарлякал kill -11 <pid> за что по голове ему надо бы, но...

В общем, доверять ему мы не можем, корку генерировать в release конфигурациях тоже не стоит - посему мы перехватываем SIGSEGV, а т.к. сделать ничего не можем, логируемся и выходим, чтобы враг не восторжествовал smile)

По поводу безопасности работы с памятью в случае возникновения SIGSEGV... В общем так, мы могли бы просто ограничиться вызовом exit(EXIT_FAILURE), что безопасно по определению. Логирование может привести к проблемам, т.к. даже та константная строка вроде "SIGSEGV raised" может быть перебита, хоть это и маловероятно, т.к. статические переменные находятся по более ранним адресам в памяти, чем динамические. Хуже конечно, если будет уничтожена секция кода smile

Ладно, на практике этого никогда не возникало, хотя вероятность циклических вызовов SIGSEGV конечно неприятна, но это не то, что не дает мне спать спокойно smile)

P.S. Некоторые функции, например mprotect() подразумевают выброс SIGSEGV даже в случае если "фактического" fault'а не было smile

Автор: MAKCim 24.12.2006, 11:36
Цитата

Логирование может привести к проблемам

вот и я про то же
а смысл перехвата для последующего вызова exit() я не вижу
ну да ладно

Автор: Dragon 24.12.2006, 18:37
Единственный смысл такого перехвата - это чтобы core файлы не появлялись в результате "unhandled signal/exception".

Автор: zabivator 24.12.2006, 23:49
А не проще ли заюзать Qt Service? Сразу получаем службу для венды и демон для линя)

Автор: MAKCim 25.12.2006, 10:48
zabivator
Зачем нам M$, если мы пишем демона и не более

Автор: kirjanov 25.12.2006, 19:21
Код

/*
 * The mother of all processes.
 */
int
main(int argc, char *argv[])
{
        int c;
        struct sigaction sa;
        sigset_t mask;

        /* Dispose of random users. */
        if (getuid() != 0) {
                (void)fprintf(stderr, "init: %s\n", strerror(EPERM));
                exit (1);
        }

        /* System V users like to reexec init. */
        if (getpid() != 1) {
                (void)fprintf(stderr, "init: already running\n");
                exit (1);
        }

        /*
         * Note that this does NOT open a file...
         * Does 'init' deserve its own facility number?
         */
        openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);

        /*
         * Create an initial session.
         */
        if (setsid() < 0)
                warning("initial setsid() failed: %m");

        /*
         * Establish an initial user so that programs running
         * single user do not freak out and die (like passwd).
         */
        if (setlogin("root") < 0)
                warning("setlogin() failed: %m");

        /*
         * This code assumes that we always get arguments through flags,
         * never through bits set in some random machine register.
         */
        while ((c = getopt(argc, argv, "sf")) != -1)
                switch (c) {
                case 's':
                        requested_transition = single_user;
                        break;
                case 'f':
                        runcom_mode = FASTBOOT;
                        break;
                default:
                        warning("unrecognized flag '-%c'", c);
                        break;
                }

        if (optind != argc)
                warning("ignoring excess arguments");

        /*
         * We catch or block signals rather than ignore them,
         * so that they get reset on exec.
         */
        handle(badsys, SIGSYS, 0);
        handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
            SIGBUS, SIGXCPU, SIGXFSZ, 0);
        handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, SIGUSR1,
            SIGUSR2, 0);
        handle(alrm_handler, SIGALRM, 0);
        sigfillset(&mask);
        delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
            SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGUSR1, SIGUSR2,
            SIGTSTP, SIGALRM, 0);
        sigprocmask(SIG_SETMASK, &mask, NULL);
        memset(&sa, 0, sizeof sa);
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sa.sa_handler = SIG_IGN;
        (void) sigaction(SIGTTIN, &sa, NULL);
        (void) sigaction(SIGTTOU, &sa, NULL);

        /*
         * Paranoia.
         */
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);

        /*
         * Start the state machine.
         */
        transition(requested_transition);

        /*
         * Should never reach here.
         */
        exit(1);
}

из OpenBSD CVS - вполне туториал  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)