Processes and Services
Who am I?
Infrastructure Engineer @ Yelp
Formerly Berkeley / OCF
Chris Kuehl
ckuehl@ocf.berkeley.edu
What is this lecture about?
Chapter 1: Processes
Chapter 2: Init systems & services
CHAPTER ONE
Processes
What is a process?
It’s just ~one running program.
Each process has its own memory space, threads, code, etc.
Process hierarchy
Each process has one parent, typically the process that started it.
Processes can have many children.
Processes vs threads
Both methods of running code “at the same time”
A process is a collection of one or more threads
Threads are all part of the same “program”, share the same memory
Processes communicate over some interface
So why is my laptop running 6000 chrome processes?
How are processes created?
A process “forks” into two separate processes using the fork syscall
The parent keeps its PID, the child gets a new PID.
How many “hello”s get printed?
int main(void) {� fork();� printf("hello: %d\n", getpid());�}
How many “hello”s get printed?
int main(void) {� fork();� printf("hello: %d\n", getpid());�}
int main(void) {� fork();� printf("hello: %d\n", getpid());�}
int main(void) {� fork();� printf("hello: %d\n", getpid());�}
Process ID 1000
Process ID 1000
Process ID 1001
How many “hello”s get printed?
int main(void) {� fork();� printf("hello: %d\n", getpid());�}
int main(void) {� fork();� printf("hello: %d\n", getpid());�}
int main(void) {� fork();� printf("hello: %d\n", getpid());�}
$ ./foo
hello: 27951
hello: 27952
Process ID 1000
Process ID 1000
Process ID 1001
How many “hello”s get printed?
int main(void) {� fork();
fork();
fork();� printf("hello: %d\n", getpid());�}
How many “hello”s get printed?
int main(void) {� fork();
fork();
fork();� printf("hello: %d\n", getpid());�}
$ ./foo
hello: 27956
hello: 27958
hello: 27959
hello: 27957
hello: 27961
hello: 27960
hello: 27963
hello: 27962
How to make the child do something else?
pid_t result = fork();�if (result > 0) {� /* parent process */� sleep(100);
} else if (result == 0) {� /* child process */� sleep(10);
} else { /* error */ }
Process tree after start:
child exits first
What happens when processes exit?
pid_t result = fork();�if (result > 0) {� /* parent process */� sleep(100);
} else if (result == 0) {� /* child process */� sleep(10);
} else { /* error */ }
Process tree after start:
After 10 seconds:
????
When a process exits, it temporarily becomes a zombie:
Zombie processes
pid_t result = fork();�if (result > 0) {� /* parent process */� sleep(100);
} else if (result == 0) {� /* child process */� sleep(10);
} else { /* error */ }
(defunct means zombie)
pid_t result = fork();�if (result > 0) {� /* parent process */� sleep(100);
} else if (result == 0) {� /* child process */� exit(123);
} else { /* error */ }
Processes always exit with an “exit code” (int between 0 and 255)
Typically, 0 is success, anything else is an error
Parents need to wait() on children
pid_t result = fork();�if (result > 0) {� /* parent process */� int status;� wait(&status);� printf("%d\n", WEXITSTATUS(status));�} else if (result == 0) {� /* child process */� sleep(10);
exit(123);�} else { /* error */ }
(aka reap)
Parents need to wait() on children
pid_t result = fork();�if (result > 0) {� /* parent process */� int status;� wait(&status);� printf("%d\n", WEXITSTATUS(status));�} else if (result == 0) {� /* child process */� sleep(10);
exit(123);�} else { /* error */ }
Process tree after start:
After 10 seconds:
Prints out:
(aka reap)
What if the parent exits first?
pid_t result = fork();�if (result > 0) {� /* parent process */
sleep(1);� exit(0);�} else if (result == 0) {� /* child process */� sleep(10);
exit(123);�} else { /* error */ }
Process tree after start:
After 1 second:
Orphaned processes are re-parented under PID 1
parent
child
child
Process signaling
Simple messages to other processes.
Typically send using kill or pkill, e.g. kill -9 <pid> or pkill -HUP apache2.
How do processes communicate?
All kinds of inter-process communication (IPC):
Working with processes: ps
Nobody remembers what the arguments to ps mean, just memorize a few
ps aux
ps xawuf
ps fux
Working with processes: ps
ps -o pid,uname,comm
Working with processes: pstree
Working with processes: pgrep & pkill
(pgrep is especially useful for composing)
Working with processes: htop
(makes pretty screenshots)
CHAPTER TWO
Init systems & services
What is an init system?
The first process launched (PID 1), lives forever
Core responsibilities:
Why does it have to reap orphaned zombies?
pid_t result = fork();�if (result > 0) {� /* parent process */
sleep(1);� exit(0);�} else if (result == 0) {� /* child process */� sleep(10);
exit(123);�} else { /* error */ }
Process tree after start:
After 1 second:
Orphaned processes are re-parented under PID 1
parent
child
child
What does a bare-bones init system look like?
/bin/bash can work in a pinch�
What about in code?
Bare-bones init
pid_t result = fork();� if (result > 0) {
/* parent process */� while (wait(NULL) != result) {}� } else if (result == 0) {� /* child process */� execl("/bin/cowsay", "cowsay"); // any process here� } else { /* error */ }
(2) reaps all children, even those it didn’t spawn (orphans)
(1) spawns some useful process
Traditional init systems
Even traditional init systems do more than the minimum required.
Typical extra responsibilities:
Different kinds of processes
Not strict definitions:
How do we control services?
Lots of ways…
Let’s look at how init systems do it...
“Old”-style init scripts
Turing-complete, actual scripts:�/etc/init.d/{service} {action}
Each script is responsible for keeping track of processes.
What does that look like?
“Old”-style init scripts
/etc/init.d/nginx on ubuntu
“Old”-style init scripts
Turing-complete, actual scripts:�/etc/init.d/{service} {action}
Each script is responsible for keeping track of processes.
What would modern service supervision look like?
Let’s look at some examples...
Traditional | Modern |
Scripts that can do anything | Restricted, declarative config files |
Store state using PID files | Store state in the init system |
Inconsistent actions (each init script its own CLI) | Consistent actions via common CLI |
Difficult to control ordering | Ordering via dependencies or events |
Modern init system: upstart
Developed by Canonical (Ubuntu), used for a few releases
Event-based (services subscribe to events)
Parallel start and stop
Way easier to write than�traditional init scripts
Modern init system: upstart
description "my cool server"��start on filesystem and started networking�stop on shutdown��exec /usr/bin/my-cool-server
/etc/init/my-cool-server.conf
Modern init system: systemd
Originally comes from Fedora-land
Dependency-based
Parallel start and stop
Way easier to write than traditional init scripts
Modern init system: systemd
Goals:
Ended up reimplementing syslog, cron, networking, GRUB, ConsoleKit, udev
UNIX philosophy???
Turns out they’re controversial
Turns out they’re controversial
Turns out they’re controversial
In practice, systemd has won
...
systemctl status
systemctl start/stop
systemctl enable/disable
systemctl (no arguments)
journalctl
View recent logs from a unit:�journalctl -u $unit -e
Tail logs from a unit:�journalctl -u $unit -f
Writing systemd unit files
[Unit]
Description=my cool server
Requires=network-online.target opt.mount
After=network-online.target opt.mount
[Service]
ExecStart=/usr/bin/my-cool-service
[Install]
WantedBy=multi-user.target
/etc/systemd/system/my-cool-server.service
Takeaways (aka my opinions)