Praktikum Sistem Operasi GNU/Linux

72
PRAKTIKUM SISTEM OPERASI oleh Yosua Alberth Sir JURUSAN ILMU KOMPUTER UNIVERSITAS NUSA CENDANA - KUPANG

description

Fokus pembahasan antara lain: (i) Process, (ii) Thread, (iii) IPC: Signal, (iv) IPC Shared Memory

Transcript of Praktikum Sistem Operasi GNU/Linux

Page 1: Praktikum Sistem Operasi GNU/Linux

PRAKTIKUM

SISTEM OPERASI

oleh

Yosua Alberth Sir

JURUSAN ILMU KOMPUTER

UNIVERSITAS NUSA CENDANA - KUPANG

Page 2: Praktikum Sistem Operasi GNU/Linux

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

Page 3: Praktikum Sistem Operasi GNU/Linux

7. Eksperimen 7: Interprocess Comunication (IPC)

a. IPC dengan Shared Memory

b. IPC dengan PIPE

c. IPC dengan Message Queue

d. IPC dengan Signal

Page 4: Praktikum Sistem Operasi GNU/Linux

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

Program (disimpan di dalam disk)

DATA INSTRUCTIONS

HEAP STACK

MEMORY

DISK

Jika di eksekusi maka: di LOAD ke MEMORY

PROGRAM berubah menjadi PROSES

dan akan memiliki STATE

Gambar 2.1 Program Vs Proses

Page 5: Praktikum Sistem Operasi GNU/Linux

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.

fork ( )

X

Proses Parent running program “X”

X

Proses Child running program “X”

Memory Parent di

copy ke Child

Parent akan

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.

Page 6: Praktikum Sistem Operasi GNU/Linux

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

Page 7: Praktikum Sistem Operasi GNU/Linux

Tabel 2.1 Deskripsi dari setiap State Process pada GNU/Linux

PROCESS

STATE KETERANGAN

S Sleeping. Usually waiting for an event to occur, such as a signal or input to

become available.

R ‘u i g. “t i tl speaki g, u a le, that is, o the u ueue eithe

executing or about to run.

D Uninterruptible Sleep (Waiting). Usually waiting for input or output to

complete.

T Stopped. Usually stopped by shell job control or the process is under the

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

Page 8: Praktikum Sistem Operasi GNU/Linux

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

Page 9: Praktikum Sistem Operasi GNU/Linux

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

Page 10: Praktikum Sistem Operasi GNU/Linux

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.

Page 11: Praktikum Sistem Operasi GNU/Linux

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

Page 12: Praktikum Sistem Operasi GNU/Linux

}

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:

Page 13: Praktikum Sistem Operasi GNU/Linux

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

Page 14: Praktikum Sistem Operasi GNU/Linux

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"

Page 15: Praktikum Sistem Operasi GNU/Linux

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

Page 16: Praktikum Sistem Operasi GNU/Linux

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

}

Page 17: Praktikum Sistem Operasi GNU/Linux

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.

Page 18: Praktikum Sistem Operasi GNU/Linux

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

Page 19: Praktikum Sistem Operasi GNU/Linux

}

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

Page 20: Praktikum Sistem Operasi GNU/Linux

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

Page 21: Praktikum Sistem Operasi GNU/Linux

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 .

Page 22: Praktikum Sistem Operasi GNU/Linux

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.

Page 23: Praktikum Sistem Operasi GNU/Linux

a. Contoh perintah execl

Hasil eksekusi

Page 24: Praktikum Sistem Operasi GNU/Linux

b. Contoh perintah execlp

Hasil eksekusi

Page 25: Praktikum Sistem Operasi GNU/Linux

c. Contoh perintah execv

Hasil Eksekusi

Page 26: Praktikum Sistem Operasi GNU/Linux

3. Contoh program

Contoh 1: Parent membuat proses child kemudian child akan mengeksekusi system call exec( )

Hasil Eksekusi

Page 27: Praktikum Sistem Operasi GNU/Linux

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>

Page 28: Praktikum Sistem Operasi GNU/Linux

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

}

}

Page 29: Praktikum Sistem Operasi GNU/Linux

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>

