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
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:
- 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. - 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. - 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. - 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.
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.
| Approach | Control | Recommended For |
|---|---|---|
| Hand-written double fork | Full control over every step | Learning, or unusual daemonizing requirements |
daemon(3) | Standard behavior, minimal code | Most traditional daemons |
systemd unit with Type=simple | No manual daemonizing needed at all | Modern 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
| Mistake | Symptom | Fix |
|---|---|---|
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/stderr | Daemon output corrupts whatever terminal happens to reuse those file descriptors | Redirect to /dev/null or a real log file, never leave them attached |
| Staying in the launch directory | A mount point can’t be unmounted while the daemon holds a reference to it | chdir("/") as part of daemonizing |
| Skipping the parent’s exit | The original shell blocks waiting for a process that never returns | The 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
SIGTERMgracefully so the daemon can shut down cleanly instead of beingSIGKILLed.
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=simpleunit 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
2 Comments