Menggunakan:
- Linux
- Bahasa C (compile dengan gcc)
- Proses dan Daemon
Proses adalah kondisi dimana OS menjalankan (eksekusi) suatu program. Ketika suatu program tersebut dieksekusi oleh OS, proses tersebut memiliki PID (Process ID) yang merupakan identifier dari suatu proses. Pada UNIX, untuk melihat proses yang dieksekusi oleh OS dengan memanggil perintah shell ps
. Untuk melihat lebih lanjut mengenai perintah ps
dapat membuka man ps
.
Dalam penggunaannya, suatu proses dapat membentuk proses lainnya yang disebut spawning process. Proses yang memanggil proses lainnya disebut parent process dan yang terpanggil disebut child process.
Merupakan identifier dari suatu proses yang menampilkan user yang menjalankan suatu program. Pada program C, dapat memanggil fungsi uid_t getuid(void);
Angka unik dari suatu proses yang sedang berjalan untuk mengidentifikasi suatu proses. Pada program C, dapat memanggil fungsi pid_t getpid(void);
Setiap proses memiliki identifier tersendiri dan juga setelah proses tersebut membuat proses lainnya. Proses yang terbentuk ini memiliki identifier berupa ID dari pembuatnya (parent). Pada program C, dapat memanggil fungsi pid_t getppid(void);
.
Untuk melihat proces yang sedang berjalan di OS, dapat menggunakan ps -ef
untuk melihat secara detailnya.
Penjelasan:
- UID: user yang menjalankan program
- PID: process IDnya
- PPID: parent PID, kalau tidak ada parent akan bernilai 0
- C: CPU Util. (%)
- STIME: waktu proses dijalankan
- TTY: terminal yang menjalankan proses. Jika tidak ada berarti background
- TIME: lamanya proses berjalan
- CMD: perintah yang menjalankan proses tersebut
Untuk menghentikan (terminate) proses yang berjalan, jalankan perintah shell kill [options] <pid>
. Biasanya untuk menghentikan paksa suatu proses dapat menggunakan perintah kill -9 <pid>
. Angka 9 adalah kode Signal untuk terminate suatu process.
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 ketika menggunakan perintah shell kill <pid>
, akan menggunakan SIGSTOP
yang mana akan menghentikan proses namun masih dapat dilanjutkan kembali.
fork
adalah fungsi system call di C untuk melakukan spawning process. Setelah memanggil fungsi itu, akan terdapat proses baru yang merupakan child process dan mengembalikan nilai 0 untuk child process dan nilai PID untuk parent process.
Coba program dibawah ini dan compile terlebih dahulu dengan gcc coba.c -o coba
Kemudian execute program dengan ./coba
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t child_id;
child_id = fork();
printf("Ini akan kepanggil 2 kali\n");
if(child_id != 0){
printf("\nParent process.\nPID: %d, Child's PID: %d\n", (int)getpid(), (int)child_id);
}else {
printf("\nChild process.\nPID: %d, Parent's PID: %d\n", (int)getpid(), (int)getppid());
}
return 0;
}
Hasilnya akan menjadi:
Ini akan kepanggil 2 kali
Parent process.
PID: 13101, Child's PID: 13102
Ini akan kepanggil 2 kali
Child process.
PID: 13102, Parent's PID: 1
Visualisasi:
+-------------------------+
| Parent Process |
+-------------------------+
| int main() { |
| pid_t child_id; |
| |
| pid = getpid(); |
| ppid = getppid(); |
| |
|--> child_id = fork(); |
+-------------------------+
| pid = 20 |
| child_id = undefined |
| ppid = 10 |
+-------------------------+
|\
| \----------------------------------\
| |
V V
+-------------------------+ +-------------------------+
| Parent Process | | Child Process |
+-------------------------+ +-------------------------+
|--> | |--> |
| pid = getpid(); | | pid = getpid(); |
| ppid = getppid(); | | ppid = getppid(); |
| } | | } |
+-------------------------+ +-------------------------+
| pid = 20 | | pid = 23 |
| child_id = 23 | | child_id = 0 |
| ppid = 10 | | ppid = 20 |
+-------------------------+ +-------------------------+
exec
adalah fungsi untuk menjalankan program baru dan menggantikan program yang sedang berjalan. Fungsi exec
memiliki banyak variasi seperti execvp
, execlp
, dan execv
.
Contoh yang akan digunakan adalah execv
.
#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", "/home/", NULL};
execv("/bin/ls", argv);
printf("This line will not be executed\n");
return 0;
}
Dengan menggabungkan fork
dan exec
, kita dapat melakukan dua atau lebih tasks secara bersamaan. Contohnya adalah membackup log yang berbeda secara bersamaan.
#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); // Jika gagal membuat proses baru, program akan berhenti
}
if (child_id == 0) {
// this is child
char *argv[] = {"cp", "/var/log/apt/history.log", "/home/[user]/", NULL};
execv("/bin/cp", argv);
} else {
// this is parent
char *argv[] = {"cp", "/var/log/dpkg.log", "/home/[user]/", NULL};
execv("/bin/cp", argv);
}
}
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
Jika ingin melakukan banyak task secara bersamaan tanpa mementingkan urutan kerjanya, dapat menggunakan fork
dan exec
.
Kita dapat menjalankan dua proses dalam satu program. Contoh penggunaannya adalah membuat folder dan mengisi folder tersebut dengan suatu file. Pertama, buat folder terlebih dahulu. Kemudian, buat file dengan perintah shell touch
pada folder tersebut. Namun, pada kenyataannya untuk melakukan dua hal bersamaan perlu adanya jeda beberapa saat.
Untuk membuat file yang berada dalam suatu folder, pertama-tama folder harus ada terlebih dahulu. Untuk delay suatu proses dapat menggunakan system call wait
.
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
int main() {
pid_t child_id;
int status;
child_id = fork();
if (child_id < 0) {
exit(EXIT_FAILURE); // Jika gagal membuat proses baru, program akan berhenti
}
if (child_id == 0) {
// this is child
char *argv[] = {"mkdir", "-p", "folderku", NULL};
execv("/bin/mkdir", argv);
} else {
// this is parent
while ((wait(&status)) > 0);
char *argv[] = {"touch", "folderku/fileku.txt", NULL};
execv("/usr/bin/touch", argv);
}
}
Pada contoh di atas, fungsi wait
adalah menunggu child process selesai melakukan tugasnya, yaitu membuat folder. Setelah terminated, parent process akan kembali menjalankan prosesnya membuat fileku
dalam folder folderku
.
system
adalah fungsi untuk melakukan pemanggilan perintah shell secara langsung dari program C. Contohnya ketika ingin memanggil suatu script dalam program C. system(ls)
akan menghasilkan output yang sama ketika memanggilnya di shell script dengan ls
.
File inibash.sh:
#!/bin/bash
echo "Shell script dipanggil"
File system.c:
#include <stdlib.h>
int main() {
int return_value;
return_value = system("bash inibash.sh");
return return_value;
}
Output:
Shell script dipanggil
Zombie Process terjadi karena adaanya child process yang di exit namun parrent processnya tidak tahu bahwa child process tersebut telah di terminate, misalnya disebabkan karena putusnya network. Sehingga parent process tidak merelease process yang masih digunakan oleh child process tersebut walaupun process tersebut sudah mati.
Orphan Process adalah sebuah proses yang ada dalam komputer dimana parent process telah selesai atau berhenti bekerja namun proses anak sendiri tetap berjalan.
Daemon Process adalah sebuah proses yang bekerja pada background karena proses ini tidak memiliki terminal pengontrol. Dalam sistem operasi Windows biasanya lebih dikenal dengan sebutan service. Daemon adalah sebuah proses yang didesain supaya proses tersebut tidak mendapatkan intervensi dari user.
Daemon adalah suatu program yang berjalan di background secara terus menerus tanpa adanya interaksi secara langsung dengan user yang sedang aktif.
Ada beberapa langkah untuk membuat sebuah daemon:
Langkah pertama adalah membuat sebuah parent process dan memunculkan child process dengan melakukan fork()
. Kemudian bunuh parent process agar sistem operasi mengira bahwa proses telah selesai.
pid_t pid; // Variabel untuk menyimpan PID
pid = fork(); // Menyimpan PID dari Child Process
/* Keluar saat fork gagal
* (nilai variabel pid < 0) */
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* Keluar saat fork berhasil
* (nilai variabel pid adalah PID dari child process) */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
Setiap file dan directory memiliki permission atau izin yang mengatur siapa saja yang boleh melakukan read, write, dan execute pada file atau directory tersebut.
Dengan menggunakan umask
kita dapat mengatur permission dari suatu file pada saat file itu dibuat. Di sini kita mengatur nilai umask(0)
agar kita mendapatkan akses full terhadap file yang dibuat oleh daemon.
umask(0);
Sebuah Child Process harus memiliki SID agar dapat berjalan. Tanpa adanya SID, Child Process yang Parent-nya sudah di-kill
akan menjadi Orphan Process.
Untuk mendapatkan SID kita dapat menggunakan perintah setsid()
. Perintah tersebut memiliki return type yang sama dengan perintah fork()
.
sid = setsid();
if (sid < 0) {
exit(EXIT_FAILURE);
}
Working directory harus diubah ke suatu directory yang pasti ada. Untuk amannya, kita akan mengubahnya ke root (/) directory karena itu adalah directory yang dijamin ada pada semua distro linux.
Untuk mengubah Working Directory, kita dapat menggunakan perintah chdir()
.
if ((chdir("/")) < 0) {
exit(EXIT_FAILURE);
}
Sebuah daemon tidak boleh menggunakan terminal. Oleh sebab itu kita harus menutup file descriptor standar (STDIN, STDOUT, STDERR).
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
Di loop utama ini lah tempat kita menuliskan inti dari program kita. Jangan lupa beri perintah sleep()
agar loop berjalan pada suatu interval.
while (1) {
// Tulis program kalian di sini
sleep(30);
}
Di bawah ini adalah kode hasil gabungan dari langkah-langkah pembuatan daemon (template Daemon):
#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; // Variabel untuk menyimpan PID
pid = fork(); // Menyimpan PID dari Child Process
/* Keluar saat fork gagal
* (nilai variabel pid < 0) */
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* Keluar saat fork berhasil
* (nilai variabel pid adalah PID dari child process) */
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) {
// Tulis program kalian di sini
sleep(30);
}
}
Untuk menjalankan daemon process pertama kita compile program C yang telah kita buat dengan perintah gcc [nama_program.c] -o [nama_file_outputd]
.
Setelah melakukan langkah sebelumnya, akan muncul sebuah file executeable yang dapat dijalankan dengan ./nama_file_outputd
.
Untuk memeriksa process apa saja yang sedang berlangsung kita dapat menggunakan perintah ps -aux
. Untuk menemukan Daemon process yang kita run, manfaatkan grep
. Sehingga perintahnya menjadi ps -aux | grep "nama_file_outputd"
. Bila ada, berarti daemon process kita sedang berjalan.
Untuk mematikan daemon process kita akan menggunakan perintah kill
. Pertama kita harus menemukan PID dari Daemon process yang akan dimatikan. Kita dapat menemukan PID tersebut pada langkah sebelumnya. Lalu jalankan sudo kill -9 pid
untuk mematikan process-nya.
Modifikasi code soal1 agar output nya menjadi angka urut dari 0 sampai 100, tanpa menghapus fungsi yang sudah ada dan menggunakan wait.
Buatlah sebuah program yang dapat mengcopy folder beserta semua isi dari folder di /home/{USER}/Music ke dalam sebuah folder dengan format nama tanggal-bulan-tahun_jam:menit:detik (contoh: 25-02-2020_16:37:53). Gunakan fork, exec, dan wait.
Buatlah sebuah daemon yang berjalan setiap 10 detik yang dapat melakukan backup isi dari file diary.txt yang disimpan dalam file diary.log.{no} (contoh: diary.log.1 , diary.log.2, … ) lalu menghapus isi diary.txt tersebut sehingga file tersebut kosong kembali. Tidak diperbolehkan menggunakan exec dan system.