Creating Linux Daemon Processes-Free Linux Device Drivers Course In Hyderabad

PREV_LECNEXT_LEC

Creating Linux Daemon Processes
Step by step: how creating daemon processes in Linux actually works, from double-forking to session isolation

Every background service on a Linux system — your SSH server, your cron scheduler, your Bluetooth stack — started life as an ordinary process that deliberately detached itself from the terminal that launched it. That process is called a daemon, and this lecture, part of EmbeddedPathashala’s free embedded Linux course, covers creating daemon processes in Linux the correct, modern way.

This builds directly on the previous two lectures in this series: process termination in Linux and the exec() family. Daemonizing a process is really just those same primitives — fork, exit, and session management — combined in a specific, well-tested sequence.

What You Will Learn

What actually defines a daemon The classic double-fork sequence setsid() and session isolation Redirecting stdin/stdout/stderr Using daemon(3) vs writing it by hand How systemd changes the picture

Prerequisites

You should understand fork(), process termination in Linux (zombies, wait()), and basic file descriptor operations (open(), dup2(), close()) before this lecture — all covered earlier in this course.

What Makes a Process a Daemon

A daemon is simply a process that runs in the background, is not attached to any controlling terminal, and is typically re-parented to PID 1 (init or systemd) rather than to whatever shell originally launched it. There’s no special kernel flag that marks a process as “a daemon” — it’s a set of conventions a process follows about how it detaches itself, and those conventions are what this lecture walks through.

The Daemonizing Sequence

Turning an ordinary process into a well-behaved daemon follows four steps:

  1. Fork, then exit the parent. The child becomes an orphan and gets re-parented to init/systemd, which will reap it when it eventually exits.
  2. Call setsid() in the child. This creates a brand-new session with the child as its sole member and session leader, detaching it from any controlling terminal.
  3. Change the working directory to /. This avoids holding a lock on whatever directory the daemon was launched from, which would otherwise prevent it from being unmounted.
  4. Redirect stdin, stdout, and stderr to /dev/null (or to log files), so there’s no input to read and no output that could unexpectedly land on someone else’s terminal.
Daemonizing a Process
Original process calls fork() Parent process exits immediately Child is orphaned — re-parented to init/systemd Child calls setsid() — becomes leader of a new session Child changes working directory to / Child closes fd 0, 1, 2 and reopens them against /dev/null Child is now a fully detached daemon

Writing It by Hand

Here’s an original demo, ep_daemonize.c, that walks through every step explicitly so you can see exactly what daemon(3) does under the hood before relying on the library function:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>

static void ep_daemonize(void)
{
    pid_t pid = fork();

    if (pid < 0)
        exit(1);
    if (pid > 0)
        exit(0);           /* parent exits, child continues */

    if (setsid() < 0)
        exit(1);            /* detach from controlling terminal */

    if (chdir("/") < 0)
        exit(1);

    /* redirect standard fds to /dev/null */
    int fd = open("/dev/null", O_RDWR);
    if (fd >= 0) {
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
        if (fd > STDERR_FILENO)
            close(fd);
    }
}

int main(void)
{
    ep_daemonize();

    openlog("ep_daemonize_demo", LOG_PID, LOG_DAEMON);
    syslog(LOG_INFO, "ep_daemonize_demo started, pid=%d", getpid());

    while (1) {
        syslog(LOG_INFO, "ep_daemonize_demo heartbeat");
        sleep(30);
    }

    return 0;
}
$ gcc -Wall -o ep_daemonize_demo ep_daemonize_demo.c
$ ./ep_daemonize_demo
$ ps -o pid,ppid,sid,tty,cmd -C ep_daemonize_demo
  PID   PPID    SID TT       CMD
48901      1  48901 ?        ./ep_daemonize_demo
$ journalctl -t ep_daemonize_demo -n 2
ep_daemonize_demo started, pid=48901
ep_daemonize_demo heartbeat

Notice the shell prompt returns immediately — the parent already exited — and ps confirms the child’s PPID is 1 and its controlling terminal (TT) is ?, meaning none.

Letting the Library Do It: daemon(3)

Everything in ep_daemonize() above is exactly what the standard library’s daemon(3) function does for you:

int daemon(int nochdir, int noclose);

Pass 0 for nochdir to still change directory to /, and 0 for noclose to still redirect the standard file descriptors to /dev/null. In new code, prefer daemon(3) over hand-rolling the sequence — it’s had decades of edge cases ironed out.

