SISTEM OPERASI
oleh
2. Eksperimen 2: Proses
a. Proses dan identitas proses
b. Pembuatan proses dengan system call fork( ) dan exit( )
c. Proses menunggu dengan system call wait( )
d. Proses zombie dan orphan
4. Eksperimen 4: Thread
a. Pembuatan Thread
b. Terminate Thread
c. Kirim Argumen ke Thread
d. Join Thread
e. Konsep Multithread
6. Eksperimen 6: Semaphore
a. UNIX System V Semaphore
b. POSIX Semaphore
c. Penggunaan Semaphore untuk Sinkronisasi Proses/Thread
7. Eksperimen 7: Interprocess Comunication (IPC)
a. IPC dengan Shared Memory
b. IPC dengan PIPE
c. IPC dengan Message Queue
d. IPC dengan Signal
Eksperimen Kedua
Proses
Tujuan Eksperimen
Menjelaskan tentang bagaimana sebuah proses dibuat, diterminate, dan bagaimana mengontrol proses
child.
Dasar Teori
1. Pembuatan Proses
Proses adalah sebuah program yang dalam keadaan sedang dieksekusi. Saat sebuah program
yang disimpan di dalam disk dieksekusi maka program tersebut akan running. Program (bersifat pasif)
tersebut akan berubah menjadi proses (bersifat aktif). Gambar 2.1 menjelaskan bagaimana sebuah
program dieksekusi dan berubah menjadi proses.
DATA INSTRUCTIONS
DISK
DATA INSTRUCTIONS
HEAP STACK
MEMORY
Parent akan X
melanjutkan
eksekusinya Child akan
melanjutkan
eksekusinya
Proses child juga dapat membuat child-child baru sehingga akan terdapat banyak proses yang
berjalan pada waktu bersamaan (ini yang disebut sebagai multitasking). Jika anda ingin agar proses
parent menunggu proses child selesai dieksekusi maka dapat menggunakan system call wait() atau
waitpid(). Saat sebuah proses selesai dieksekusi (terminate) maka segala sumber daya akan dilepaskan
oleh kernel.
2. Melihat Status Proses
Untuk melihat status proses dapat digunakan perintah ps dengan sintaks: ps [-option]
Perintah ps sangat bervariasi tergantung dari versi Linux yang digunakan. Bagaimana cara melihat State
Process dari proses-proses yang sedang aktif ? Informasi mengenai State Process ada di bagian STAT.
Perhatikan Gambar 2.3, bagian STAT adalah State Process. Penjelasan mengenai arti dari setiap state
process dapat dilihat pada Table 2.1
Pada Gambar 2.4 terlihat bahwa proses dengan PID 1 adalah INIT. Proses INIT adalah nenek moyang dari
semua proses yang aktif. Proses INIT memiliki Child yaitu NetworkManager dengan PID 3717, sedangkan
proses NetworkManager memiliki 2 buah Child yaitu dhclient PID 3769 dan {NetworkManager} dengan
PID 3770. Proses INIT akan running saat booting dan hanya akan terminate jika sistem di shutdown.
3. Identifikasi Proses
Tipe data pid_t akan menampilkan process ID dalam format signed integer (int). Untuk
mengetahui process ID (PID) dari sebuah proses maka gunakan getpid() sedangkan untuk mengetahui
process ID dari parent maka gunakan getppid(). Untuk menggunakan getpid() dan getppid() tersebut
harus menggunakan file header u istd.h da s s/t pes.h .
5. Contoh program
Source Code1:
#include <stdio.h>
#include <unistd.h> /* berisi pustaka fork( ) */
int main(void) {
printf("ilkom\n"); /* string ilkom ditampilkan oleh parent */
fork( ); /* buat proses child */
printf("undana\n"); /* string undana ditampilkan oleh parent dan child */
}
Terlihat bahwa proses parent memiliki PID 3312 sedangkan proses child 3312. Parent dari proses Parent
adalah BASH dengan PID 3231. Kalau anda coba run ./test maka proses tersebut akan langsung
terminate. Agar anda dapat lihat proses tree maka pada akhir baris kode program tambahkan sleep(8),
supaya ada waktu tunda sejenak sehingga saat anda eksekusi pstree –p maka proses test dapat terlihat
dengan jelas tree-nya.
Contoh 2: Melihat PID dan PPID dari Parent dan Child
Algoritma Contoh 2:
1. Buat proses Child menggunakan system call fork.
2. Jika nilai yang dikembalikan adalah -1 maka:
a. Cetak "Pembuatan proses Child gagal"
b. Keluar (terminate) menggunakan system call exit( ).
4. Jika nilai yang dikembalikan adalah 0 maka:
a. Cetak "Child: ini proses child"
b. Cetak "Child: PID saya: XXX, PID Parent saya: YYY"
. Cetak "Child: “elesai……"
5. Jika nilai yang dikembalikan > 0 maka:
a. Cetak "Parent: ini proses parent"
. Cetak Pa e t: PID sa a: YYY, PID Child sa a: XXX, PID Pa e t sa a: )))
d. Cetak Pa e t: “elesai……………..
6. Stop
Source Code2
#include <stdio.h>
#include <unistd.h> /* pustaka untuk system call fork() */
#include <stdlib.h> /* pustaka untuk system call exit() */
int main() {
pid_t sir;
sir = fork();
if(sir == -1){
printf("Pembuatan Child gagal\n");
exit(EXIT_FAILURE);
}
else if(sir == 0){
printf("Child: Ini Proses Child\n");
printf("Child: PID saya: %d, PID Parent saya: %d\n",getpid(),getppid());
printf("Child: Selesai.....\n");
}
else {
printf("Parent: Ini Proses Parent\n");
printf("Parent: PID saya: %d, PID Parent saya: %d, PID Child saya: %d\n",getpid(),getppid(),sir);
printf("Parent: Selesai.....\n");
}
return(0);
}
Apakah saat fork () dieksekusi maka proses parent dan child benar-benar identik ?? Tidak !! Saat fork ()
terjadi maka proses parent akan di-copy ke proses child tetapi tidak semua informasi di-copy. Atribut
apa saja yang berbeda (tidak ikut dicopy) ?? Coba anda ketik pada BASH: man fork kemudian pelajari
informasi-informasi tersebut dan analisislah dalam laporan anda. Contoh 3 menunjukan bahwa isi
variable Parent juga ikut dicopy ke Child saat fork( ) dieksekusi.
Algoritma Contoh 3:
1. Buat proses Child menggunakan system call fork.
2. Jika nilai yang dikembalikan adalah -1 maka:
a. Cetak "Pembuatan proses Child gagal"
b. Keluar (terminate) menggunakan system call exit( ).
4. Jika nilai yang dikembalikan adalah 0 maka:
a. Cetak "Child process"
b. Cetak "Process id = YYY"
. Cetak "Nilai a ia le = )
d. Cetak "P o ess id da i Pa e t = AAA
5. Jika nilai yang dikembalikan > 0 maka:
a. Cetak "Parent process"
. Cetak P o ess id = AAA
. Cetak "Nilai a ia le = )
6. Stop
Quiz:
Apakah variable x adalah shared variable (variable yang digunakan bersama oleh parent dan child) ???
Buktikan jawaban anda !!! (masukan pertanyaan quiz ini beserta jawaban (kode program) ke dalam
laporan anda).
Contoh 4: Proses Parent menunggu Proses Child selesai dengan system call wait( )
Algoritma Contoh 4:
1. Buat proses Child menggunakan system call fork.
2. Jika nilai yang dikembalikan adalah -1 maka:
a. Cetak "Pembuatan proses Child gagal"
b. Keluar (terminate) menggunakan system call exit( ).
4. Jika nilai yang dikembalikan adalah 0 maka:
a. Cetak "Child: ini proses child"
b. Tunda 5 detik menggunakan sleep( ).
. Cetak "Child: selesai……"
d. Keluar (terminate) menggunakan system call exit( ).
5. Jika nilai yang dikembalikan > 0 maka:
a. Cetak "Parent: ini proses parent"
. Cetak Pa e t: seka a g e u ggu hild selesai……..
c. Tunggu menggunakan system call wait( )
d. Cetak Pa e t: selesai……………..
6. Stop
Idealnya, saat sebuah proses Child selesai (terminate) maka dia harus mengirimkan code pemberitahuan
ke Parentnya. Tetapi jika saat Child terminate tanpa mengirimkan sinyal ke Parent maka Parent tersebut
akan menjadi proses ZOMBIE. Contoh 5 menunjukan bagaimana sebuah proses zombie dibentuk.
Algoritma Contoh 5:
1. Buat proses Child menggunakan system call fork.
2. Jika nilai yang dikembalikan adalah -1 maka:
a. Cetak "Pembuatan proses Child gagal"
b. Keluar (terminate) menggunakan system call exit( ).
4. Jika nilai yang dikembalikan adalah 0 maka:
a. Langsung determinate dengan system call exit(0).
5. Jika nilai yang dikembalikan > 0 maka:
a. Sleep(20);
6. Stop
if(sir == -1){
printf("Pembuatan Child gagal\n");
exit(EXIT_FAILURE);
}
else if(sir == 0){
exit(0);
}
else {
sleep(20);
printf("Parent: PID saya: %d, PID Child saya: %d\n",getpid(),sir);
printf("Parent: Selesai.....\n");
sleep(20);
}
return(0);
}
Tampak pada gambar di atas state proses = Z+ yang artinya proses ZOMBIE. Bagaimana kalau pada
Parent ditambahkan WAIT( ) ??? Jika demikian maka itu bukan proses Zombie karena Child dan Parent
akan terminate dengan normal. Karena proses zombie adalah proses yang tidak diinginkan maka anda
harus secara manual men-terminate proses zombie tersebut dengan cara mengetik: kill PID dari proses
parent yang menjadi zombie.
Contoh 6: Proses Orphan
Saat Parent terminate duluan sebelum Child maka Child akan menjadi Anak Yatim dan secara otomatis
yang menjadi orangtua dari child tersebut adalah proses i it (PID = 1) yang merupakan kakek
buyutnya (Ingat: Child diangkat sebagai anak bukan sebagai cucu !!). Contoh 6 akan menunjukan apa itu
proses orphan.
Algoritma Contoh 6:
1. Buat proses Child menggunakan system call fork.
2. Jika nilai yang dikembalikan adalah -1 maka:
a. Cetak "Pembuatan proses Child gagal"
b. Keluar (terminate) menggunakan system call exit( ).
4. Jika nilai yang dikembalikan adalah 0 maka:
a. Cetak "Child: PID saya = XXX, PID Parent saya = YYY"
b. Sleep(20);
c. Cetak "Sekarang PID Parent saya = AAA"
5. Jika nilai yang dikembalikan > 0 maka:
a. Cetak "Parent: PID saya = YYY, PID Child saya = XXX"
6. Stop
#include <stdio.h>
#include <unistd.h> /* pustaka untuk system call fork() */
#include <stdlib.h> /* pustaka untuk system call exit() */
int main() {
pid_t sir;
sir = fork();
if(sir == -1){
printf("Pembuatan Child gagal\n");
exit(EXIT_FAILURE);
}
else if(sir == 0){
printf("Child: PID saya = %d, PID Parent saya = %d\n",getpid(),getppid());
sleep(20);
printf("Child: Sekarang PID Parent saya = %d\n",getppid());
}
else {
printf("Parent: PID saya = %d, PID Child saya = %d\n",getpid(),sir);
}
return(0);
}
6. Eksperimen
NANTI SAYA AKAN BERIKAN DI DALAM KELAS !!!!!!!!!!!
Eksperimen Ketiga
Proses (Lanjutan)
Tujuan Eksperimen
Menjelaskan tentang bagaimana sebuah proses melakukan pergantian process imagenya saat
menjalankan perintah system call exec.
Dasar Teori
1. Eksekusi File
Process adalah program yang sedang dieksekusi (running di memory). Untuk melihat process
yang sedang running maka dapat menggunakan Task Manager (pada Windows) atau perintah ps –ef
(pada Linux). Sistem operasi akan mengatur setiap process dengan cara mengontrol PID-nya (PID =
process identifier). Varian dari perintah exec(), misalnya execl(), execlp(), execle(), execv(), execvp(), dan
execvpe() akan mengganti process yang sedang running dengan process lain. Setiap proses yang sedang
aktif memiliki process image-nya masing-masing. Apabila sebuah proses mengeksekusi program baru
maka process image-nya akan berganti dengan process image dari execlp. Perhatikan Gambar 3.1.A
sampai Gambar 3.1.C.
Saat EXEC dieksekusi maka EXEC akan menggantikan process image dari proses yang sedang running
dengan process image dari EXEC (dapat berupa file atau PATH). Yang dimaksud dengan process image
adalah process's text/code, data, stack (prinsip penggantian ini sama dengan fork). Setelah EXEC
dieksekusi maka kernel akan menghapus process image dari proses yang sedang running kemudian
menggantikannya dengan process text/data, data, stack ilik da i p oses foo .
Gambar 3.2 menunjukan contoh eksekusi menggunakan execlp().
Hasil eksekusi
b. Contoh perintah execlp
Hasil eksekusi
c. Contoh perintah execv
Hasil Eksekusi
3. Contoh program
Contoh 1: Parent membuat proses child kemudian child akan mengeksekusi system call exec( )
Hasil Eksekusi
Coba anda run beberapa kali maka akan muncul hasil yang berbeda (urutannya berbeda).
Lihat gambar di bawah ini, hasil eksekusi menunjukan urutan yang berbeda dengan gambar sebelumnya.
Mengapa demikian ????
Jawabanya karena setelah fork(), parent dan child benar-benar berdiri sendiri (tidak ada sinkronisasi
diantara keduanya).
Process completion status merujuk pada bagaimana cara membuat proses parent menunggu child
terminate baru kemudian parent akan terminate. Perintah yang digunakan adalah system call WAIT().
System call WAIT() akan membuat proses parent di-pause sampai salah satu child-nya terminate. Untuk
menggunakan system call WAIT() maka harus menggunakan macro sys/wait.h.
Berikut ini adalah contoh penggunaan wait(). Proses Parent akan membuat 2 child, kemudian menunggu
kedua child tersebut terminate. Child pertama dan Child kedua akan memberi respon yang berbeda saat
te i ate. Jika setiap hild e it se a a o al aka ketik ilko tetapi jika tidak aka ketik u da a .
Kode program
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
pid_t x1,x2,x3;
int jum,status;
x3 = fork();
if (x3 == 0) {
printf("Child Pertama, PID saya = %d\n", getpid());
sleep(10);
exit(EXIT_SUCCESS);
}
else if (x3 == -1) {
perror("Forking pertama: Jika ada masalah maka exit\n");
exit(EXIT_FAILURE) ;
}
else
printf ("Undana\n");
}
}
Hasil eksekusi
Jika Child kedua exit secara tidak normal maka hasil eksekusi adalah
Coba anda ganti exit child kedua dari exit(EXIT_SUCCESS) menjadi exit(EXIT_FAILURE) dan juga cara yang
sama untuk child pertama kemudian amati hasilnya.
Contoh 3: Parent tunggu child. Child akan meminta user untuk memasukan sebuah angka antara 0-
255, kemudian mengembalikan nilai tersebut sebagai status exit-nya
Kode program
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main () {
int number=0, statval;
printf ("Parent[%d]: Saya adalah Parent dengan PID = %d \n", getpid(),getpid());
printf ("Parent[%d]: Sedang melakukan forking ! \n", getpid());
if (fork() == 0)
{
printf ("Child[%d]: Ini proses Child, PID saya = %d !\n", getpid(),getpid());
printf ("Child[%d]: Masukan Sebuah Angka : ", getpid ());
scanf ("%d", &number);
printf ("Child[%d]: EXIT !!! EXIT !!! dengan angka = %d\n", getpid(), number);
exit(number);
}
printf ("Parent[%d]: Sekarang menunggu Child !\n", getpid());
wait (&statval) ;
if (WIFEXITED (statval)) {
printf ("Parent[%d]: Child exit dengan status %d\n",
getpid(), WEXITSTATUS(statval));
}
}
Hasil Eksekusi
5. Eksperimen
NANTI SAYA AKAN BERIKAN DI DALAM KELAS !!!!!!!!!!!
Eksperimen Keempat
Tujuan Eksperimen
Menjelaskan tentang bagaimana komunikasi antar proses menggunakan SIGNAL. Dengan mengetahui
cara komunikasi antar proses menggunakan SIGNAL, Anda dapat memproteksi program Anda dari
penekanan tombol CTRL C, atau dapat juga mengatur signal alarm clock untuk men-terminate suatu
process jika proses tersebut terlalu lama merespon sebuah event.
Dasar Teori
1. Signal
Saat anda menjalankan sebuah program, kadang-kadang muncul masalah yang tidak anda
diharapkan atau tidak terduga. Masalah-masalah yang tidak terduga/tidak diharapakan ini kita anggap
saja se agai e e ts . Event-event ini misalnya floating point error, power failure (masalah supply listrik
ke komputer), atau ada permintaan dari user melalui terminal (misalnya user menekan tombol Control-
C, Control-Z, dan Control-\). Event-event ini biasanya dikenal dengan nama interrupts. Saat sistem
operasi UNIX/Linux mendeteksi munculnya event-event ini maka sistem operasi (kita sebut saja sebagai
kernel) mengirimkan sebuah signal ke proses yang dituju.
Kernel bukanlah satu-satunya yang dapat mengirimkan sebuah signal. Sebuah proses juga dapat
mengirimkan signal ke proses lain, tetapi sepanjang ada ijin (permissions). Ada banyak sekali jenis
signal, dimana setiap tipe signal memiliki nomor identitas masing-masing. Pada Linux, nomor
identitas/nama signal ada pada file header signal.h (lihat Gambar 1).
Gambar 1. Nama Signal
Seorang programmer dapat mengatur apakah sebuah signal tertentu akan diabaikan atau diproses.
Programmer dapat mengaturnya menggunakan signal handler. Saat sebuah proses menerima signal,
maka proses tersebut akan menunda aktifitasnya, kemudian mengeksekusi signal handler, dan apabila
signal handler selesai dieksekusi maka proses akan melanjutkan aktifitasnya yang tertunda tadi.
Ctrl-C
Penekanan tombol Ctrl-C akan menyebabkan kernel mengirimkan signal INT (SIGINT) ke proses yang
sedang running. Proses yang sedang running akan secara default di-terminate.
Ctrl-Z
Penekanan tombol Ctrl-Z akan menyebabkan kernel mengirimkan signal TSTP (SIGTSTP) ke proses yang
sedang running. Proses yang sedang running secara default akan menunda eksekusi.
Ctrl-\
Penekanan tombol Ctrl-\ akan menyebabkan kernel mengirimkan signal ABRT (SIGABRT) ke proses yang
sedang running. Secara default, signal jenis ini akan menyebabkan proses terminate.
Jika dijalankan maka program mencoba mengirim signal termination (SIGTERM) ke proses yang memiliki
PID 773. Pengubahannya dapat dilakukan dengan signal SIGQUIT atau SIGKILL yang juga berfungsi untuk
menghentikan suatu proses.
3. Implementasi
Contoh 1:
Apabila proses child berakhir maka secara otomatis sistem akan mengirim sebuah signal SIGCHLD ke
proses parentnya yang menandakan bahwa proses child berakhir.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h> /* signal() */
#include <sys/types.h>
#include <sys/wait.h> /* wait() */
int main(){
pid_t pid;
int i;
signal(SIGCHLD, sig_handler);
/* install signal handler baru untuk SIGCHLD */
pid = fork(); /* buat proses baru */
switch (pid) {
case -1: perror("fork"); /* proses fork() gagal */
exit(EXIT_FAILURE); /* exit jika gagal */
case 0: /* ini proses child */
printf("CHILD: Hello parent\n");
sleep(3); /* tunda selama 3 detik */
exit(EXIT_SUCCESS); /* akhiri proses child */
default : /* ini proses parent */
printf("PARENT: Hello child\n");
for (i=1; i<=5; i++) {
printf("PARENT: %d\n", i);
sleep(1); /* tunda selama 1 detik */
}
printf("parent Selesai.\n");
exit(EXIT_SUCCESS); /* akhiri proses parent */
}
}
Penjelasan Program
1. Menginstal fungsi penanganan signal SIGCHLD pada parent, hal ini memberitahukan bahwa
apabila sistem mengirim SIGCHLD ke parent maka parent mengarahkan aksi penanganannya ke
fungsi sig_handler().
2. fork() untuk membuat proses baru (child)
3. Child dibuat tertunda selama 3 detik dan parent melakukan pencetakan nilai i.
4. Setelah 3 detik child mengeksekusi exit() untuk mengakhiri prosesnya
5. Pada saat child selesai sistem mengirim SIGCHLD ke parent dan parent mengarahkannya ke
fu gsi sig_ha dle a g e a pilka pesa hild selesai
6. Parent melanjutkan kembali prosesnya sampai selesai.
Pada contoh program diatas signal dikirim secara otomatis oleh sistem begitu child mengeksekusi fungsi
exit. Tentunya dapat dikirim suatu signal ke proses lain menggunakan system call kill() seperti yang telah
dilakukan.
Contoh 2:
Berikut contoh program yang mendefenisikan aksi untuk signal tertentu serta mengirim signal
tersebut untuk melihat aksi dari rutin yang disiapkan.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
void sigquit (); /*prototype fungsi penanganan signal */
Contoh 3:
Berikut contoh program yang mencetak terus- e erus kata UNDANA di layar. Apabila user
menekan tombol Ctrl-C aka proses tidak ter i ate tetapi aka e cetak di la ar kata ILKOM .
Apabila user menekan sekali lagi tombol Ctrl-C maka proses akan terminate.
Untuk membuat kode program di atas maka harus menggunakan Signal Handler yang akan bertugas
untuk menangkap signal INT dari penekanan tombol Ctrl-C ke udia e etak kata ILKOM . “etelah
itu Signal Handler HARUS DI RESET KE DEFAULT agar saat user menekan tombol Ctrl-C sekali lagi maka
proses akan terminate secara normal.
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
Penjelasan Program
Perintah (void) signal(SIGINT, SIG_DFL); berfungsi untuk mengembalikan atau RESET SIGNAL ke
DEFAULT sehingga saat penekanan tombol Ctrl-C sekali lagi maka proses akan terminate secara
normal.
4. Eksperimen
NANTI SAYA AKAN BERIKAN DI DALAM KELAS !!!!!!!!!!!
Eksperimen Kelima
Tujuan Eksperimen
Menjelaskan tentang apa itu thread, bagaimana membuat thread baru, bagaimana mengirimkan data ke
thread, bagaimana cara men-join thread, dan bagaimana memanipulasi atribut thread.
Dasar Teori
1. Thread
Thread merupakan unit dasar dari penggunaan CPU, yang terdiri dari Thread_ID, program counter,
register set, dan stack. Sebuah thread berbagi code section, data section, dan sumber daya system
operasi dengan Thread lain yang dimiliki oleh proses yang sama. Thread juga sering disebut lightweight
process. Sebuah proses tradisional atau heavyweight process mempunyai thread tunggal yang berfungsi
sebagai pengendali. Perbedaannya ialah proses dengan thread yang banyak mengerjakan lebih dari satu
tugas pada satu satuan waktu.
GNU/Linux menggunakan POSIX standard thread API (lebih dikenal dengan nama pthreads). Seluruh
function-function thread dan tipe data dideklarasikan di dalam file header <pthread.h>.
2. Pembuatan Thread
Proses adalah program yang sedang dirunning. Proses secara default memiliki sebuah thread.
Dengan kata lain: SETIAP PROSES PASTI MEMILIKI MINIMAL SEBUAH THREAD. Sebuah proses dapat
membuat thread-thread lain (disebut juga multithread) dengan menggunakan function :
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void
*(*start_rtn)(void), void *restrict arg)
Berikut ini adalah contoh program untuk membuat thread baru (multithread)
Contoh 1. Multithread
#include <pthread.h>
#include <stdio.h>
/* Cetak x di layar. */
void* print_xs (void* unused) {
while (1)
fput , stde ;
return NULL;
}
/* Program utama. */
int main () {
pthread_t thread_id;
/* buat thread baru, dimana thread ini akan mencetak x dilayar. Thread baru akan running di function print_xs. */
pthread_create (&thread_id, NULL, &print_xs, NULL);
/* default th ead aka e etak o di la a . */
while (1)
fput o , stde ;
return 0;
}
kita akan lihat bagaimana hasil runningnya.
Jangan lupa menulis –lpthread, karena akan me-link ke library pthread. Kalo anda lupa menulis –
lpthread maka akan muncul error seperti gambar di bawah ini.
Jika decompile dengan benar maka hasil running tampak pada gambar di bawah ini.
Data thread1_args adalah data yang akan dikirimkan ke thread function bernama char_print.
Contoh berikut ini menunjukan cara mengirimkan data ke thread function. Data yang dikirimkan
memiliki struktur data record atau dalam bahasa C disebut STRUCT !!! Selain itu, harus juga dilakukan
casting terhadap struktur data tersebut agar manipulasi datanya mudah dilakukan. Pada contoh kasus
ini, struct yang bernama data_thread akan dicasting ke variable p.
Jika decompile dan dieksekusi, hasilnya seperti ini.
Terlihat bahwa data yang dicetak adalah x dan jumlahnya 30. Coba anda ubah data jumlah cetak untuk
melihat apakah jumlah cetaknya juga berubah. Ini adalah contoh bagaimana cara mengirimkan data ke
thread function.
5. Join Thread
Kalo anda perhatikan kembali contoh pada Bab 4 (Mengirimkan data ke thread) maka sebenarnya
kode program tersebut sebenarnya ada masalah. Bayangkan jika ada lebih dari 2 buah thread yang akan
dibuat kemudian salah satu dari thread tersebut belum selesai running tetapi thread default (thread
pada program utama) sudah selesai dieksekusi. Untuk mengatasi masalah tersebut maka anda dapat
mengatur agar thread default (thread pada program utama) menunggu sementara waktu saat thread-
thread dieksekusi. Proses menunggu ini hamper sama dengan wait(). Untuk keperluan menunggu
tersebut, anda dapat menggunakan function pthread_join().
Pada contoh berikut, akan dibuat 2 buah thread. Thread default akan menunggu saat kedua thread
e etak ka akte o da .
Kode program
#include <pthread.h>
#include <stdio.h>
data_thread2.character = 'o';
data_thread2.count = 5;
pthread_create (&thread2, NULL, &char_print, &data_thread2);
Jika anda compile dan running maka hasilnya seperti di bawah ini.
7. Eksperimen
NANTI SAYA AKAN BERIKAN DI DALAM KELAS !!!!!!!!!!!
Eksperimen Keenam
Tujuan Eksperimen
Saat banyak thread yang running secara concurrent (secara bersamaan), mereka butuh komunikasi satu
sama lain (atau lebih dikenal dengan nama sinkronisasi). Salah satu keuntungan menggunakan thread
adalah mudah disinkronisasi. Percobaan keenam fokus pada sinkronisasi antar thread dan bagaimana
cara melakukan sinkronisasi. Kita akan membahas dua metode untuk melakukan sinkronisasi antar
thread, yaitu:
Mutual Exclusion (Mutex) Locks
Condition Variables
Dasar Teori
1. Mengapa butuh sinkronisasi dan bagaimana cara melakukannya?
Anggap terdapat 2 buah thread seperti gambar di bawah ini.
THREAD A THREAD B
a = r; b = r;
a++; b -- ;
r = a; r = b;
pri tf r = %d\ , r ; pri tf r = %d\ , r ;
Berapa nilai r ? Nilai r Sulit diprediksi karena kedua thread saling berebut menggunakan variable r
sehingga hasil akhir dari r menjadi sulit diprediksi. Kondisi ini disebut race condition.
1. Sintaks #include<pthread.h> pada baris ke-5 adalah file header untuk mengakses POSIX thread
library.
2. Baris 24 pthread_create() digunakan untuk membuat thread baru.
3. Function tambah pada baris ke 10 s/d 19 adalah aktifitas dari thread yang barusan dibuat atau
thread T0.
4. Baris 33 yaitu pthread_join() digunakan untuk menunggu thread T0 selesai dieksekusi.
5. Ada variable global bernama bilangan yang awalnya bernilai nol (0).
6. Baris 26 s/d 31 variabel bilangan ditambah satu sampai bilangan bernilai 20.
7. Baris 12 s/d 17 variabel bilangan juga ditambah satu sampai bilangan bernilai 20.
8. Jadi seharusnya variable bilangan bernilai 40. Tetapi mengapa saat dieksekusi variable bilangan
hanya bernilai 20 ?
Mutex harus diset sebagai variable global karena seluruh thread yang sedang running akan
melihat/menggunakan mutex ini. Jika saya analogikan pengatur lalulintas seperti gambar lalulintas
di atas maka lampu merah akan digunakan untuk me-LOCK sebuah thread sehingga hanya satu
thread saja yang akan lewat. Sedangkan lampu hijau digunakan meng-UNLOCK thread sehingga
seluruh thread yang aktif akan dapat mengakses variable/data. Bagaimana cara menggunakan
mutex sebagai pengatur hak akses ??? Berikut ini adalah langkah-langkahnya:
Mutex Summary
Membuat mutex, gunakan sintak:
pthread_mutex_init (pthread_mutex_t mutex, pthread_mutexattr_t attr)
Menghancurkan mutex yang sudah tidak digunakan lagi, gunakan sintak:
pthread_mutex_destroy ( pthread_mutex_t mutex )
me-Lock mutex, gunakan sintak:
pthread_mutex_lock ( pthread_mutex_t mutex )
meng-Unlock mutex, gunakan sintak:
pthread_mutex_unlock ( pthread_mutex_t mutex )
3. Condition Variable
Condition variables adalah salah satu metode yang digunakan untuk melakukan sinkronisasi thread.
Kalau mutex melakukan sinkronisasi dengan cara mengontrol akses thread ke data yang di-share maka
condition variable mengijinkan sinkronisasi thread berdasarkan nilai data saat kondisi (condition)
tertentu. Condition variable menggunakan mutex sebagai basis.
a. Membuat dan Menghapus Condition Variable
a. Rutin yang digunakan:
b. Cara Penggunaan:
Condition variable harus dideklarasikan terlebih dahulu dengan menggunakan type
pthread_cond_t, dan harus diinisialisasi sebelum digunakan. Ada 2 cara untuk meng-
b. Cara Penggunaan:
pthread_cond_wait() akan mem-blok thread (thread yang dipanggil) sampai kondisi tertentu
(sampai diberi signal oleh Thread pemanggil supaya dapat running kembali).
Misalnya rutin pthread_cond_wait() ada di Thread A, dan pthread_cond_signal ada di Thread B
maka Thread A akan diblok (tidak dapat running) dan hanya akan running kembali jika
diperintahkan (diberi signal) oleh Thread B.
Rutin ini harus dipanggil saat mutex dalam kondisi locked, dan akan secara otomatis
membebaskan mutex saat menunggu. Setelah signal diterima dan thread terjaga (dari kondisi
wait ke kondisi ready), mutex akan secara otomatis akan terkunci (locked) untuk digunakan oleh
thread. Programmer bertugas untuk meng-unlock mutex saat thread selesai.
pthread_cond_signal() digunakan untuk memberi signal (atau membangunkan) thread lain yang
sedang menunggu condition variable. pthread_cond_signal() harus dipanggil setelah mutex di
locked, dan harus meng-unlock mutex agar supaya pthread_cond_wait() selesai.
pthread_cond_broadcast() hampir sama cara kerjannya dengan pthread_cond_signal. Bedanya
adalah jika pthread_cond_signal hanya untuk 1 thread saja sedangkan
pthread_cond_broadcast() jika ada lebih dari 1 thread.
Tidak boleh memanggil pthread_cond_signal() sebelum memanggil pthread_cond_wait().
Condition Variable Summary
Membuat/Menghancurkan Condition Variable, gunakan:
- pthread_cond_init
- pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- pthread_cond_destroy
Menunggu kondisi tertentu (condition), gunakan:
- pthread_cond_wait - unlocks dan tunggu condition variable diberi signal.
- pthread_cond_timedwait – beri batas berapa lama akan memblok.
Membangunkan thread berdasarkan kondisi tertentu :
- pthread_cond_signal - restarts salah satu thread yang sedang menunggu
condition variable.
- pthread_cond_broadcast – membangunkan (wake up) semua thread yang
diblok oleh condition variable.
Ada dua thread, yaitu thread A dan thread B. Thread A akan mencetak angka 1 sampai 3 dan
juga angka 8 sampai 10 sedangkan Thread B HANYA mencetak angka 4 sampai 7.
Algoritma:
Nilai count = 0 (kondisi awal)
Pada THREAD B
o Selama nilai count < 3 ATAU count > 6 maka: pthread_cond_signal(&condition_var);
Penjelasan: Kirim signal atau bangunkan Thread A yang sedang menunggu.
o Jika kondisi di atas tidak terpenuhi maka:
count++; /* tambahkan nilai variable count */
p i tf Th ead B: Nilai ou t = %d\n ,count);
Pada THREAD A
o Lock mutex dan tunggu signal dari Thread B untuk meng-unlock Thread A.
pthread_mutex_lock(&count_mutex);
o mutex unlocked jika condition varialbe dalam Thread B memberi signal.
pthread_cond_wait(&condition_var,&count_mutex);
count++;
printf("Thread A: Nilai count = %d\n",count);
Implementasi Program
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void *functionCount1();
void *functionCount2();
int count = 0;
#define COUNT_DONE 10
#define COUNT_HALT1 3
#define COUNT_HALT2 6
int main(){
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, &functionCount1, NULL);
pthread_create(&thread2, NULL, &functionCount2, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
exit(0);
}
void *functionCount2(){
for(;;){
pthread_mutex_lock(&count_mutex);
if(count < COUNT_HALT1 || count > COUNT_HALT2){
// Condition yang terjadi jika statement di atas terpenuhi.
// Kirim signal untuk membebaskan thread yang sedang tunggu di functionCount1
// sekarang, functionCount1() diijinkan untuk mengakses variable "count".
pthread_cond_signal(&condition_var);
}
else {
count++;
printf("Counter value functionCount2: %d\n",count);
}
pthread_mutex_unlock(&count_mutex);
if(count >= COUNT_DONE) return(NULL);
}
}
Eksperimen Ketujuh
Tujuan Eksperimen
Eksperimen ketujuh bertujuan untuk:
1. Bagaimana cara menggunakan semaphore untuk menyelesaikan berbagai macam masalah
sinkronisasi.
2. Bagaimana cara mengimplementasikan Unix System V Semaphore, antara lain:
Inisialisasi semaphores
Mengurangi nilai/counter dari semaphore
Menambahkan nilai/counter dari semaphore
Menghancurkan/menghapus semaphore
Dasar Teori
1. Apa itu Semaphore?
Semaphore adalah counter atau penanda yang digunakan mengatur sinkronisasi saat proses atau
thread berbagi (shared) sumber daya yang sama pada saat yang sama. Pada saat berbagi sumber daya
bersama maka harus ada jaminan bahwa hanya satu proses yang mengakses sumber daya tersebut pada
suatu waktu tertentu (jaminan ini disebut mutual exclusion).
Saat Wait maka nilai counter berkurang 1. Jika counter bernilai negative maka proses/thread ini akan
dipaksa berhenti (sleep). Proses/thread yang diberi operasi Wait akan dipaksa berhenti sampai
proses/thread tersebut diberi tanda Signal. Jadi kalo counter lebih kecil dari 0 maka akan proses/thread
akan diblok.
Saat Signal maka nilai counter bertambah 1. Proses/thread yang diberi operasi/tanda Signal akan
running dan harus berhenti jika diberi tanda Wait. Kalo proses/thread lebih besar atau sama dengan nol
maka akan running (unblock).
Contoh Kasus 1:
Contoh kasus 1 menjelaskan bagaimana cara menggunakan semaphore untuk melakukan sinkronisasi
antar proses. Anggap ada 2 proses yaitu Proses A dan Proses B, dimana kedua proses men-share sumber
daya yang sama (variable bilangan). Jika tidak dilakukan sinkronisasi antara proses maka nilai variable
bilangan menjadi tidak pasti (tidak stabil), misalnya: berapa nilai variable bilangan pada Gambar di
bawah ini ?? Bilangan = 20 atau 40 ?? JIKA ANDA RUNNING BEBERAPA KALI MAKA
NILAI BILANGAN TIDAK PASTI/STABIL KARENA SELALU BERUBAH-UBAH.
Jika program di atas diimplementasi TANPA MENGGUNAKAN SEMAPHORE maka kode programnnya
tampak seperti Gambar di bawah ini
CONTOH KASUS 1 TANPA SEMAPHORE
COMPILE DAN RUNNING (CONTOH KASUS 1 TANPA SEMAPHORE)
Dengan menggunakan semaphore maka anda dapat mengatur thread mana yang harus running terlebih
dahulu sehingga kedua thread tidak berebutan menggunakan variable bilangan. Gambar sebelumnya
dimodifikasi dengan menambahkan semaphore, hasilnya seperti gambar di bawah ini.
Pada ga a di atas, se apho e a g e a a osua dii isialisasi INIT & osua, de ga e ei
nilai ol. “e apho e osua i i disha e antara kedua thread. Artinya thread A & B sama-sama
mengetahui semaphore ini dan menggunakannya.
Pada kasus ini, thread A & B e getahui ada a se apho e osua da au diatu oleh se apho e
osua . Mula-mula thread B akan dipaksa berhenti karena ada operasi Wait (nilai yosua yang awalnya
bernilai nol akan dikurangi 1 sehingga = -1) sedangkan thread A langsung bekerja. Saat thread A selesai
aka “ig al & osua aka e uat ilai osua sa a de ga ka e a “ig al e uat ou te
osua e ta ah se elu a e ilai -1). Setelah yosua bernilai 0, maka thread B akan bekerja.
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
void sem_v(int id, int value){ // untuk ganti nilai semaphore dengan -1 atau 1
struct sembuf sem_b;
int v;
sem_b.sem_num = 0;
sem_b.sem_op = 1; /* V() */
sem_b.sem_flg = SEM_UNDO;
if(semop(id, &sem_b, 1) == -1)
fprintf(stderr, "\nError...Semaphore V Increment Gagal");
}
// THREAD
void *tambah(void *a) {
int i,j;
sem_wait(yosua);
sem_signal(yosua);
void* result;
pthread_join(T0, &result);
printf("Nilai Bilangan Akhir = %i\n", bilangan);
return 0;
}
./test
Contoh Kasus 2:
Diketahui terdapat 2 proses A dan B dimana masing-masing proses memiliki 2 buah thread. Misalnya
ingin diatur agar Proses A thread a1 kerja lebih dahulu kemudian diikuti oleh Proses B thread b1. Setelah
b1 selesai maka thread b2 akan dieksekusi dan terakhir adalah eksekusi thread a2. Urutan kerjanya:
a1 b1 b2 a2
Penjelasan:
Langkah 1:
Wait (&sir) akan membuat semaphore sir menjadi nol sehingga a1 running. Pada saat yang sama, Proses
B diblok karena semaphore yosua bernilai -1 (yosua yang semula bernilai 0 lalu dikurangi 1).
Langkah 2:
Setelah a1 selesai, Signal() membuat yosua bernilai 0 dan Proses B, b1 dan b2 running. Pada saat yang
sama Proses A, a2 diblok karena sir bernilai -1.
Langkah 3:
Setelah b2 selesai, Signal(&sir) akan membuat sir bernilai 0 sehingga proses a2 dapat run.
Contoh Kasus 3:
Pe hatika kasus di a ah i i. Apakah kedua p oses dapat u de ga e a ??? Tidak….I i a g
dinamakan DEADLOCK !!!.
Saat run, kedua proses sama-sama Wait (&yosua) dan membuat semaphore yosua bernilai -1. Kedua
proses akan saling menunggu pemberian Signal (&yosua) tetapi tidak pernah dapat !!. Coba run program
deadlock_bilangan_semaphore.c yang saya berikan dengan cara: Pada terminal bash ketik:
gcc deadlock_bilangan_semaphore.c –o test -lpthread
Lalu run dengan cara ketik: ./test
Output: Program tersebut akan hang !!!.
Contoh Kasus 4:
Ini latihan untuk anda. Perhatikan gambar di bawah ini. Apakah kedua proses dapat run dengan benar
jika:
1. Semaphore sir = 1 & yosua = 1.
2. Semaphore sir = 1 & yosua = 0.
3. Semaphore sir = 0 & yosua = 1.
4. Semaphore sir = 0 & yosua = 0.
UNIX System V Semaphore
Untuk menggunakan System V semaphore, gunakan pustaka:
#include <sys/sem.h>
Berikut ini saya jelaskan function-function semaphore:
1. Semget
Semget digunakan untuk membuat semaphore yang baru. Bentuk umum perintah semget:
int semget(key_t key, int num_sems, int sem_flags);
key_t = digunakan untuk mengijinkan proses-proses menggunakan semaphore yang sama. Kalo
anda hanya menggunakan 1 proses saja tapi multi thread maka gunakan IPC_PRIVATE. Tetapi
jika anda mau supaya proses-proses lain juga mamakai semaphore maka setiap proses hanrus
menggunakan kunci key_t yang sama.
Contoh penggunaan (untuk multi thread):
if((yosua = semget(IPC_PRIVATE, 1, 0666|IPC_CREAT)) == -1) {
printf("\nError... Tidak bisa buat semaphore yosua");
exit(1);
}
0666 = Operasi Read & write
IPC_CREAT = Proses pembuatan (create) semaphore.
Sem_flag = 0666|IPC_CREAT.
num_sems = biasanya selalu diberi angka 1 (jumlah semaphore)
2. Semop
Digunakan untuk mengganti nilai semaphore. Bentuk umumnya:
int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
sem_id = didapat dari semget.
struct sembuf *sem_ops = pointer ke array of structure (record). Isi dari struct sembuf adalah:
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
3. Semctl
Digunakan untuk mengontrol informasi semaphore. Bentuk umum:
int semctl(int sem_id, int sem_num, int command, ...);
sem_num = jumlah semaphore. Biasanya diberi 0 yang artinya hanya sebuah semaphore.
Command = terdiri dari 2 yaitu:
1. SETVAL = digunakan untuk menginisialisasi semaphore ke sebuah nilai.
2. IPC_RMID = digunakan untuk menghapus identifier semaphore jika tidak digunakan lagi.
Contoh Kasus Semaphore 1 (Multithread)
Saya ingin membuat 1 buah proses dengan 2 buah thread. Kedua thread akan men-share 1 buah
variabel yang sama yaitu variable bilangan. Saya ingin menghitung naik variable bilangan mulai dari 1
sampai 40. Saya akan bagi 2:
thread 1 untuk hitung naik dari 1 sampai 20.
thread 2 untuk hitung naik dari 1 sampai 20.
Seharusnya (idealnya) total nilai bilangan = 40.
Karena kedua thread bekerja secara kongruen (bersama-sama) maka akan terjadi race condition. Saya
ingin menggunakan semaphore untuk mengatur urutan eksekusi dari thread sehingga akan mencegah
race condition dan membuat hasil akhir dari variable bilangan = 40.
Algoritma
1. Buat 1 buah thread baru.
2. Buat 1 buah semaphore bernama yosua
3. Urutan kerjanya sbb:
8. Eksperimen
NANTI SAYA AKAN BERIKAN DI DALAM KELAS !!!!!!!!!!!