Anda di halaman 1dari 72

PRAKTIKUM

SISTEM OPERASI

oleh

Yosua Alberth Sir

JURUSAN ILMU KOMPUTER


UNIVERSITAS NUSA CENDANA - KUPANG
Daftar Isi

1. Eksperimen 1: Pengenalan sistem operasi Linux


a. Perintah dasar linux
b. Operasi input/output
c. Operasi file dan struktur direktori

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

3. Eksperimen 3: Proses Lanjutan


a. Simulasi Context Switch Antar Proses
b. System Call exec( )
c. Konsep multitasking sederhana

4. Eksperimen 4: Thread
a. Pembuatan Thread
b. Terminate Thread
c. Kirim Argumen ke Thread
d. Join Thread
e. Konsep Multithread

5. Eksperimen 5: Thread Lanjutan


a. Sinkronisasi thread menggunakan mutex
b. Sinkronisasi thread menggunakan semaphore

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.

Program (disimpan di dalam disk)

DATA INSTRUCTIONS

DISK

Jika di eksekusi maka: di LOAD ke MEMORY


PROGRAM berubah menjadi PROSES
dan akan memiliki STATE

DATA INSTRUCTIONS

HEAP STACK

MEMORY

Gambar 2.1 Program Vs Proses


Saat proses terbentuk maka dia akan memiliki alamat sendiri dan secara otomatis akan terdapat satu
buah thread. Saat program dieksekusi, mungkin saja terdapat banyak proses yang terbentuk dimana
masing-masing proses tersebut memiliki alamat dan nomor identitas sendiri (PID) yang unik (yang
dimaksud dengan unik adalah tidak ada proses yang memiliki PID yang sama saat dieksekusi) dan setiap
proses berkerja secara independen (tidak tergantung pada proses-proses lainnya).
Organisasi proses berbentuk seperti pohon proses. Setiap proses memiliki sebuah proses parent
dan jika proses tersebut membuat proses baru maka proses yang baru tersebut dinamakan proses child.
Sebuah proses child yang terbentuk dengan system call fork() hampir identik dengan proses
parentnya (yang identik antara lain: variable, code, dan file descriptors) tetapi memiliki PID (Process
ID) yang berbeda. Setelah proses baru (child) berhasil dibuat eksekusi dilanjutkan secara normal di
masing-masing proses pada baris setelah pemanggilan system call fork(). Gambar 2.2 menunjukkan
bagaimana system call fork() bekerja.

Proses Parent running


program “X”

Proses Child running


program “X”

fork ( ) Memory Parent di


copy ke Child

Parent akan X
melanjutkan
eksekusinya Child akan
melanjutkan
eksekusinya

Gambar 2.2 System Call fork( )

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

Gambar 2.3 Proses yang sedang aktif


Tabel 2.1 Deskripsi dari setiap State Process pada GNU/Linux
PROCESS
KETERANGAN
STATE
Sleeping. Usually waiting for an event to occur, such as a signal or input to
S
become available.
‘u i g. “t i tl speaki g, u a le, that is, o the u ueue eithe
R
executing or about to run.
Uninterruptible Sleep (Waiting). Usually waiting for input or output to
D
complete.
Stopped. Usually stopped by shell job control or the process is under the
T
control of a debugger.
Z Defu t o zo ie p o ess.
N Low priority task, i e.
W Paging. (Not for Linux kernel 2.6 onwards.)
S Process is a session leader.
+ Process is in the foreground process group.
L Process is multithreaded
< High priority task.

2.1 Contoh perintah ps


1. Untuk melihat setiap proses pada system, gunakan sintaks:
ps -e
ps -ef
ps -eF
ps -ely
2. Untuk melihat proses tree, gunakan sintaks:
ps -ejH
ps axjf
3. Untuk melihat informasi tentang thread, gunakan sintaks:
ps -eLf
ps axms
4. Untuk melihat setiap proses pada system dengan user-defined format, gunakan sintaks:
ps -eo pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm
ps axo stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
ps -eopid,tt,user,fname,tmout,f,wchan
Seperti telah dijelaskan di atas, struktur proses berbentuk seperti pohon, dimana setiap proses memiliki
sebuah proses parent dan jika proses tersebut membuat proses baru maka proses yang baru tersebut
dinamakan proses child. Hubungan Parent-Child dapat dilihat dengan menggunakan sintak: pstree –p

Gambar 2.4 Struktur proses berbentuk pohon

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 .

4. Membuat Banyak Proses


