Bab 22. Sinkronisasi Linux
Pendahuluan
Concurrent access dapat terjadi dalam sebuah kernel, termasuk kernel linux. Concurrent access adalah beberapa thread yang sedang berjalan mengakses resources yang sama
dalam waktu yang sama. Jika hal ini terjadi, thread-thread tersebut akan saling
meng-overwrite perubahan yang dilakukan thread sesamanya, sebelum perubahan tersebut
mencapai state yang konsisten. Sehingga hasil dari proses menjadi tidak seperti yang diharapkan. Oleh karena itu, diperlukan proteksi dalam kernel yang bersangkutan. Proteksi yang dapat dilakukan adalah dengan sinkronisasi.
Proteksi resources dari concurrent access bukanlah merupakan hal yang mudah. Beberapa tahun yang lalu sebelum Linux mendukung adanya symmetrical
multiprocessing, proteksi masih mudah dilakukan karena hanya ada satu processor yang
didukung. Satu-satunya cara bagi resources dapat diakses secara concurrent (bersama-sama) oleh thread adalah ketika ada interrupt atau memang ada jadwal dari kernel bahwa
thread lain diperbolehkan untuk mengakses resources tersebut.
Namun dengan berkembangnya zaman, Linux akhirnya mendukung adanya symmetrical
multiprocessing dalam versi 2.0 kernelnya. Multiprocessing artinya kernel code dapat
dijalankan dalam dua atau lebih processor. Jika tanpa proteksi, code yang dijalankan dalam dua processor yang berbeda dapat mengakses resources yang sama dalam waktu yang sama. Dengan adanya kernel Linux 2.6 yang mendukung adanya konsep
preemptive, scheduler dalam kernel dapat meng-interrupt kernel code yang sedang
berjalan untuk memberi kesempatan bagi kernel code lain untuk dijalankan. Dengan demikian pengaksesan resources yang sama dalam waktu yang sama dapat dihindari.
Dalam bab ini akan dijelaskan bagaimana implementasi sinkronisasi dalam Linux. Metode-metode sinkronisasi yang dibahas meliputi integer atomik yang merupakan salah satu jenis dari operasi atomik, locking yang terdiri dari spin lock dan semafor, dan dijelaskan hal-hal lain yang terkait dengan pembahasan ini.
Critical Section
Sebuah proses memiliki bagian dimana bagian ini akan melakukan akses dan manipulasi data. Bagian ini disebut dengan Critical section. Ketika sebuah proses sedang dijalankan dalam critical sectionnya, tidak ada proses lain yang boleh dijalankan dalam critical
sectionnya karena hal ini dapat memungkinkan terjadinya akses ke resources yang sama
dan dalam waktu yang sama. Keadaan seperti ini disebut sebagai proses mutually
exclusive. Oleh karena itu, diperlukan suatu mekanisme atau aturan agar proses bersifat mutually exclusive dapat terpenuhi.
Dengan cara mengontrol variabel mana yang berubah baik di dalam maupun di luar
saat program multithreading, dimana program yang terdiri dari banyak thread dapat mengubah nilai dari variabel. Dalam hal ini critical section diperlukan untuk melindungi variabel dari concurrent access yang dapat membuat nilai dari variabel tersebut menjadi tidak konsisten.
Lalu bagaimana critical section tersebut diimplementasikan di dalam sistem operasi ? Metode yang paling sederhana adalah dengan mencegah adanya thread lain yang mengubah variabel yang sedang digunakan dalam critical section. Selain itu, system call yang dapat menyebabkan context switch juga dihindari. Jika scheduler meng-interrupt proses yang sedang mengakses critical sectionnya, maka scheduler akan membiarkan proses tersebut menyelesaikan critical sectionnya atau menghentikannya sementara untuk memberi kesempatan bagi proses lain untuk menjalankan critical sectionnya. Proses yang sedang berada dalam critical sectionnya dijalankan secara mutually exclusive.
Contoh 22.1. Critical section
do{
critical section
}while(1)
Penyebab Konkurensi Kernel
Sinkronisasi diperlukan karena adanya program yang dijadwalkan secara preemptive. Karena proses dapat di interrupt maka proses yang lain dapat masuk ke processor menggantikannya untuk dijalankan. Hal ini memungkinkan proses awal di interrupt di
critical regionnya. Jika ada proses baru yang dijadwalkan untuk dijalankan selanjutnya
masuk ke critical region yang sama, maka race condition akan terjadi. Ada dua jenis
concurrency yaitu pseudo-concurrency dan true-concurrency. Pseudo-concurrency
terjadi ketika dua proses tidak benar-benar berjalan dalam waktu yang tepat sama namun ada waktu jeda diantara keduanya. Sedangkan true-concurrency terjadi ketika dua proses berjalan secara bersama-sama yang waktu yang sama. True-concurrency biasanya terjadi pada komputer yang berbasis symmetrical multiprocessing.
Ada beberapa penyebab konkurensi kernel, diantaranya:
• Interrupt. Interrupt dapat terjadi sewaktu-waktu, menghentikan sementara proses
yang sedang berjalan.
• Softirqs dan Tasklets. Kernel dapat menjadwalkan Softirqs dan Tasklets
sewaktu-waktu untuk menghentikan sementara proses yang sedang berjalan.
Softirqs dan Tasklets adalah pengatur interrupt untuk interrupt-interrupt yang
tidak dapat dilakukan interrupt-handler yang biasa. Softirqs dapat berjalan secara bersama-sama dalam beberapa processor bahkan dua Softirqs yang sama dapat berjalan bersama-sama. Sifat tasklets hampir sama dengan Softirqs, hanya saja dua tasklets yang sama tidak dapat berjalan secara bersama-sama.
• Kernel Preemption. Karena kernel bersifat preemptive, sebuah proses yang
sedang berjalan dapat dihentikan sementara oleh proses yang lain.
• Sleeping dan Syncronization with user-space. Sebuah proses dalam kernel dapat sleep dan meminta kernel untuk menjalankan proses yang lain.
• Symmetrical Multiprocessing. Dua atau lebih processor dapat menjalankan kernel code dalam waktu yang sama.
Integer Atomik
Salah satu metode dalam kernel Linux untuk sinkronisasi adalah atomic operations. Integer atomik adalah salah satu jenis dari atomic operations. Atomic operations menyediakan instruksi yang dijalankan secara atomik ( tanpa interrupt ). Contohnya sebuah atomic increment dapat membaca dan meng-increment sebuah variabel dalam sebuah step yang tidak dapat dibagi-bagi. Misalnya i diinisialisasi sama dengan 7
Gambar 22.1. Atomic Operation
Thread 1 Thread 2
---Atomic increment i (7 -> 8) -
- Atomic increment i (8->9)
Atomic increment pada thread 1 akan dijalankan sampai selesai tanpa interrupt sehingga
hasil dari thread pertama adalah 8. Setelah itu barulah thread 2 dijalankan. Mula-mula nilai i yang dibaca adalah 8 kemudian di iincrement sehingga hasil akhirnya 9.
Methods integer atomik hanya berjalan untuk tipe data atomic_t. Fungsi atomik akan
menggunakan tipe data ini dan tidak akan mengirim tipe data ini ke fungsi nonatomik. Dalam penggunaannya, compiler tidak mengakses langsung nilai dari data ini namun yang diakses adalah alamat memorinya. Integer atomik ( atomic_t ) memiliki 32 bits dalam representasinya. Namun yang dipakai untuk menyimpan suatu nilai hanya 24 bits karena 8 bits sisanya dipakai untuk lock yang berfungsi untuk memproteksi concurrent
access ke data atomic yang bersangkutan. Hal ini diimplementasikan dalam SPARC port
Gambar 22.2. 32-bit atomic_t
Untuk menggunakan atomic integer operations perlu dideklarasikan terlebih dahulu
<asm/atomic.h>. Berikut ini contoh penggunaan atomic integer : atomic_t v; atomic_t v = ATOMIC_INIT(0); atomic_set(&v,4); atomic_add(2,&v); atomic_inc(&v); printk("%d\n",atomic_read(&v));
Fungsi atomic_t v akan mendefinisikan variabel v sebagai sebuah integer atomik.
Kemudian variabel v akan diinisialisasi menjadi 0 oleh fungsi ATOMIC_INIT(0). Untuk
menset nilai v, misalnya menjadi 4 secara atomik dapat digunakan fungsi
atomic_set(&v,4). Menambah nilai v secara atomik dengan menggunakan fungsi atomic_add(2,&v). Variabel v dapat juga dikenakan atomic increment dengan
menggunakan fungsi atomic_inc(&v). Fungsi printk("%d\n",atomic_read(&v))
akan mencetak nilai v.
Berikut ini beberapa Atomic Integer Operations :
Tabel 22.1. Tabel Atomic Integer Operations
Atomic Integer Operation Description
ATOMIC_INIT(int i) inisialisasi atomic_t menjadi i
atomic_read(atomic_t *v) membaca nilai integer dari v
void atomic_set(atomic_t *v,int i) set nilai v menjadi i
void atomic_add(int i,atomic_t *v) menambah v sebesar i
void atomic_sub(int i,atomic_t *v) mengurangi c sebesar i
void atomic_inc(atomic_t *v) menambah v dengan 1
Spin Locks
Metode Locking adalah salah satu metode dalam sinkronisasi yang diperlukan untuk menjaga agar tidak ada proses yang berjalan bersama-sama, yang dapat memungkinkan adanya concurrent access ke resources yang sama. Locking akan menutup proses yang sedang berjalan dalam critical region-nya dari proses yang lain sehingga hanya akan ada satu proses yang sedang berjalan dalam sebuah processor. Locking yang paling umum digunakan dalam Linux adalah spin lock. Spin lock adalah lock yang hanya dapat dilakukan oleh satu thread. Ketika sebuah thread yang akan dijalankan meminta spin
lock yang sedang digunakan, maka thread ini akan loops menunggu sampai spin lock
tersebut selesai digunakan oleh thread yang sedang berjalan. Jika spin lock sedang tidak digunakan maka thread yang akan dijalankan akan langsung mendapatkannya. Hal ini akan mencegah adanya dua thread yang dijalankan bersama-sama. Dalam spin lock tidak diperbolehkan kernel preemption. Berikut ini contoh penggunaan spin lock :
Sebelum menggunakan spin lock, perlu dideklarasikan <asm\spinlock.h>. spinlock-t mr_lock = SPIN_LOCK_UNLOCKED;
unsigned long flags; spin_lock_irqsave(&mr_lock,flags);
/ * critical region … * /
spin_unlock_irqrestore(&mr_lock,flags);
Fungsi SPIN_LOCK_UNLOCKED menyatakan bahwa variabel yang bersangkutan, dalam hal
ini mr_lock, sedang tidak menjalankan spin lock. Fungsi spin_lock_irqsave(&mr_lock,flags) menyebabkan mr_lock mendapatkan spin lock
dan menyimpan state terakhir dari thread yang di interrupt. Setelah itu, mr_lock akan
masuk ke critical regionnya. Setelah selesai maka mr_lock melepaskan spin lock dengan
menggunakan spin_unlock_irqrestore(&mr_lock,flags) dan thread yang di
interrupt kembali ke state terakhirnya.
Berikut ini beberapa spin lock methods :
Tabel 22.2. Tabel Spin Lock Methods
Method Description
spin_lock() Mendapatkan lock
spin_lock_irq() Menginterrupt dan mendapatkan lock
spin_lock_irqsave() Menyimpan current state dari local interrupt dan
mendapatkan lock
Method Description
spin_unlock_irq() Melepaskan lock dan enable local interrupt
spin_unlock_irqrestore() Melepaskan lock dan memberi local interrupt previous
statenya
Semafor
Semafor dalam Linux adalah sleeping locks. Ketika sebuah thread meminta semafor yang sedang digunakan, maka semafor akan meletakkan thread tersebut dalam wait queue dan menyebabkan thread tersebut masuk status sleep. Kemudian processor menjalankan
thread yang lain. Ketika thread yang memegang semafor melepaskan locknya, maka satu
dari thread yang ada di wait queue akan dipanggil sehingga akan mendapatkan semafor.
Beberapa konklusi yang dapat diambil berkaitan dengan sleeping yang terjadi dalam semafor diantaranya :
• Karena thread yang sedang menunggu giliran untuk dijalankan masuk dalam status sleep, maka semafor cocok untuk lock yang digunakan untuk waktu yang cukup lama.
• Sebaliknya, semafor tidak cocok untuk lock yang digunakan dalam waktu yang singkat karena waktu yang digunakan untuk sleeping, menjaga wait queue, dan membangunkan thread yang sedang sleep akan menambah waktu lock.
• Thread yang sedang memegang semafor dapat sleep dan tidak akan deadlock
ketika ada thread yang lain yang mendapatkan semafor yang sama karena thread tersebut akan sleep dan membiarkan thread yang pertama untuk melanjutkan eksekusinya.
• Suatu thread tidak dapat memegang spin lock ketika menunggu untuk mendapatkan semafor karena thread tersebut harus sleep dan tidak dapat sleep dengan memegang spin lock.
Berbeda dengan spin lock, semafor memperbolehkan adanya kernel preemption. Dalam semafor juga diperbolehkan pemegang semafor lebih dari satu dalam suatu waktu yang sama. Banyaknya pemegang lock di sebut usage count. Nilai yang paling umum untuk
usage count adalah satu yang artinya hanya ada satu thread yang berjalan dalam suatu
waktu. Dalam hal ini jika usage count sama dengan 1 maka disebut binary semaphore atau mutex. Implementasi dari semafor didefinisikan dalam <asm\semaphore.h>. Semafor dapat dibuat dengan cara sebagai berikut :
static DECLARE_SEMAPHORE_GENERIC(name,count)
dimana name menyatakan nama variabel dan count adalah usage count dari semafor. static DECLARE_MUTEX(name)
Berikut ini contoh implementasi semaphore : static DECLARE_MUTEX(mr_sem); if(down_interruptible(&mr_sem)){ /*critical region*/ up(&mr_sem); }
Fungsi down_interruptible() digunakan untuk meminta semafor. Jika gagal, maka
variabel yang bersangkutan akan sleep dalam state TASK_INTERRUPTIBLE. Jika kemudian variabel menerima signal maka down_interruptible() akan mengembalikan
-EINTR. Sehingga fungsi down() akan membuat variabel masuk dalam state
TASK_UNINTERRUPTIBLE dan akan dijalankan dalam critical regionnya. Untuk melepaskan semafor, digunakan fungsi up().Mengetahui apakah akan menggunakan spin
lock atau semafor adalah cara yang baik untuk mengoptimalkan code yang dibuat.
Berikut ini tabel mengenai apa yang diperlukan untuk menentukan lock mana yang digunakan.
Tabel 22.3. Tabel Spin Lock Versus Semaphore
Requirement Recommended
Overhead locking yang rendah Spin lock Lock hold time yang singkat Spin lock Lock hold time yang panjang Semaphore Sleep ketika menunggu lock Semaphore
SMP
Symmetrical Multiprocessing (SMP) mendukung adanya pengeksekusian secara paralel
dua atau lebih thread oleh dua atau lebih processor. Kernel Linux 2.0 adalah kernel Linux pertama yang memperkenalkan konsep SMP. Untuk menjaga agar dua thread tidak mengakses resources yang sama dalam waktu yang sama, maka SMP menerapkan aturan dimana hanya ada satu processor yang dapat menjalankan thread dalam kernel mode dengan cara spin lock tunggal untuk menjalankan aturan ini. Spin lock ini tidak memunculkan permasalahan untuk proses yang banyak menghabiskan waktu untuk menunggu proses komputasi, tapi untuk proses yang banyak melibatkan banyak aktifitas kernel, spin lock menjadi sangat mengkhawatirkan. Sebuah proyek yang besar dalam pengembangan kernel Linux 2.1 adalah untuk menciptakan penerapan SMP yang lebih masuk akal, dengan membagi kernel spin lock tunggal menjadi banyak lock yang masing-masing melindungi terhadap masuknya kembali sebagian kecil data struktur kernel.
Dengan menggunakan teknik ini, pengembangan kernel yang terbaru mengizinkan banyak processor untuk dieksekusi oleh kernel mode secara bersamaan.
Rangkuman
Pada suatu saat dalam sebuah kernel, tidak terkecuali kernel Linux, dapat terjadi
concurrent access. Dalam hal ini diperlukan proteksi dalam kernel yang bersangkutan.
Proteksi dapat dilakukan dengan sinkronisasi.
Sebuah proses memiliki bagian dimana bagian ini akan melakukan akses dan manipulasi data. Bagian ini disebut dengan critical section. Ketika sebuah proses sedang dijalankan dalam critical sectionnya, tidak ada proses lain yang boleh dijalankan dalam critical
sectionnya.
Ada dua jenis concurrency yaitu pseudo-concurrency dan true-concurrency. Ada beberapa penyebab konkurensi kernel, diantaranya interrupt, softirqs dan tasklets, kernel
preemption, sleeping dan synchronization with user-space, dan symmetrical multiprocessing.
Salah satu metode dalam kernel Linux untuk sinkronisasi adalah atomic operations. Integer atomik adalah salah satu jenis dari atomic operations. Integer Atomik menyediakan instruksi yang dijalankan secara atomik ( tanpa interrupt).
Locking yang paling umum digunakan dalam Linux adalah spin lock. Spin lock adalah lock yang hanya dapat dilakukan oleh satu thread. Ketika sebuah thread yang akan
dijalankan meminta spin lock yang sedang digunakan, maka thread ini akan loops menunggu sampai spin lock tersebut selesai digunakan oleh thread yang sedang berjalan.
Semafor dalam Linux adalah sleeping locks. Ketika sebuah thread meminta semafor yang sedang digunakan, maka semafor akan meletakkan thread tersebut dalam wait queue dan menyebabkan thread tersebut masuk status sleep.
Symmetrical multiprocessing (SMP) mendukung adanya pengeksekusian secara paralel
dua atau lebih thread oleh dua atau lebih processor. Kernel Linux 2.0 adalah kernel Linux pertama yang memperkenalkan konsep SMP.
Rujukan
[RobertLove2005] Robert Love. 2005. Linux Kernel Development. Novell Press.
[Silberschatz2005] Avi Silberschatz, Peter Galvin, dan Grag Gagne. 2005. Operating
Systems Concepts. Seventh Edition. John Wiley & Sons.
[Stallings2001] William Stallings. 2001. Operating Systems: Internal and Design
[WEBDrake96] Donald G Drake. April 1996. Introduction to Java threads – A quick
tutorial on how to implement threads in Java – http://www.javaworld.com/ javaworld/ jw-04-1996/ jw-04-threads.html . Diakses 29 Mei 2006.
[WEBFasilkom2003] Fakultas Ilmu Komputer Universitas Indonesia . 2003. Sistem
Terdistribusi – http://telaga.cs.ui.ac.id/ WebKuliah/ sisdis2003/ . Diakses 29 Mei 2006. [WEBHarris2003] Kenneth Harris. 2003. Cooperation: Interprocess Communication –
Concurrent Processing – http://people.cs.uchicago.edu/ ~mark/ 51081/ LabFAQ/ lab5/ IPC.html . Diakses 2 Juni 2006.
[WEBWalton1996] Sean Walton. 1996. Linux Threads Frequently Asked Questions
(FAQ) – http://linas.org/ linux/ threads-faq.html . Diakses 29 Mei 2006.
[WEBWiki2006a] From Wikipedia, the free encyclopedia. 2006. Title – http://en.wikipedia.org/wiki/Zombie_process . Diakses 2 Juni 2006.
[WEBLINUX2002] Robert Love. 2002. Kernel Locking Techniques http://www.linuxjournal.com/article/5833/ . Diakses 3 Maret 2007.
[WEBwikipedia2007a] From Wikipedia, the free encyclopedia. 2007.. 2007. Symmetric
Multiprocessing http://en.wikipedia.org/wiki/Symmetric_multiprocessing/ . Diakses 3 Maret 2007.
[WEBwikipedia2007] From Wikipedia, the free encyclopedia. 2007.. 2007. Critical
Appendix
Perbandingan Sinkronisasi yang dilakukan pada Linux
dan Windows
Kernel code memanggil fungsi penjadwalan sendiri
Setiap waktu banyak proses yang berjalan dalam kernel mode, akibatnya sangat mungkin untuk terjadi race condition, contoh, dalam kernel mode terdapat struktur data yang menyimpan list file yang terbuka, list tersebut termodofikasi bila ada data file yang baru dibuka atau ditutup,dengan menambah atau menghapus dari list, race condition timbul ketika ada dua file yang dibuka dan ditutup bersamaan, untuk mengatasi hal tersebut kernel mempunyai metode yaitu:
a. Preemptive kernel.
Pada mode ini proses yang sedang dieksekusi dalam kernel diizinkan untuk diinterupsi oleh proses lain yang memenuhi syarat, akibatnya mode ini juga rentan terkena race
condition. Keuntungannya adalah mode ini amat efektif untuk digunakan dalam real time programming, namun mode ini lebih sulit diimplementasikan dari pada mode non preemptive kernel. Mode ini diimplementasikan oleh Linux versi 2.6 dan versi komersial
linux lainnya.
b. Non preemptive kernel.
Mode yang tidak memperbolehkan suatu proses yang berjalan dalam kernel mode diinterupsi oleh proses lain, proses lain hanya bisa dijalankan setelah proses yang ada dalam kernel selesai dieksekusi, implementasinya memang lebih mudah dibandingkan dengan preemptive kernel, mode ini diimplementasikan lewat Windows XP dan Windows 2000.