thecodingidiot.com

The TerminalRunning and Stopping Things

Running and Stopping Things

A process[1] is a running program. Everything you launch from the shell is a process. Understanding how to manage processes — run them in the background, stop them, kill them, and chain them conditionally — is not optional knowledge. It will come up every day.

Background and foreground

./long_render &

The & at the end of a command runs it in the background. The shell prints the job number and PID, then returns the prompt immediately. The process keeps running.

[1] 48291
 
jobs            # list background jobs for this shell session
fg              # bring the most recent background job to the foreground
fg %1           # bring job number 1 to the foreground

Ctrl-Z suspends the foreground process (pauses it) and puts it in the background. Then fg to resume it, or bg to let it keep running in the background.

./render        # starts in foreground
^Z              # Ctrl-Z: suspend
[1]+  Stopped  ./render
bg              # resume in background

Interrupting and killing

Ctrl-C sends SIGINT (signal 2) to the foreground process. Most programs treat this as "stop what you are doing and exit cleanly." It is the polite way to stop a process.

kill 48291          # send SIGTERM (15) — ask the process to exit
kill -9 48291       # send SIGKILL — force terminate, immediately
kill -SIGINT 48291  # send SIGINT by name

kill sends a signal to a process by PID. SIGTERM (the default) asks the process to terminate; the process can catch it and clean up. SIGKILL cannot be caught or ignored — the kernel terminates the process immediately. Use -9 only when the process is genuinely unresponsive.

Finding processes

ps aux                          # all running processes
ps aux | grep 'render'          # find a specific process by name
pgrep render                    # print PIDs of processes named "render"
pkill render                    # kill all processes named "render"

ps aux produces a lot of output. Pipe it through grep to find what you are looking for.

Exit codes

Every process exits with a number. 0 means success. Anything else means failure. The shell stores the exit code of the last command in $?:

ls /nonexistent
echo $?             # prints 2 (or similar non-zero)
 
ls /tmp
echo $?             # prints 0

Conditional chaining

Exit codes make && and || useful:

gcc main.c -o main && ./main

&& runs the right side only if the left side exited with 0. This compiles and runs only if compilation succeeded. It is the shell equivalent of "if this worked, then do that."

./build.sh || echo "build failed"

|| runs the right side only if the left side failed (non-zero exit). Use it for fallbacks and error reporting in scripts.

mkdir output && cp *.c output/ && echo "done" || echo "failed"

Chain as many commands as you need. If any one fails, the rest do not run and || catches the failure. If everything succeeds, echo "done" exits with 0 and the || leg is skipped.

In c05/03 you will build the internals of | in C. That chapter is built on fork, exec, dup2, and waitpid — the system calls that create processes, replace them with a new program, rewire their file descriptors, and wait for them to finish. Exit codes are not a shell convention in that chapter; they are the return value of waitpid. SIGINT is not a keyboard shortcut; it is a signal your shell sends to a process group. Everything on this page is the vocabulary that makes that chapter readable.

Footnotes

  1. Process (computing) - Wikipedia