Untuk pembuatan proses baru di sistem, GNU C menyediakan system call fork() yang disertakan
pada file heade u istd.h de ga e tuk p otot pe s :
pid_t fork (void);
System call fork() mengembalikan suatu besaran bertipe pid_t yang dapat bernilai :
 Angka -1 menandakan pembuatan proses baru (child) gagal
 Angka 0 menandakan proses child
 Angka > 1 menandakan proses parent

5. Contoh program

Contoh 1: Parent membuat proses child dengan system call fork( )

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 */
}

Penjelasan Source Code1:


 Mula- ula st i g ilko ditampilkan oleh parent (Child belum ada/belum lahir).
 Saat system call fork ( ) dieksekusi maka akan muncul proses child. Pada saat ini maka akan ada
2 proses yaitu Parent dan Child.
 Kedua proses ini akan mengeksekusi st i g u da a sehi gga st i g u da a aka di etak
dilayar sebanyak 2 kali (1 oleh Parent dan 1 oleh Child).
Hasil Compile & Eksekusi Source Code 1

Gambar 2.5 Hasil running source code 1

Lihat Proses Tree


Buka BASH baru lalu ketik pstree –p dan teka E te

Gambar 2.6 Hasil pstree -p

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);
}

Penjelasan Source Code2:


 getpid( ) digunakan untuk mengambil PID proses.
 getppid( ) digunakan untuk mengambil proses Parent.
 sir adalah variable untuk fork( ).

Hasil Compile & Eksekusi

Gambar 2.7 Hasil running source code 2

Contoh 3: Proses Parent dan Child identik ??

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

Source Code Contoh 3

Gambar 2.8 Source code 3


Hasil Compile dan Run Source Code Contoh 3

Gambar 2.9 Hasil running source code 3

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

Source Code Contoh 4

Gambar 2.10 Source code 4

Hasil Compile dan Run Source Code Contoh 4

Gambar 2.11 Hasil running source code 4

Penjelasan Source Code 4:


 Apabila diinginkan proses parent menunggu sampai proses child selesai maka gunakan fungsi
wait() yang tersedia pada file header wait.h,
 Terlihat bahwa setelah fungsi wait() dieksekusi oleh proses parent maka parent akan menunggu
proses child selesai yaitu mengeksekusi fungsi exit(). Setelah child selesai maka proses parent
melanjutkan eksekusinya pula sampai selesai.

Contoh 5: Proses Zombie

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

Source Code Contoh 5


#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();
int rv;

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);
}

Hasil Compile dan Run Source Code Contoh 5


Ketik di BASH: ps –eo pid,tt,ppid,stat,command

Gambar 2.12 Hasil running source code 4

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

Source Code Contoh 6

#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);
}

Hasil Compile dan Run Source Code Contoh 6

Gambar 2.13 Hasil running source code 6

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.

Gambar 3.1.A: P oses X PID 6 e geksekusi p og a foo


Gambar 3.1.B: Process image milik Proses X (PID 26) dihapus

Gambar 3.1.C: P o ess i age ilik foo di uati ke P oses X PID 6

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().

Gambar 3.2 Hasil eksekusi execlp

Bagaimana cara kerja dari Gambar 3.1 ?


Mula-mula proses akan mencetak Pare t: ‘u i g ps e ggu aka e eclp kemudian akan
memanggil execlp. System call execlp kemudian akan mengeksekusi perintah: ps ax. Saat perintah ps
selesai dieksekusi maka akan muncul prompt pada BASH shell. Proses ./test tidak akan dieksekusi lagi
sehingga kalimat kedua: Il u Ko puter U iv. Nusa Ce da a tidak akan muncul di layar. Hal yang
menarik dari system call execlp() adalah PID dari proses baru (ps –ax) sama dengan PID dari proses
sebelumnya (./test) dimana keduanya bernilai sama yaitu 10307.

2. Contoh Penggunaan Exec( )


Berikut ini adalah contoh-contoh eksekusi perintah-perintah dari varian system call exec(). Coba
dikompile dan run untuk melihat hasilnya.
a. Contoh perintah execl

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).

4. Process Completion Status

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 if ((x2 = fork()) == 0) {


printf("Child kedua, PID saya %d\n", getpid());
sleep(15);
exit(EXIT_FAILURE) ;
}
else if (x2 == -1) {
perror("Forking kedua: Jika ada masalah maka exit\n");
exit(EXIT_FAILURE) ;
}
printf ("Saya adalah Parent, PID saya = %d\n",getpid());
jum = 0;

