In the print dialog, choose "Save as PDF" as the destination.
Operating Systems 1, Lab 10
Watch, signal and prioritize running programs, then practise the command toolkit a DevOps engineer uses every day.
R, S, D, T, Z in ps and topkill, kill -9, -STOP and -CONTnice and renicesudo, id, chown, grep, sort, tail, cut, tr, sed, diff, find, dd, tar, df, du, free, history, hostname, ifconfig, ping, telnetPart 1
A program in execution
| Type | Also called | Depends on the user? |
|---|---|---|
| Foreground | interactive | yes: it waits for your input |
| Background | non-interactive, automatic | no: it runs on its own |
sleep 300 waits 300 seconds. The & starts it in the background, so the prompt comes back at once. Predict: what does ps list now?
$ sleep 300 &
[1] 953$ ps
PID TTY TIME CMD
942 pts/0 00:00:00 bash
953 pts/0 00:00:00 sleep
954 pts/0 00:00:00 ps| State | Letter | Meaning |
|---|---|---|
| Running | R | running, or ready to run |
| Sleeping (interruptible) | S | waiting for a resource; wakes up to handle signals |
| Sleeping (uninterruptible) | D | waiting for a resource; does not wake for signals |
| Stopped | T | received a stop signal |
| Zombie | Z | dead, but its entry is still in the process table |
Part 2
A snapshot, and a live view
ps -u adds %CPU, %MEM and STAT (the state). Predict the state letter of each row.
$ ps -u
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 942 0.5 0.0 7608 4440 pts/0 Ss 08:03 0:00 bash
student 953 0.0 0.0 5688 2148 pts/0 S 08:03 0:00 sleep 300
student 955 0.0 0.0 10888 4352 pts/0 R+ 08:03 0:00 ps -uPlain ps shows only this terminal; -A lists all processes, even ones not attached to it
$ ps -A
PID TTY TIME CMD
1 ? 00:00:00 bash
7 ? 00:00:00 sleep
43 pts/0 00:00:00 bash
53 pts/0 00:00:00 sleep
54 pts/0 00:00:04 yes
56 pts/0 00:00:00 ps$ ps -A | grep sleep
7 ? 00:00:00 sleep
53 pts/0 00:00:00 sleeptop refreshes the table in real time. We started sleep 300 & and yes > /dev/null & (yes prints y forever; > /dev/null throws the output away). Predict S and %CPU for both.
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
54 student 20 0 5688 1976 1860 R 90.0 0.0 0:03.76 yes
53 student 20 0 5688 2024 1916 S 0.0 0.0 0:00.01 sleep| Column | Meaning |
|---|---|
PID, USER | the process ID, and the user who owns it |
PR, NI | the scheduling priority, and the nice value (the fourth column) |
VIRT, RES, SHR | virtual memory, physical memory, memory shared with other processes |
S | the state: D, R, S, T or Z |
%CPU, %MEM, TIME+ | share of CPU and RAM used, total CPU time consumed |
Part 3
End, stop and continue a process
kill -L lists the signals by number (the first lines):
$ kill -L
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM| Command | Signal | Effect |
|---|---|---|
kill PID | 15 SIGTERM (the default) | asks the process to end |
kill -9 PID | 9 SIGKILL | for a process that ignores a normal kill |
Predict the second ps.
$ sleep 300 &
[1] 84
$ kill 84$ ps
PID TTY TIME CMD
72 pts/0 00:00:00 bash
91 pts/0 00:00:00 ps
[1]+ Terminated sleep 300stubborn.sh ignores SIGTERM. Predict each ps.
#!/bin/bash
# ignores the polite kill signal (15, SIGTERM)
trap '' TERM
while true; do
sleep 1
done$ ./stubborn.sh &
[1] 92
$ kill 92
$ ps
PID TTY TIME CMD
72 pts/0 00:00:00 bash
92 pts/0 00:00:00 stubborn.sh
104 pts/0 00:00:00 sleep
105 pts/0 00:00:00 ps
$ kill -9 92
[1]+ Killed ./stubborn.shkill -L also lists 19 SIGSTOP and 18 SIGCONT
$ kill -STOP 130
$ ps -u
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 130 0.0 0.0 5688 2024 pts/0 T 08:06 0:00 sleep 300
[1]+ Stopped sleep 300
$ kill -CONT 130
$ ps -u
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 130 0.0 0.0 5688 2024 pts/0 S 08:06 0:00 sleep 300sleep 300 is stopped (T). We send the default kill. Predict: is it gone? Then we send -CONT.
$ sleep 300 &
[1] 366
$ kill -STOP 366
$ kill 366$ ps -u
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 366 0.0 0.0 5688 112 pts/0 T 08:39 0:00 sleep 300
$ kill -CONT 366
[1]+ Terminated sleep 300(sleep 1 & exec sleep 60) & makes a parent (sleep 60) whose child ends after one second and is never collected. Predict what kill -9 does to the zombie.
$ ps -u
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 188 0.0 0.0 5688 2136 pts/0 S 08:07 0:00 sleep 60
student 189 0.0 0.0 0 0 pts/0 Z 08:07 0:00 [sleep] <defunct>$ kill -9 189
$ ps -u
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 189 0.0 0.0 0 0 pts/0 Z 08:07 0:00 [sleep] <defunct>
$ kill 188
$ ps -u
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 178 0.6 0.0 7608 4276 pts/0 Ss 08:07 0:00 bash
student 204 0.0 0.0 10888 4308 pts/0 R+ 08:07 0:00 ps -u
[1]+ Terminated ( sleep 1 & exec sleep 60 )-o chooses the columns; PPID is the PID of the parent. Another run of the same command:
$ (sleep 1 & exec sleep 60) &
[1] 896
$ ps -o pid,ppid,stat,cmd
PID PPID STAT CMD
889 888 Ss bash
896 889 S sleep 60
897 896 Z [sleep] <defunct>
899 889 R+ ps -o pid,ppid,stat,cmdPart 4
Who gets the CPU first
The priority value of a process is its niceness
$ nice --help | head -4
Usage: nice [OPTION] [COMMAND [ARG]...]
Run COMMAND with an adjusted niceness, which affects process scheduling.
With no COMMAND, print the current niceness. Niceness values range from
-20 (most favorable to the process) to 19 (least favorable to the process).-20 to 19; the default is `0`nice -n [value] [command]renice [value] -p PID$ nice -n 10 sleep 300 &
[1] 32
$ top -b -n 1 -p 32
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
32 student 30 10 5688 1932 1824 S 0.0 0.0 0:00.00 sleep
$ renice 15 -p 32
32 (process ID) old priority 10, new priority 15Every top row in this lab obeys one rule. Predict PR for NI = 10, 15 and 5.
| NI | PR | Seen in |
|---|---|---|
10 | 20 + 10 = 30 | nice -n 10 sleep 300 |
15 | 20 + 15 = 35 | after renice 15 |
5 | 20 + 5 = 25 | after sudo renice 5 (next example) |
The process now has niceness 15. As student, we ask for 5. Predict the answer.
$ renice 5 -p 32renice: failed to set priority for 32 (process ID): Permission denied
$ sudo renice 5 -p 32
[sudo] password for student:
32 (process ID) old priority 15, new priority 5Part 5
Development and operations as one team
Development team
plans, designs, builds
Operations team
tests, implements
Plan
Code
Git (version control)
Build
Maven, Gradle
Test
Selenium
Deploy
Ansible, Docker, Kubernetes
Monitor
Nagios
Git, Selenium, Docker, Nagios, Jenkins. Predict the phase of each.
| Tool | Phase |
|---|---|
| Git | code: version control, a repository of versions merged when required |
| Selenium | test: automation testing |
| Docker | deploy (with Ansible and Kubernetes) |
| Nagios | monitor the product after deployment |
| Jenkins | continuous integration: send code to build and test |
Part 6
Most companies run their servers on Linux; these commands work the same on your VM, on a server and in a container
sudo runs one command with root (superuser) privileges
$ id
uid=1001(student) gid=1001(student) groups=1001(student),27(sudo)
$ sudo groupadd devops
[sudo] password for student:
$ sudo useradd -m deploy
$ sudo usermod -g devops deploy
$ id deploy
uid=1002(deploy) gid=1002(devops) groups=1002(devops)| Command | Does |
|---|---|
sudo useradd -m NAME | add a user (with a home folder) |
sudo groupadd NAME | add a group |
sudo usermod -g GROUP NAME | set the user's primary group |
sudo userdel, groupdel | delete the user, the group |
id -u, -g, -G, -n | user ID, group ID, all groups, names |
chown [OWNER][:[GROUP]] FILE; giving a file away needs sudo
$ ls -l servers.txt
-rw-r--r-- 1 student student 105 Sep 25 08:03 servers.txt
$ sudo chown deploy servers.txt
$ ls -l servers.txt
-rw-r--r-- 1 deploy student 105 Sep 25 08:03 servers.txt
$ sudo chown root:devops servers.txt
$ ls -l servers.txt
-rw-r--r-- 1 root devops 105 Sep 25 08:03 servers.txtapp.log has 10 lines (INFO, WARN, ERROR, and one lowercase info). Predict each number.
$ grep -c ERROR app.log
$ grep info app.log
$ grep -i info app.log
$ grep -c -v INFO app.log| Command | Answer | Why |
|---|---|---|
grep -c ERROR | 3 | -c counts the matching lines |
grep info | 1 line | case matters: only the lowercase line |
grep -i info | 5 lines | -i ignores case |
grep -c -v INFO | 6 | -v keeps lines without the pattern |
ports.txt holds 8080 22 443 5432 80 3306, one per line. Predict sort and sort -n.
$ sort ports.txt
22
3306
443
5432
80
8080$ sort -n ports.txt
22
80
443
3306
5432
8080Logs grow at the bottom, so DevOps reads the end first
$ tail -n 3 app.log
2026-09-25 09:05:44 WARN db1 disk usage 85 percent
2026-09-25 09:06:10 INFO web1 deploy version 2.4 finished
2026-09-25 09:07:02 ERROR web1 out of memory
$ tail -n +9 app.log
2026-09-25 09:06:10 INFO web1 deploy version 2.4 finished
2026-09-25 09:07:02 ERROR web1 out of memory
$ tail -c 20 app.log
web1 out of memory$ cut -c1-2 servers.txt
we
db
we
ca
ba
$ cut -d , -f 1,3 servers.txt
web1,8080
db1,5432
web2,8080
cache1,6379
backup1,22tr reads a pipe, not a file name
$ cat servers.txt | tr a-z A-Z
WEB1,CAIRO,8080,4
DB1,MANSOURA,5432,16
WEB2,ALEXANDRIA,8080,4
CACHE1,CAIRO,6379,8
BACKUP1,MANSOURA,22,32
$ echo "web1 db1 cache1" | tr -s " "
web1 db1 cache1
$ echo "web1 web2 db1" | tr -d 0-9
web web dbPredict what cat servers.txt shows after the sed command.
$ sed 's/8080/9090/' servers.txt
web1,cairo,9090,4
db1,mansoura,5432,16
web2,alexandria,9090,4
cache1,cairo,6379,8
backup1,mansoura,22,32$ cat servers.txt
web1,cairo,8080,4
db1,mansoura,5432,16
web2,alexandria,8080,4
cache1,cairo,6379,8
backup1,mansoura,22,32Predict what each line of the output means.
$ diff config.old config.new
1c1
< port=8080
---
> port=9090
3c3
< debug=true
---
> debug=false
4a5
> timeout=30$ find . -name "*.log"
./app.log
./logs/old/web-0924.log
./logs/web.log
$ ls -i app.log
4456513 app.log
$ find . -inum 4456513
./app.log
$ find . -name "*.log" -ok rm {} \;
< rm ... ./app.log > ? n
< rm ... ./logs/old/web-0924.log > ? n
< rm ... ./logs/web.log > ? y$ dd if=/dev/zero of=disk.img bs=1M count=5
5+0 records in
5+0 records out
5242880 bytes (5.2 MB, 5.0 MiB) copied, 0.013896 s, 377 MB/s
$ dd if=/dev/urandom of=key.bin bs=16 count=1
1+0 records in
1+0 records out
16 bytes copied, 0.000774319 s, 20.7 kB/sc create, t list, x extract, z through gzip, f the archive file
$ tar czf lab10.tar.gz app.log servers.txt notes.txt
$ tar tzf lab10.tar.gz
app.log
servers.txt
notes.txt
$ mkdir restore
$ tar xzf lab10.tar.gz -C restore
$ ls restore
app.log notes.txt servers.txt$ gzip app.log
$ ls -l app.log*
-rw-r--r-- 1 student student 264 Sep 25 10:31 app.log.gz-h: human-readable sizes; du -s: one summary line
$ df -h /
Filesystem Size Used Avail Use% Mounted on
overlay 916G 822G 52G 95% /
$ du -h /var/log
40K /var/log/apt
304K /var/log
$ du -sh /var/log
304K /var/log$ free -m
total used free shared buff/cache available
Mem: 31866 30491 398 471 2276 1375
Swap: 24575 24575 0$ history 5
177 dd if=servers.txt of=upper.txt conv=ucase
178 cat upper.txt
179 free -m
180 free -g
181 history 5$ hostname
ubuntu-lab$ ifconfig -s
Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1500 43 0 0 0 3 0 0 0 BMRU
lo 65536 0 0 0 0 0 0 0 0 LRU-c 4 stops after 4 packets; without it, stop ping with Ctrl+C
$ ping -c 4 google.com
PING google.com (142.251.27.138) 56(84) bytes of data.
64 bytes from cv-in-f138.1e100.net (142.251.27.138): icmp_seq=1 ttl=109 time=47.4 ms
64 bytes from cv-in-f138.1e100.net (142.251.27.138): icmp_seq=2 ttl=109 time=49.8 ms
64 bytes from cv-in-f138.1e100.net (142.251.27.138): icmp_seq=3 ttl=109 time=46.8 ms
64 bytes from cv-in-f138.1e100.net (142.251.27.138): icmp_seq=4 ttl=109 time=46.7 ms
--- google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3003ms
rtt min/avg/max/mdev = 46.666/47.683/49.815/1.263 msA test service listens on port 8080; nothing listens on 9090. Predict both telnet results.
$ telnet localhost 8080
Trying ::1...
Connection failed: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
$ telnet localhost 9090
Trying ::1...
Connection failed: Connection refused
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refusedPart 7
About 30 minutes: three on paper, then tasks in your own VM
About 6 minutes
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 563 0.7 0.0 7608 4436 pts/0 Ss 09:03 0:00 bash
student 571 0.0 0.0 5688 2088 pts/0 S 09:03 0:00 sleep 500
student 572 0.0 0.0 5688 1984 pts/0 T 09:03 0:00 sleep 700
student 579 98.1 0.0 5688 1920 pts/0 R 09:03 0:03 yes
student 580 0.0 0.0 5688 2036 pts/0 S 09:03 0:00 sleep 90
student 581 0.0 0.0 0 0 pts/0 Z 09:03 0:00 [sleep] <defunct>
student 582 0.0 0.0 10888 4476 pts/0 R+ 09:03 0:00 ps -ukill 572? And after kill -CONT 572?572 (T). Using the CPU: 579, yes (R, 98.1%)581 (Z, <defunct>). kill cannot remove it; end its parent, 580 (sleep 90), which ps -o pid,ppid,stat,cmd shows in the PPID columnkill 572: nothing yet, the stopped process holds the signal. kill -CONT 572: it continues and is TerminatedAbout 6 minutes
nice -n 19 sleep 100 & starts PID 615. What are its NI and PR?student: renice 10 -p 615. What happens?sudo renice -20 -p 615. What are NI and PR now?student: nice -n -5 sleep 1. What happens?$ top -b -n 1 -p 615 | tail -2
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
615 student 39 19 5688 2132 2024 S 0.0 0.0 0:00.00 sleep
$ renice 10 -p 615
renice: failed to set priority for 615 (process ID): Permission denied$ sudo renice -20 -p 615
[sudo] password for student:
615 (process ID) old priority 19, new priority -20
$ top -b -n 1 -p 615 | tail -2
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
615 student 0 -20 5688 2132 2024 S 0.0 0.0 0:00.00 sleep
$ nice -n -5 sleep 1
nice: cannot set niceness: Permission deniedAbout 10 minutes. deploy.log:
09:10 OK build web1
09:12 FAIL test web1
09:15 OK build web2
09:16 ok test web2
09:20 FAIL deploy web2
09:25 OK deploy web1FAIL?ok in any case, with line numbersFAIL linesFAIL with RETRY in the output only$ grep -c FAIL deploy.log
2
$ grep -n -i ok deploy.log
1:09:10 OK build web1
3:09:15 OK build web2
4:09:16 ok test web2
6:09:25 OK deploy web1
$ grep FAIL deploy.log | cut -d " " -f 4
web1
web2cut -d " " -f 3 deploy.log: build test build test deploy deploysed 's/FAIL/RETRY/' deploy.log: two lines change, the file does nottail -n 2 deploy.log: the 09:20 and 09:25 linesAbout 8 minutes
app.log and stubborn.sh from the lesson page; run chmod +x stubborn.shsleep 600 & and ./stubborn.sh &; find both PIDs with ps -usleep with -STOP, check T, continue it, then end it with killstubborn.sh: which signal works?nice -n 5 sleep 600 &, read PR and NI in top, then renice 12 itgrep ERROR app.log | cut -d " " -f 4 | sort and check it in the widgetps is a snapshot, top is liveR running, S/D sleeping, T stopped, Z zombiekill sends 15 (SIGTERM, can be ignored); kill -9 sends SIGKILL; -STOP and -CONT pause and resume-20 to 19; PR = 20 + NI; only root may lower itgrep, cut, sort, tail, sed, diff read logs and configs; tar archives; df, du, free, ifconfig, ping, telnet check the machineMahmoud Abas|Linux Commands, Part 2: Processes and DevOps