Page 30: Praktikum Sistem Operasi GNU/Linux

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

Page 31: Praktikum Sistem Operasi GNU/Linux

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

Page 32: Praktikum Sistem Operasi GNU/Linux

Gambar 1. Nama Signal

Page 33: Praktikum Sistem Operasi GNU/Linux

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.

Page 34: Praktikum Sistem Operasi GNU/Linux

Gambar 2. Terminate Proses ILKOM (PID 3220) Melalui Command Line

Gambar 3. Proses ILKOM Berhasil Diterminate

Page 35: Praktikum Sistem Operasi GNU/Linux

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

Page 36: Praktikum Sistem Operasi GNU/Linux

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.

Page 37: Praktikum Sistem Operasi GNU/Linux

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

Page 38: Praktikum Sistem Operasi GNU/Linux

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

Page 39: Praktikum Sistem Operasi GNU/Linux

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

Page 40: Praktikum Sistem Operasi GNU/Linux

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 :

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;

}

#include <pthread.h>

int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void

*(*start_rtn)(void), void *restrict arg)

Page 41: Praktikum Sistem Operasi GNU/Linux

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.

Page 42: Praktikum Sistem Operasi GNU/Linux

Jika decompile dengan benar maka hasil running tampak pada gambar di bawah ini.

3. Melihat Thread yang Sedang Running

Perhatikan gambar di bawah ini

Page 43: Praktikum Sistem Operasi GNU/Linux

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.

Page 44: Praktikum Sistem Operasi GNU/Linux

Jika decompile dan dieksekusi, hasilnya seperti ini.

Page 45: Praktikum Sistem Operasi GNU/Linux

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

Page 46: Praktikum Sistem Operasi GNU/Linux

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

Page 47: Praktikum Sistem Operasi GNU/Linux

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

a = r;

a++;

r = a;

pri tf r = %d\ , r ;

THREAD B

b = r;

b -- ;

r = b;

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.

Page 48: Praktikum Sistem Operasi GNU/Linux

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

Page 49: Praktikum Sistem Operasi GNU/Linux

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.

Page 50: Praktikum Sistem Operasi GNU/Linux

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

Page 51: Praktikum Sistem Operasi GNU/Linux

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)

Page 52: Praktikum Sistem Operasi GNU/Linux

Kode program hasil modifikasi:

Page 53: Praktikum Sistem Operasi GNU/Linux

Hasil eksekusi kode program di atas:

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.

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 )

Page 54: Praktikum Sistem Operasi GNU/Linux

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.

Page 55: Praktikum Sistem Operasi GNU/Linux

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

Page 56: Praktikum Sistem Operasi GNU/Linux

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.

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.

Page 57: Praktikum Sistem Operasi GNU/Linux

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;

Page 58: Praktikum Sistem Operasi GNU/Linux

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

}

}

Page 59: Praktikum Sistem Operasi GNU/Linux

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.

Page 60: Praktikum Sistem Operasi GNU/Linux

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

Page 61: Praktikum Sistem Operasi GNU/Linux

CONTOH KASUS 1 TANPA SEMAPHORE

Page 62: Praktikum Sistem Operasi GNU/Linux

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.

Page 63: Praktikum Sistem Operasi GNU/Linux

Pada ga a di atas, se apho e a g e a a osua dii isialisasi INIT & osua, de ga e e i

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

Page 64: Praktikum Sistem Operasi GNU/Linux

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;

}

Page 65: Praktikum Sistem Operasi GNU/Linux

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

Page 66: Praktikum Sistem Operasi GNU/Linux

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

Page 67: Praktikum Sistem Operasi GNU/Linux

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

Page 68: Praktikum Sistem Operasi GNU/Linux

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

Page 69: Praktikum Sistem Operasi GNU/Linux

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.

Page 70: Praktikum Sistem Operasi GNU/Linux

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.

Page 71: Praktikum Sistem Operasi GNU/Linux

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:

Page 72: Praktikum Sistem Operasi GNU/Linux

8. Eksperimen

NANTI SAYA AKAN BERIKAN DI DALAM KELAS !!!!!!!!!!!