while (jum < 2) {


x1 = wait(&status);
jum++;
if (x1 == x3) printf ("Child pertama exit\n");

else printf ("Child kedua exit\n");

if ((status & 0xffff) == 0)


printf ("Ilkom\n");

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

Komunikasi Antar Proses Menggunakan Signal

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.

2. Mengirimkan Signal ke Proses


Ada dua cara mengirimkan Signal ke proses: (i) menggunakan terminal (BASH) dan menggunakan
keyboard.
a. Pengiriman Signal menggunakan keyboard
Pengiriman Signal menggunakan keyboard antara lain:

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.

b. Pengiriman Signal menggunakan perintah (Command Line)


Cara lain mengirimkan Signal adalah melalui perintah-perintah pada terminal, misalnya kill(). Perintah
kill() menggunakan 2 parameter, yaitu nama signal (atau angka integer), dan process ID (PID). Sintaknya
seperti berikut ini:
kill -<signal> <PID>
Contoh, jika ingin mengirimkan signal SIGTERM ke proses yang memiliki ID 773 maka ketik pada
terminal:
kill –s SIGTERM 773
atau
kill –s 15 773
Perintah di atas sama fungsinya dengan penekanan tombol Ctrl-C saat proses tersebut dijalankan.
Gambar 2. Terminate Proses ILKOM (PID 3220) Melalui Command Line

Gambar 3. Proses ILKOM Berhasil Diterminate


c. Pengiriman Signal menggunakan System Call
Cara ketiga mengirimkan Signal ke proses adalah menggunakan system call kill(). Berikut ini adalah
contoh program menggunakan system call kill().
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

pid_t my_pid = 773;


kill(my_pid, SIGTERM);

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() */

/* prototype fungsi penanganan signal */


void sig_handler();

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 */
}
}

/* ini definisi fungsi penanganan signal */


void sig_handler(){
printf("child Selesai.\n");
}

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 */

int main (){


pid_t pid;
pid=fork();
switch (pid) {
case -1: perror("fork"); /* proses fork() gagal */
exit(EXIT_FAILURE); /* exit jika gagal */
case 0: /* ini proses child */
signal (SIGQUIT, sigquit); /* install signal handler baru untuk child */
for (;;); /* loop terus */
default: /* ini proses parent */
printf ("\nPARENT: Kirim signal SIGQUIT ke CHILD\n\n");
kill (pid, SIGQUIT); /* kirim signal */
sleep (3); /* tunda 3 detik */
exit(EXIT_SUCCESS); /* akhiri proses parent */
}
}

/* ini definisi fungsi penanganan signal */


void sigquit (){
printf ("CHILD: Terima SIGQUIT dari parent.\n");
exit (0);
}

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>

void sir(int sig) {


p i tf ILKOM\ ;
(void) signal(SIGINT, SIG_DFL);
}
int main() {
(void) signal(SIGINT, sir);
while(1) {
p i tf UNDANA\ ;
sleep(1);
}
}

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

Thread: Pembuatan dan Eksekusi Thread

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 penjelasan dari function di atas:


 Argument pertama adalah pthread_t. Pthread adalah alamat dariu struktur data yang berfungsi
untuk menyimpan seluruh informasi mengenai thread yang akan dibuat.
 Argument kedua berisi atribut-atribut dari thread baru. Beri saja data NULL.
 Argument ketiga adalah function pointer. Bagian ini akan menunjuk ke function tempat thread
baru akan running.
 Argument keempat digunakan untuk mengirim parameter/data ke function thread.

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.

3. Melihat Thread yang Sedang Running


Perhatikan gambar di bawah ini
Perhatikan proses ./test pada gambar di atas. Ada satu buah proses ./test dengan PID 6388 dan
parent id-nya yaitu PPID 3148. Sedangkan ada dua thread yaitu LWP 6388 dan LWP 6389.
Apa itu LWP ? Light Weight Process atau Thread. Jadi hanya ada 1 buah proses (memiliki
PID sama) tetapi ada 2 thread yang berbeda (memiliki LWP berbeda). Thread pertama LWP 6388 adalah
default PID (PID dan LWP bernilai sama 6388) sedangkan thread kedua adalah thread yang baru dibuat
oleh proses yang memiliki LWP 6390.

4. Mengirimkan Data ke Thread


Anda dapat mengirimkan data ke thread function dengan cara menuliskan/mengisi data pada argument
keempat dari function pthread_create (lihat bab 2 pembuatan thread). Contoh pengiriman data ke
thread function adalah:

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>

/* record yang berisi parameter yang akan dicetak. */


struct char_print_parms {
/* karakter yang akan dicetak. */
char character;
/* jumlah pencetakan. */
int count;
};