ApproachControlRecommended For
Hand-written double forkFull control over every stepLearning, or unusual daemonizing requirements
daemon(3)Standard behavior, minimal codeMost traditional daemons
systemd unit with Type=simpleNo manual daemonizing needed at allModern services on systemd-based distributions

How systemd Changes This

On a modern systemd-managed system, most services don’t need to daemonize themselves at all. A unit file with Type=simple tells systemd to treat the process you launch as the service directly — systemd handles the process supervision, logging (via its own journal), and restart-on-crash behavior that a hand-rolled daemon would otherwise need to implement itself. Daemonizing by hand is still relevant for portability, for daemons that must run outside systemd, and simply for understanding what a service manager is doing on your behalf.

Common Mistakes and Troubleshooting

MistakeSymptomFix
Forgetting setsid()Daemon still receives terminal signals (e.g. SIGHUP on logout)Always call setsid() after the first fork, before doing anything else
Not redirecting stdout/stderrDaemon output corrupts whatever terminal happens to reuse those file descriptorsRedirect to /dev/null or a real log file, never leave them attached
Staying in the launch directoryA mount point can’t be unmounted while the daemon holds a reference to itchdir("/") as part of daemonizing
Skipping the parent’s exitThe original shell blocks waiting for a process that never returnsThe parent must exit immediately after forking

Best Practices

  • On systemd systems, prefer a proper unit file over hand-rolled daemonizing where possible.
  • Log through syslog(3) or the systemd journal rather than writing to arbitrary files.
  • Write a PID file (or let systemd track the PID) so administrators and monitoring tools can find your daemon.
  • Handle SIGTERM gracefully so the daemon can shut down cleanly instead of being SIGKILLed.

Performance consideration: daemonizing itself is a one-time cost at startup and has no runtime performance impact — don’t over-optimize this path.

Security consideration: daemons often run with elevated privileges; drop unnecessary privileges (via setuid()/setgid()) after any privileged setup is complete, and never leave a daemon’s stdin attached to anything an unprivileged user could write to.

Real-World Use Cases

SSH servers, cron, syslog daemons, database servers, and countless embedded Linux services (including Bluetooth daemons like bluetoothd) all follow this same detachment pattern, whether they implement it by hand or rely on daemon(3) or their init system’s supervision model.

Summary and Key Takeaways

  • A daemon is defined by convention: backgrounded, no controlling terminal, re-parented to init.
  • The classic sequence is fork-and-exit-parent, setsid(), chdir("/"), redirect standard fds.
  • daemon(3) implements this sequence for you and is preferred over hand-rolling it in new code.
  • On systemd systems, a Type=simple unit often removes the need to daemonize manually at all.

Conclusion

Creating daemon processes in Linux is really just process termination, forking, and session management from earlier in this series, applied in a specific well-known order. Whether you write the sequence by hand, call daemon(3), or hand the whole problem to systemd, understanding what’s actually happening underneath means you can debug a misbehaving service instead of just restarting it and hoping. That wraps up the process-management arc of this free Linux kernel development course — next we move on to a new subsystem.

Frequently Asked Questions

Why does daemonizing require forking twice in some implementations?

A single fork plus setsid() is enough to detach from a terminal. A second fork is sometimes added so the daemon can never re-acquire a controlling terminal by accident, since only a session leader can do that, and the second fork ensures the daemon isn’t one.

Do I still need to daemonize manually under systemd?

Usually not. A unit file with Type=simple treats your foreground process as the service directly; systemd handles backgrounding and supervision for you.

Why redirect stdin/stdout/stderr to /dev/null instead of just closing them?

Leaving fd 0, 1, 2 closed risks a later open() call accidentally reusing one of those numbers for an unrelated file, which can silently corrupt data. Redirecting to /dev/null keeps the slots occupied safely.

What’s the difference between a daemon and a zombie process?

They’re unrelated states. A daemon is a normal, running, detached background process. A zombie is a process that has already exited and is waiting for its parent to collect its exit status.

Can a daemon be a child of a process other than init?

Yes, if a “subreaper” process is in place (see prctl(2) PR_SET_CHILD_SUBREAPER), orphaned children can be re-parented to that subreaper instead of PID 1 — this is how some container runtimes track processes.

Continue the Free Linux Kernel Development Course

This lecture is part of EmbeddedPathashala’s free embedded systems course covering Linux process management from first principles.

Browse the Full Course Join the Community

PREV_LECNEXT_LEC

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *