## ## ####### ######## ## ## ## #######
### ### ## ## ## ## ## ## ## ## ##
#### #### ## ## ## ## ## ## ## ##
## ### ## ## ## ## ## ## ## ## #######
## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ##
## ## ####### ######## ####### ######## #########
+-++-++-++-++-++-+ +-++-++-+ +-++-++-++-++-++-+
|P||r||o||s||e||s| |d||a||n| |D||a||e||m||o||n|
+-++-++-++-++-++-+ +-++-++-+ +-++-++-++-++-++-+
Proses dan Daemon
Requirement:
- Linux
- gcc / g++
Tutorial compiling C code: here
- Peserta mengetahui proses
- Peserta memahami bagaimana proses bekerja
- Peserta memahami bagaimana cara membuat proses
- Peserta mengerti jenis-jenis proses
- Peserta bisa memahami proses
- Peserta mampu membuat proses daemon
- 1. Proses
- 1.1 Proses
- 1.2 Proses ID
- 1.3 Melihat Proses
- 1.4 Membunuh Proses
- 1.5 Membuat Proses
- 1.6 Jenis-Jenis Proses
- 2 Daemon
- 2.1 Daemon
- 2.2 Cara Membuat Daemon
- 2.3 Proses Pembuatan Daemon
- Appendix
TL; DR.
Setiap program yang sedang berjalan disebut dengan proses. Proses dapat berjalan secara foreground atau background.
Jalankan $ ps -e
untuk melihat semua proses yang sedang berjalan.
Proses-proses dapat diibaratkan seperti orang tua (parent) dengan anak (child) yang turun temurun.
- Setiap proses memiliki parent dan child.
- Setiap proses memiliki ID (pid) dan parent ID (ppid), kecuali proses
init
atausystemd
. - ppid dari sebuah proses adalah ID dari parent proses tersebut. Perhatikan ascii art dibawah:
[Parent] [Child]
+--------+ +--------+
| pid=7 | | pid=10 |
| ppid=4 |------->| ppid=7 |
| bash | | nano |
+--------+ +--------+
Perhatikan, ppid dari proses nano
adalah pid dari proses bash
.
Untuk memvisualisasikan semua hierarki parent-child, jalankan
$ pstree | less
init-+-acpid
|-apache2---7*[apache2]
|-atd
|-cron
|-dbus-daemon
|-dockerd-+-docker-containe---10*[{docker-containe}]
| `-10*[{dockerd}]
|-fail2ban-server---2*[{fail2ban-server}]
|-freshclam
Explanation
- pstree: pstree berfungsi untuk menampilkan suatu proses dalam bentuk tree. Tree dari proses yang ditampilkan memiliki pid atau init (jika pid dihilangkan) sebagai root.
Contoh program demo-process.c
#include <stdio.h>
#include <unistd.h>
int main() {
printf("The process ID is %d\n", (int) getpid());
printf("The parent process ID is %d\n", (int) getppid());
return 0;
}
Hasil
The process ID is 8295
The parent process ID is 29043
Penjelasan
- getpid(): digunakan untuk memunculkan process ID dari proses yang dipanggil.
- getppid(): digunakan untuk memunculkan process ID dari parent proses yang dipanggil
Jalankan program
$ ps -e -o pid,ppid,command
PID PPID COMMAND
1 0 /sbin/init splash
2 0 [kthreadd]
6 2 [ksoftirqd/0]
7 2 [rcu_sched]
8 2 [rcu_bh]
9 2 [migration/0]
10 2 [lru-add-drain]
11 2 [watchdog/0]
.......
(long list)
.......
3760 2684 /usr/lib/x86_64-linux-gnu/notify-osd
3789 2684 /opt/google/chrome/chrome
25793 9789 ps -e -o pid,ppid,command
Penjelasan
- ps : menampilkan rincian dari proses yang sedang berjalan.
- -e : memilih seluruh proses yang sedang berjalan.
- -o : format yang ditentukan user.
Membunuh proses menggunakan $ kill {pid}
Contoh:
$ kill 3789
terdapat beberapa macam signal yang digunakan dalam command kill, antara lain sebagi berikut :
Signal name | Signal value | Effect |
---|---|---|
SIGHUP | 1 | Hangup |
SIGINT | 2 | Interrupt from keyboard |
SIGKILL | 9 | Kill signal |
SIGTERM | 15 | Termination signal |
SIGSTOP | 17,19,23 | Stop the process |
Secara default, $ kill
menggunakan signal SIGTERM. Untuk menggunakan signal tertentu, gunakan $ kill -{signal value} {pid}
. Contoh, $ kill -9 3789
untuk menggunakan SIGKILL.
Proses dapat dibuat menggunakan dua cara (pada C), yaitu dengan system()
atau fork
& exec
. fork
dan exec
adalah bagian dari system call, sedangkan system
bukan.
TL;DR.
fork
digunakan untuk menduplikasi program yang sedang berjalan.
exec
digunakan untuk mengganti program yang sedang berjalan dengan program yang baru.
fork
digunakan untuk menduplikasi proses. Proses yang baru disebut dengan child proses, sedangkan proses pemanggil disebut dengan parent proses. Spesifikasi fork
bisa dilihat dengan $ man 2 fork
atau di sini.
Setelah fork
dipanggil, kita tidaklah tahu proses manakah yang pertama selesai.
$ man 2 fork
....
RETURN VALUE
On success, the PID of the child process is returned in the parent,
and 0 is returned in the child. On failure, -1 is returned in the
parent, no child process is created, and errno is set appropriately.
....
int main() {
pid: 23, ppid: 10
[Main process]
|
fork(); > Child process created <
+
/ \
/ \
pid: 23, ppid: 10 pid: 30, ppid: 23
[Parent Process] [Child Process]
return 0;
}
Perhatikan, ppid child proses sama dengan pid parent process.
+-------------------------+
| Parent Process |
+-------------------------+
| int main() { |
| pid_t pid, |
| child_id, |
| ppid; |
| |
|--> child_id = fork(); |
| pid = getpid(); |
| ppid = getppid(); |
| } |
+-------------------------+
| pid = undefined |
| child_id = undefined |
| ppid = undefined |
+-------------------------+
|\
| \----------------------------------\
| |
V V
+-------------------------+ +-------------------------+
| Parent Process | | Child Process |
+-------------------------+ +-------------------------+
| int main() { | | int main() { |
| pid_t pid, | | pid_t pid, |
| child_id, | | child_id, |
| ppid; | | ppid; |
| | | |
| child_id = fork(); | | child_id = fork(); |
| pid = getpid(); | | pid = getpid(); |
|--> ppid = getppid(); | |--> ppid = getppid(); |
| } | | } |
+-------------------------+ +-------------------------+
| pid = 23 | | pid = 30 |
| child_id = 30 | | child_id = 0 |
| ppid = 10 | | ppid = 23 |
+-------------------------+ +-------------------------+
Perhatikan, bahwa;
pid
Parent Process ==ppid
Childchild_id
Parent Process ==pid
Child Process
Real code:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
pid_t child_id;
printf("This is the main program, with PID = %d, Child's ID = %d, Parent ID = %d\n",
(int) getpid(), (int) child_id, (int) getppid());
child_id = fork();
if (child_id != 0) {
printf("This is the parent process, with PID = %d, Child's ID = %d, Parent ID = %d\n",
(int) getpid(), (int) child_id, (int) getppid());
} else {
printf("This is the child process, with PID = %d, Child's ID = %d, Parent ID = %d\n",
(int) getpid(), (int) child_id, (int) getppid());
}
return 0;
}
Exec adalah function yang digunakan untuk menjalankan program baru dan mengganti program yang sedang berlangsung. exec
adalah program family yang memiliki berbagai fungsi variasi, yaitu execvp
, execlp
, execv
, dan lain lain.
Manual: $ man 3 exec
Example: exec-sample.c
#include <stdio.h>
#include <unistd.h>
int main () {
// argv[n] = { {your-program-name}, {argument[1]}, {argument[2]},.....,{argument[n-2]}, NULL }
char *argv[4] = {"list", "-l", "/", NULL};
execv("/bin/ls", argv);
printf("This line will not be executed\n");
return 0;
}
Permasalahan: Bagaimana cara menjalankan dua proses dalam satu program?
Contoh Permasalahan:
Bagaimana cara membuat folder ~/sisop
dan membuat file kosong bernama ~/process.c
?
Maka, bagaimana cara menjalankan mkdir
dan touch
dalam satu program?
Solusi:
Gunakan fork
dan exec
!
TL;DR.
Buat sebuah program dengan:
- Buat proses baru dengan
fork
- Jalankan
exec
yang memanggilmkdir
pada child process - Jalankan
exec
yang memanggiltouch
pada parent process
Visualisasi
+--------+
| pid=7 |
| ppid=4 |
| bash |
+--------+
|
| calls fork
V
+--------+ +--------+
| pid=7 | forks | pid=22 |
| ppid=4 | ------------------> | ppid=7 |
| bash | | bash |
+--------+ +--------+
| |
| calls exec to run touch | calls exec to run mkdir
| |
V V
Example: sample-fork-exec.c
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
pid_t child_id;
child_id = fork();
if (child_id < 0) {
exit(EXIT_FAILURE);
}
if (child_id == 0) {
// this is child
char *argv[] = {"mkdir", "sample-dir", NULL};
execv("/bin/mkdir", argv);
} else {
// this is parent
char *argv[] = {"touch", "sample-touch.txt", NULL};
execv("/usr/bin/touch", argv);
}
}
wait
adalah function yang digunakan untuk mendapatkan informasi ketika child proses berganti state-nya. Pergantian state dapat berupa termination, resume, atau stop. Pada modul ini, kita hanya menggunakan wait
untuk menangkap state termination.
Fungsi wait
pada parent process juga berguna untuk menangkap exit status dari child.
Permasalahan:
Bagaimana cara membuat program yang menjalankan suatu proses tanpa menghentikan program?
Contoh permasalahan:
Bagaimana cara membuat folder ~/sisop
dan membuat file kosong bernama ~/process.c
di dalamnya?
Maka, bagaimana cara menjalankan mkdir
lalu menjalankan touch
dalam satu program?
Solusi:
Gunakan fork
, exec
, dan wait
!
Buat sebuah program dengan:
- Buat proses baru dengan
fork
- Jalankan
exec
yang memanggilmkdir
pada child process - Buat parent process menunggu (
wait
) hingga proses pada child selesai - Setelah child selesai, jalankan
exec
yang memanggiltouch
pada parent
Visualisasi
+--------+
| pid=7 |
| ppid=4 |
| bash |
+--------+
|
| calls fork
V
+--------+ +--------+
| pid=7 | forks | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash | | bash |
+--------+ +--------+
| |
| waits for pid 22 | calls exec to run mkdir
| V
| +--------+
| | pid=22 |
| | ppid=7 |
| | ls |
V +--------+
+--------+ |
| pid=7 | | exits
| ppid=4 | <---------------+
| bash |
+--------+
|
| calls exec to run touch
|
V
Example: sample-fork-exec-wait.c
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
pid_t child_id;
int status;
child_id = fork();
if (child_id == 0) {
// this is child
char *argv[4] = {"mkdir", "-p", "sample-dir", NULL};
execv("/bin/mkdir", argv);
} else {
// this is parent
// the parent waits for all the child processes
while ((wait(&status)) > 0);
char *argv[3] = {"touch", "sample-dir/sample-touch.txt", NULL};
execv("/usr/bin/touch", argv);
}
}
Ketika system dijalankan, program hanya memanggil external command (kalau di Ubuntu berupa program /bin/bash
). Penggunaan system
sangat tergantung pada environment.
Contoh, ketika user menjalankan system("ls -l")
, ini sama seperti menjalankan $ ls -l
pada bash.
Meskipun mudah digunakan, tidak disarankan menggunakan fungsi system
karena beberapa asalan:
$ man system
....
NOTES
system() provides simplicity and convenience: it handles all
of the details of calling fork(2), execl(3), and waitpid(2),
as well as the necessary manipulations of signals.
Do not use system() from a program with set-user-ID or set-
group-ID privileges, because strange values for some environ‐
ment variables might be used to subvert system integrity.
...
Contoh
#include <stdlib.h>
int main() {
int return_value;
return_value = system("ls -l /");
return return_value;
}
Hasil
total 156
drwxr-xr-x 2 root root 4096 Sep 14 06:35 bin
drwxr-xr-x 4 root root 4096 Sep 20 00:24 boot
drwxrwxr-x 2 root root 4096 Agu 14 14:05 cdrom
drwxr-xr-x 3 root root 4096 Sep 12 19:11 data
(long list)
Zombie proses adalah child proses yang telah selesai mengerjakan tugasnya, namun belum ditangkap oleh parentnya dengan fungsi wait
.
PID PPID STAT COMMAND
28621 31403 S+ ./sample-zombie-process
28622 28621 Z+ [sample-zombie-p] <defunct>
Status dari zombie process adalah Z
atau terdapat <defunct>
di akhir commandnya.
Example: sample-zombie-process.c
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main () {
pid_t child_pid;
/* Create a child process. */
child_pid = fork ();
if (child_pid < 0) {
exit(EXIT_FAILURE);
}
if (child_pid == 0) {
/* This is the child process. Exit immediately. */
exit (0);
} else {
/* This is the parent process. Sleep for a minute. */
sleep (60);
}
return 0;
}
Orphan process adalah child process yang yang parent processnya telah berhenti.
PID PPID STAT COMMAND
1 0 Ss /sbin/init
28369 1 S ./sample-orphan-process
Example: sample-orphan-process.c
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main () {
pid_t child_pid;
/* Create a child process. */
child_pid = fork ();
if (child_pid < 0) {
exit(EXIT_FAILURE);
}
if (child_pid == 0) {
/* This is the child process. Sleep for a minute. */
sleep (60);
} else {
/* This is the parent process. Exit immediately. */
exit (0);
}
return 0;
}
Daemon process adalah proses yang berjalan di balik layar (background) dan tidak dapat berinteraksi dengan user melalui standard input/output. Akan dijelaskan di bab selanjutnya.
Daemon process adalah proses yang berjalan di balik layar (background) dan tidak dapat berinteraksi dengan user melalui standard input/output.
Daemon dapat dibuat dalam beberapa langkah:
- Melakukan fork pada parent process dan menghentikan parent process
- Mengubah mode file dengan
umask
- Mengubah unique session ID
- Mengubah direktori kerja
- Menutup file descriptor
- Membuat loop utama program
Langkah pertama adalah melakukan forking untuk membuat process baru kemudian mematikan Parent Process. Process induk yang mati akan membuat system mengira proses telah selesai sehingga akan kembali ke terminal user. Proses anak yang melanjutkan program setelah proses induk dimatikan.
pid_t pid;
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
if (pid > 0) {
// simpan PID dari child proces
exit(EXIT_SUCCESS);
}
Untuk menulis beberapa file (termasuk logs) yang dibuat oleh daemon, mode file harus diubah untuk memastikan bahwa file tersebut dapat ditulis dan dibaca secara benar. Pengubahan mode file menggunakan implementasi umask().
umask(0);
Child Process harus memiliki unik SID dari kernel untuk dapat beroperasi. Sebaliknya, Child process menjadi Orphan Proses pada system. Tipe pid_t yang dideklarasikan pada bagian sebelumnya, juga digunakan untuk membuat SID baru untuk child process. Pembuatan SID baru menggunakan implementasi setsid(). Fungsi setsid() memiliki return tipe yang sama seperti fork().
sid = setsid();
if (sid < 0) {
exit(EXIT_FAILURE);
}
Directori kerja yang aktif harus diubah ke suatu tempat yang telah pasti akan selalu ada. Pengubahan tempat direktori kerja dapat dilakukan dengan implementasi fungsi chdir
. Fungsi chdir
mengembalikan nilai -1 jika gagal.
if ((chdir("/")) < 0) {
exit(EXIT_FAILURE);
}
Salah satu dari langkah terakhir dalam mengeset daemon adalah menutup file descriptor standar (STDIN, STDOUT, STDERR). Karena daemon tidak perlu menggunakan kendali terminal, file descriptor dapat berulang dan berpotensi memiliki bahaya dalam hal keamanan. Untuk mengatasi hal tersebut maka digunakan implemtasi fungsi close().
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
Daemon bekerja terus menerus, sehingga diperlukan sebuah looping.
while(1) {
// daemon program
sleep(30);
}
exit(EXIT_SUCCESS);
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
int main() {
pid_t pid, sid;
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS);
}
umask(0);
sid = setsid();
if (sid < 0) {
exit(EXIT_FAILURE);
}
if ((chdir("/")) < 0) {
exit(EXIT_FAILURE);
}
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
while(1) {
// main program here
sleep(30);
}
exit(EXIT_SUCCESS);
}
# apt-get install manpages-posix-dev
$ man {anything-you-want-to-know}
$ man fopen
$ man fclose
$ man unistd.h
Ubahlah code pada program berikut, agar bisa print angka 0-199 secara berurutan.
Buatlah sebuah daemon yang dapat menghapus file berekstensi .trash pada folder /tmp/modul2 tiap 30 detik sekali. Namun daemon akan berhenti berjalan apabila ada file stop.trash!
Terdapat file dmesg.log.
Buatlah duplikat file tersebut dengan nama logku.txt
.
Hint: gunakan fork
, exec
dan perintah cp
.
Setelah itu buatlah file wlan0.log
berisi semua baris pada logku.txt
yang mengandung string wlan0
.
Hint: gunakan fopen
.
https://notes.shichao.io/apue/
http://advancedlinuxprogramming.com/alp-folder/alp-ch03-processes.pdf
http://www.linuxzasve.com/preuzimanje/TLCL-09.12.pdf
https://stackoverflow.com/questions/1653340/differences-between-fork-and-exec