void* char_print (void* parameters) {


/* lakukan casting cookie pointer agar memiliki tipe yang benar. */
struct char_print_parms* p = (struct char_print_parms*) parameters;
int i;
for (i = 0; i < p->count; ++i){
fputc (p->character, stderr);
sleep(1);
}
return NULL;
}

int main (){


pthread_t thread1, thread2;
struct char_print_parms data_thread1, data_thread2;
data_thread1.character = 'x';
data_thread1.count = 5;
pthread_create (&thread1, NULL, &char_print, &data_thread1);

data_thread2.character = 'o';
data_thread2.count = 5;
pthread_create (&thread2, NULL, &char_print, &data_thread2);

pthread_join (thread1, NULL);


pthread_join (thread2, NULL);
return 0;
}

Jika anda compile dan running maka hasilnya seperti di bawah ini.

Bagaimana jika pthread_join (thread1,NULL) dan pthread_join (thread2,NULL) dihapus ?? Hasilnya


seperti di bawah ini.

7. Eksperimen
NANTI SAYA AKAN BERIKAN DI DALAM KELAS !!!!!!!!!!!
Eksperimen Keenam

Thread: Sinkronisasi (Mutex & Condition Variables)

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.

2. Mutex dan Race Condition


Mutual exclusion (mutex) adalah metode yang digunakan untuk mencegah terjadinya inkonsitensi
data sebagai akibat dari race condition. Thread yang menggunakan sumber daya kritis atau sumber daya
yang digunakan secara bersama-sama (share) disebut sedang memasuki critical section. Untuk lebih
memahami race condition dan mutex maka perhatikan kode program di bawah ini.
Penjelasan kode program di atas:

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 ?

Hasil Compile dan Eksekusi (RUN) terhadap kode program di atas:

Mengapa variable bilangan tidak berjumlah 40 (karena ada 2 buah


perulangan FOR LOOP sebanyak 40x) tetapi hanya 20 ?
Karena saya menggunakan thread tersebut secara tidak aman/benar sehingga hasilnya sulit
diprediksi. Thread 1 dan thread 2 saling berebut menggunakan variable BILANGAN sehingga hasil
akhirnya (BILANGAN) menjadi sulit diprediksi. Kondisi ini disebut RACE CONDITION. Supaya
hasilnya benar yaitu bilangan = 40 maka salah satu caranya adalah menggunakan MUTEX. Inilah
gunanya MUTEX dalam Sistem Operasi !! Pada kode program di atas, variable bilangan adalah
CRITICAL SECTION. Apa itu critical section ? Proses/Thread yang menggunakan sumber daya kritis
atau sumber daya yang dishare atau digunakan secara bersama-sama disebut sedang memasuki
CRITICAL SECTION/REGION. Pada kasus kita, critical section adalah variable BILANGAN, maka untuk
mencegah proses/thread berebutan hak akses terhadap variable BILANGAN (critical section) maka
kita harus mengatur hak akses tersebut menggunakan mutex.
Ilustrasi: Mutex sebagai Pengatur Lalulintas Sinyal
Untuk melindungi bagian critical section terhadap hasil yang tidak dapat diprediksi maka harus
digunakan mutex lock seperti ini:

pthread_mutex_t a_lock = PTHREAD_MUTEX_INITIALIZER;

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:

Langkah 1. Critical Section


Langkah pertama kita tentukan dulu apa atau bagian mana dari kode program di atas yang harus
menjadi critical section. Jawabannya pasti variable bilangan !! Mengapa ?? Karena variable bilangan
digunakan secara bersama-sama oleh thread baru (dibuat menggunakan fungsi pthread_create) dan
thread asli (thread yang dibuat saat proses dibuat). Tetapi dimana dipasang ?? Ada dua area critical
section yaitu pada bagian sebelum FOR LOOP dan sesudah FOR LOOP (ada dua FOR LOOP). Jika
dipasang pada kedua FOR LOOP ini maka hanya satu thread saja yang mengakses variable bilangan !

Langkah 2. Lampu Merah (Thread LOCK)

Langkah 3. Lampu Hijau (Thread UNLOCK)


Kode program hasil modifikasi:
Hasil eksekusi kode program di atas:

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-

inisialisasi condition variable:


o Secara statik, saat dideklarasikan. Contoh:
pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER;
o Secara dinamis, dengan menggunakan rutin pthread_cond_init(). ID dari condition
variable akan dikembalikan ke thread yang memanggil melalui condition parameter.
Metode ini mengijinkan setting atribut objek dari condition variable yaitu, attr.
 Objek attr (bersifat optional) digunakan untuk mengatur atribut-atribut dari condition variable.
