Helsingin yliopisto Tietojenkäsittelytieteen laitos
 

Tietojenkäsittelytieteen laitos

Tietoa laitoksesta:

 
Helsingin yliopisto / Tietojenkäsittelytieteen laitos / Copyright © 2001 Jan Lindström. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

10. Daemon ohjelmointi


Daemon-prosessi yleensä määritellään taustaprosessiksi, joka ei kuulu terminaali-istuntoon. Useita systeemipalveluita suoritaa daemonit, esimerkiksi tietoliikennepalvelut, tulostus jne.

Yksinkertainen taustakäynnistys ei riitä näille pitkäkestoisille ohjelmille. Taustakäynnistys ei oikein irroita prosessia terminaali-istunnosta, joka sen käynnisti. Yleinen tapa käynnistää daemon on joko käynnistää se manuaalisesti tai rc-skriptistä. Daemonin odotetaan itse laittavansa itsensä taustalle.

Seuraavat toimet tehdään daemon-prosessissa:

Käytännössä mikään näistä ei ole välttämätöntä (tai suositeltavaa) jos daemon käynistetään inetd:ssä. Vain chdir() ja umask() kohdat säilyvät hyödyllisinä.

Esimerkkikoodi daemon.c.



#include < unistd.h>
#include < stdlib.h>
#include < fcntl.h>
#include < signal.h>
#include < sys/types.h>
#include < sys/wait.h>
#include < errno.h>

#define TCP_PORT 8888

/* closeall() -- close all FDs >= a specified value */

void closeall(int fd)
{
    int fdlimit = sysconf(_SC_OPEN_MAX);

    while (fd < fdlimit)
      close(fd++);
}

/* daemon() - detach process from user and disappear into the background
 * returns -1 on failure, but you can't do much except exit in that case
 * since we may already have forked. This is based on the BSD version,
 * so the caller is responsible for things like the umask, etc.
 */

/* believed to work on all Posix systems */

int daemon(int nochdir, int noclose)
{
    switch (fork())
    {
        case 0:  break;
        case -1: return -1;
        default: _exit(0);          /* exit the original process */
    }

    if (setsid() < 0)               /* shoudn't fail */
      return -1;

    /* dyke out this switch if you want to acquire a control tty in */
    /* the future -- not normally advisable for daemons */

    switch (fork())
    {
        case 0:  break;
        case -1: return -1;
        default: _exit(0);
    }

    if (!nochdir)
      chdir("/");

    if (!noclose)
    {
        closeall(0);
        open("/dev/null",O_RDWR);
        dup(0); dup(0);
    }

    return 0;
}

/* fork2() -- like fork, but the new process is immediately orphaned
 *            (won't leave a zombie when it exits)
 * Returns 1 to the parent, not any meaningful pid.
 * The parent cannot wait() for the new process (it's unrelated).
 */

/* This version assumes that you *haven't* caught or ignored SIGCHLD. */
/* If you have, then you should just be using fork() instead anyway.  */

int fork2()
{
    pid_t pid;
    int rc;
    int status;

    if (!(pid = fork()))
    {
        switch (fork())
        {
          case 0:  return 0;
          case -1: _exit(errno);    /* assumes all errnos are <256 */
          default: _exit(0);
        }
    }

    if (pid < 0 || waitpid(pid,&status,0) < 0)
      return -1;

    if (WIFEXITED(status))
      if (WEXITSTATUS(status) == 0)
        return 1;
      else
        errno = WEXITSTATUS(status);
    else
      errno = EINTR;  /* well, sort of :-) */

    return -1;
}

void errexit(const char *str)
{
    syslog(LOG_INFO, "%s failed: %d (%m)", str, errno);
    exit(1);
}

void errreport(const char *str)
{
    syslog(LOG_INFO, "%s failed: %d (%m)", str, errno);
}

/* the actual child process is here. */

void run_child(int sock)
{
    FILE *in = fdopen(sock,"r");
    FILE *out = fdopen(sock,"w");
    int ch;

    setvbuf(in, NULL, _IOFBF, 1024);
    setvbuf(out, NULL, _IOLBF, 1024);

    while ((ch = fgetc(in)) != EOF)
      fputc(toupper(ch), out);

    fclose(out);
}

/* This is the daemon's main work -- listen for connections and spawn */

void process()
{
    struct sockaddr_in addr;
    int addrlen = sizeof(addr);
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    int flag = 1;
    int rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                        &flag, sizeof(flag));

    if (rc < 0)
      errexit("setsockopt");

    addr.sin_family = AF_INET;
    addr.sin_port = htons(TCP_PORT);
    addr.sin_addr.s_addr = INADDR_ANY;

    rc = bind(sock, (struct sockaddr *) &addr, addrlen);
    if (rc < 0)
      errexit("bind");

    rc = listen(sock, 5);
    if (rc < 0)
      errexit("listen");

    for (;;)
    {
        rc = accept(sock, (struct sockaddr *) &addr, &addrlen);

        if (rc >= 0)
          switch (fork2())
          {
            case 0:  close(sock); run_child(rc); _exit(0);
            case -1: errreport("fork2"); close(rc); break;
            default: close(rc);
          }
    }
}

int main()
{
    if (daemon(0,0) < 0)
    {
        perror("daemon");
        exit(2);
    }

    openlog("test", LOG_PID, LOG_DAEMON);

    process();

    return 0;
}


Jan Lindström (Jan.Lindstrom@cs.Helsinki.FI)