Hanya ada satu atribut yang didefinisikan untuk condition variable, yaitu process-shared, yang
berfungsi untuk mengijinkan condition variable dapat dilihat oleh thread-thread lain di dalam
proses-proses yang lain. Atribute object, jika digunakan, harus bertipe pthread_condattr_t
(dapat diberi nilai NULL).
 Rutin pthread_condattr_init() dan pthread_condattr_destroy() digunakan untuk membuat dan
menghancurkan atribut objek dari condition variable.
 pthread_cond_destroy() harus digunakan untuk membebaskan sebuah condition variable yang
tidak digunakan lagi.
b. Menunggu dan Melakukan Signalling pada Condition Variable
a. Rutin yang digunakan:

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.

Contoh Kasus Sinkronisasi Menggunakan 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>

pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;


pthread_cond_t condition_var = PTHREAD_COND_INITIALIZER;

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);

printf("Final count: %d\n",count);

exit(0);
}

/* functionCount2 mengijinkan functionCount1 untuk MENCETAK


ANGKA 1 SAMPAI 3 DAN 8 SAMPAI 10 */
void* functionCount1(){
for(;;){
// Lock mutex & tunggu signal untuk meng-unlock mutex
pthread_mutex_lock(&count_mutex);
// Tunggu saat functionCount2() mengakses count
// mutex unlocked jika condition varialbe dalam functionCount2() memberi signal.
pthread_cond_wait(&condition_var,&count_mutex);
count++;
printf("Counter value functionCount1: %d\n",count);
pthread_mutex_unlock(&count_mutex);
if(count >= COUNT_DONE) return (NULL);
}
}

/* functionCount2 MENCETAK ANGKA 4 SAMPAI 7 saja */

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

Komunikasi Antar Proses Menggunakan Semaphore

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).

2. Cara Kerja Semaphore?


Semaphore memiliki 2 buah operasi:
 Operasi Wait (nama lainnya: P (Proberen) atau Down atau Lock)
 Operasi Signal (nama lainya: V (Verhogen) atau Up atau Unlock atau Post)

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)

SEKARANG DICOBA MENGGUNAKAN 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.

PENTING !!!: Semaphore harus dishare diantara proses/thread yang terlibat


dalam komunikasi kalo tidak maka anda tidak dapat melakukan sinkronisasi
antar proses/thread tersebut.

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.

Implementasi kode program Contoh Kasus 1

#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>

int bilangan = 0; // VARIABEL GLOBAL YANG DISHARE BERSAMA ANTARA


// THREAD 1 DAN THREAD TAMBAH
pthread_t T0;

int yosua; /* variabel semaphore */

// FUNCTION DARI SEMAPHORE


void sem_p(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; /* P() */
sem_b.sem_flg = SEM_UNDO;
if (semop(id, &sem_b, 1) == 1)
fprintf(stderr, "\nError...Semaphore P Decrement Gagal");
}

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");
}

void sem_create(int semid, int initval){


int semval;
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} s;
s.val = initval;
if((semval = semctl(semid, 0, SETVAL, s)) < 0)
fprintf(stderr,"\nsemctl error....");
}

void sem_wait(int id){ // Decrement P


int value = -1;
sem_p(id, value);
}

void sem_signal(int id){ // Increment V


int value = 1;
sem_v(id, value);
}
// END FUNCTION SEMAPHORE

// THREAD
void *tambah(void *a) {
int i,j;

sem_wait(yosua);

for (i = 0; i < 20; i++) {


j = bilangan;
j++;
sleep(1);
bilangan = j;
}
return NULL;
}
int main() {
int i,j;

printf("Nilai Bilangan Awal = %i\n", bilangan);

// BUAT SEMAPHORE "yosua"


if((yosua = semget(IPC_PRIVATE, 1, 0666|IPC_CREAT)) == -1){
printf("\nError... Tidak bisa buat semaphore yosua");
exit(1);
}
sem_create(yosua, 0);

if(pthread_create(&T0, NULL, tambah, NULL)==-1)


error("thread tidak bisa dibuat");

// THREAD INI YANG RUNNING DULUAN KEMUDIAN THREAD TAMBAH

for ( i=0; i<20; i++) {


j = bilangan;
j++;
sleep(1);
bilangan = j;
}

sem_signal(yosua);

void* result;
pthread_join(T0, &result);
printf("Nilai Bilangan Akhir = %i\n", bilangan);
return 0;
}

Compile & Run


gcc bilangan_semaphore.c –o test –lpthread

./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

Saran: Gunakan 2 buah semaphore !!


Solusi:

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 !!!.

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 !!!.

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 !!!!!!!!!!!

Anda mungkin juga menyukai