01.07.2013 Views

Review Konsep Thread - Bebas - vLSM

Review Konsep Thread - Bebas - vLSM

Review Konsep Thread - Bebas - vLSM

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Review</strong> <strong>Konsep</strong> <strong>Thread</strong><br />

Anggota Kelompok<br />

1. Genta Moerita (A 0606104265)<br />

2. Yuan Hanif Syaniardi (B 0606031603)<br />

3. Yunus Kuntawi Aji (B 0606031616)<br />

Komentar Umum<br />

Secara umum menurut kami bab 11 mengenai konsep thread ini sudah cukup jelas dalam<br />

menggambarkan multithreading. Gambar yang tersedia juga sudah cukup memberikan<br />

pemahaman terhadap konsep model-model multithreading. Beberapa hal yang perlu<br />

ditambahkan adalah kerugian dari multithreading dan contoh thread pada Sistem Operasi lain.<br />

Hubungan dengan bab sebelumnya/selanjutnya<br />

Bab ini menerangkan tentang multithreading dimana dalam suatu waktu bisa terjadi lebih dari<br />

satu proses yang konsepnya telah dijelaskan pada bab sebelumnya, yaitu bab 10 tentang<br />

<strong>Konsep</strong> Proses.<br />

Bab selanjutnya membahas tentang penggunaan thread pada java, thread bisa memiliki status<br />

new, runnable, block, dead.<br />

Komentar kelengkapan per bagian<br />

11.1 Bagian ini sudah lengkap karena sudah menerangkan definisi dari multithreading dan<br />

alasan membuat thread<br />

11.2 Bagian ini masih kurang lengkap, sebaiknya ditambah dengan kekurangan multithreading<br />

11.3 Ilustrasi gambar pada bab ini sudah cukup menjelaskan secara lengkap tentang model<br />

multithreading<br />

11.4 Bab ini perlu ditambah lagi tentang penjelasan pustaka thread<br />

11.5 Pada penjelasan cara pemberhentian thread perlu sedikit pengubahan redaksi kalimat<br />

11.6 Di dalam bab ini perlu ditambahkan definisi mengenai <strong>Thread</strong> Pools<br />

11.7 - Terdapat kesalahan penulisan pada paragraf dua baris pertama pada kata multithreading<br />

- Untuk memperjelas pemahaman mungkin perlu ditambahkan ilustrasi gambar<br />

11.8 Perlu diberi contoh thread dari sistem operasi yang lain untuk memperkaya pemahaman<br />

dan pengetahuan.<br />

11.9 Di dalam kesimpulan perlu ditambahkan rangkuman tentang pustaka thread<br />

Usulan kelengkapan<br />

11.1 Perlu ada sedikit pengubahan redaksi<br />

11.2 Bagian ini sebaiknya ditambah dengan kekurangan multithreading


11.3 - Perlu ditambahkan contoh pada model many-to-many<br />

- Perlu penjelasan lebih mengenai kelebihan dan kekurangan tiap model<br />

11.4 Perlu diberikan definisi atau penjelasan lebih tentang pustaka thread<br />

11.5 Perlu dilakukan perubahan redaksi kalimat dalam cara pemberhentian thread<br />

11.6 Perlu ditambahkan definisi mengenai <strong>Thread</strong> Pools<br />

11.7 Tambahkan ilustrasi gambar<br />

11.8 Perlu diberi contoh thread dari sistem operasi yang lain untuk memperkaya pemahaman<br />

dan pengetahuan.<br />

11.9 Di dalam kesimpulan perlu ditambahkan rangkuman tentang pustaka thread


Bab 11. <strong>Konsep</strong> <strong>Thread</strong><br />

11.1. Pendahuluan<br />

Pada bab sebelumnya kita telah mempelajari tentang proses, namun seiring berjalannya waktu<br />

dan tuntutan teknologi ternyata ditemukan kelemahan yang sebenarnya bisa diminimalisir pada<br />

proses. Untuk itulah diciptakan thread yang merupakan cara dari komputer untuk menjalankan<br />

dua atau lebih task dalam waktu bersamaan, sedangkan multithreading adalah cara komputer<br />

untuk membagi-bagi pekerjaan yang dikerjakan sebagian-sebagian dengan cepat sehingga<br />

menimbulkan efek seperti menjalankan beberapa task secara bersamaan walaupun otaknya<br />

hanya satu. Di sini kita akan belajar mengapa harus ada thread, perbedaan thread dengan<br />

proses, keuntungan dan kerugian pengimplementasian thread, model-model multithreading,<br />

pengimplementasian pustaka thread, pembatalan thread, thread pools, penjadwalan thread dan<br />

thread di Linux.<br />

11.2. Keuntungan dan Kerugian Multi<strong>Thread</strong>ing<br />

Multiprocessing merupakan penggunaan dua atau lebih CPU dalam sebuah sistem komputer.<br />

Multitasking merupakan metode untuk menjalankan lebih dari satu proses dimana terjadi<br />

pembagian sumberdaya seperti CPU. Multithreading adalah cara pengeksekusian yang<br />

mengizinkan beberapa thread terjadi dalam sebuah proses, saling berbagi sumber daya tetapi<br />

dapat dijalankan secara independen.<br />

Keuntungan dari sistem yang menerapkan multithreading dapat kita kategorikan menjadi 4<br />

bagian:<br />

a. Responsif. Aplikasi interaktif menjadi tetap responsif meskipun sebagian dari program sedang<br />

diblok atau melakukan operasi lain yang panjang. Umpamanya, sebuah thread dari web browser<br />

dapat melayani permintaan pengguna sementara thread yang lain berusaha menampilkan<br />

gambar.<br />

b. Berbagi sumber daya. Beberapa thread yang melakukan proses yang sama akan berbagi<br />

sumber daya. Keuntungannya adalah mengizinkan sebuah aplikasi untuk mempunyai beberapa<br />

thread yang berbeda dalam lokasi memori yang sama.<br />

c. Ekonomis. Pembuatan sebuah proses memerlukan pengalokasian memori dan sumber daya.<br />

Alternatifnya adalah dengan menggunakan thread, karena thread membagi memori dan sumber<br />

daya yang dimilikinya sehingga lebih ekonomis untuk membuat thread dan context switching<br />

thread. Akan susah mengukur perbedaan waktu antara thread dan switch, tetapi secara umum<br />

pembuatan dan pengaturan proses akan memakan waktu lebih lama dibandingkan dengan<br />

thread. Pada Solaris, pembuatan proses memakan waktu 30 kali lebih lama dibandingkan<br />

pembuatan thread sedangkan proses context switch 5 kali lebih lama dibandingkan context<br />

switching thread.<br />

d. Utilisasi arsitektur multiprosesor. Keuntungan dari multithreading dapat sangat meningkat<br />

pada arsitektur multiprosesor, dimana setiap thread dapat berjalan secara paralel di atas<br />

procesor yang berbeda. Pada arsitektur processor tunggal, CPU menjalankan setiap thread<br />

secara bergantian tetapi hal ini berlangsung sangat cepat sehingga menciptakan ilusi paralel,<br />

tetapi pada kenyataannya hanya satu thread yang dijalankan CPU pada satu-satuan waktu.<br />

Adapun kerugian dari multithreading adalah :<br />

1. Jika digunakan secara berlebihan, multithreading akan berdampak pada pemborosan<br />

resource dan CPU yang dialokasikan untuk switching threads. Misalnya jika heavy disk I/O<br />

terlibat, akan lebih cepat jika hanya memiliki 1 atau 2 thread yang melaksanakan tugas secara<br />

berurutan, daripada menggunakan multithread yang masing-masing mengeksekusi sebuah task<br />

pada waktu yang sama.


2. Sistem yang memiliki kecepatan prosesor dan memory yang cenderung sama, sehingga tidak<br />

ada efisiensi yang hilang (mengacu kepada latency), tidak akan memperoleh peningkatan<br />

bandwidth yang signifikan jika menggunakan multithreading.<br />

3. Multithreading menghasilkan program yang lebih kompleks. Menggunakan multiple thread<br />

sendiri tidak akan menciptakan kerumitan, tapi interaksi antar thread-lah yang mengakibatkan<br />

kompleksitas tersebut.<br />

4. <strong>Thread</strong> yang banyak bisa saling berinterferensi ketika saling berbagi sumber daya hardware<br />

seperti cache.<br />

11.3. Model Multi<strong>Thread</strong>ing<br />

Beberapa terminologi yang akan dibahas:<br />

a. <strong>Thread</strong> pengguna: <strong>Thread</strong> yang pengaturannya dilakukan oleh pustaka thread pada tingkatan<br />

pengguna. Karena pustaka yang menyediakan fasilitas untuk pembuatan dan penjadwalan<br />

thread, thread pengguna cepat dibuat dan dikendalikan.<br />

b. <strong>Thread</strong> Kernel: . <strong>Thread</strong> yang didukung langsung oleh kernel. Pembuatan, penjadwalan dan<br />

manajemen thread dilakukan oleh kernel pada kernel space. Karena dilakukan oleh sistem<br />

operasi, proses pembuatannya akan lebih lambat jika dibandingkan dengan thread pengguna.<br />

Gambar 11.1. Model-Model Multi<strong>Thread</strong>ing<br />

Model-Model Multi<strong>Thread</strong>ing:<br />

a. Model Many-to-One. Model ini memetakan beberapa thread tingkatan pengguna ke sebuah<br />

thread. tingkatan kernel. Pengaturan thread dilakukan dalam ruang pengguna sehingga efisien.<br />

Hanya satu thread pengguna yang dapat mengakses thread kernel pada satu saat. Jadi Multiple<br />

thread tidak dapat berjalan secara paralel pada multiprosesor. Kekurangannya adalah ketika ada<br />

satu blocking systemc call, semua akan menjadi terblok juga. Contoh: Solaris Green <strong>Thread</strong>s dan<br />

GNU Portable <strong>Thread</strong>s.<br />

b. Model One-to-One. Model ini memetakan setiap thread tingkatan pengguna ke setiap thread.<br />

Ia menyediakan lebih banyak concurrency dibandingkan model Many-to-One. Keuntungannya<br />

sama dengan keuntungan thread kernel. Kelemahan model ini ialah setiap pembuatan thread<br />

pengguna memerlukan tambahan thread kernel. Karena itu, jika mengimplementasikan sistem ini<br />

maka akan menurunkan kinerja dari sebuah aplikasi sehingga biasanya jumlah thread dibatasi<br />

dalam sistem. Contoh: Windows NT/XP/2000 , Linux, Solaris 9, OS/2.


c. Model Many-to-Many. Model ini memultipleks banyak thread tingkatan pengguna ke thread<br />

kernel yang jumlahnya sedikit atau sama dengan tingkatan pengguna. Model ini mengizinkan<br />

developer membuat thread sebanyak yang ia mau tetapi concurrency tidak dapat diperoleh<br />

karena hanya satu thread yang dapat dijadwalkan oleh kernel pada suatu waktu. Keuntungan<br />

dari sistem ini ialah kernel thread yang bersangkutan dapat berjalan secara paralel pada<br />

multiprosessor dan lebih efisien. Contoh : Solaris 2, IRIX, HPUX.<br />

11.4. Pustaka <strong>Thread</strong><br />

Pustaka <strong>Thread</strong> atau yang lebih familiar dikenal dengan <strong>Thread</strong> Library bertugas untuk<br />

menyediakan API untuk programmer dalam menciptakan dan memanage thread. Ada dua cara<br />

dalam mengimplementasikan pustaka thread:<br />

a. Menyediakan API dalam level pengguna tanpa dukungan dari kernel sehingga pemanggilan<br />

fungsi tidak melalui system call. Jadi, jika kita memanggil fungsi yang sudah ada di pustaka,<br />

maka akan menghasilkan pemanggilan fungsi call yang sifatnya lokal dan bukan system call.<br />

b. Menyediakan API di level kernel yang didukung secara langsung oleh sistem operasi.<br />

Pemanggilan fungsi call akan melibatkan system call ke kernel.<br />

Ada tiga pustaka thread yang sering digunakan saat ini, yaitu: POSIX Pthreads, Java, dan Win32.<br />

Implementasi POSIX standard dapat dengan cara user level dan kernel level, sedangkan Win32<br />

adalah kernel level. Java API thread dapat diimplementasikan oleh Pthreads atau Win32.<br />

11.5. Pembatalan <strong>Thread</strong><br />

<strong>Thread</strong> Cancellation ialah pembatalan thread sebelum tugasnya selesai. Misalnya hendak<br />

mematikan Java Virtual Machine (JVM) pada program Java. Maka sebelum JVM dimatikan<br />

seluruh thread yang berjalan harus dibatalkan terlebih dahulu. Contoh lain adalah pada masalah<br />

search. Apabila sebuah thread mencari sesuatu dalam database dan menemukan serta<br />

mengembalikan hasilnya, thread sisanya akan dibatalkan. <strong>Thread</strong> yang akan diberhentikan biasa<br />

disebut target thread.<br />

Pemberhentian target <strong>Thread</strong> dapat dilakukan dengan 2 cara:<br />

a. Asynchronous cancellation. Suatu thread seketika itu juga membatalkan target thread.<br />

b. Deferred cancellation. Suatu thread secara periodik memeriksa apakah ia harus batal, cara<br />

ini memperbolehkan target thread untuk membatalkan dirinya secara terurut.<br />

Hal yang sulit dari pembatalan thread ini adalah ketika terjadi situasi dimana sumber daya sudah<br />

dialokasikan untuk thread yang akan dibatalkan. Selain itu kesulitan lain adalah ketika thread<br />

yang dibatalkan sedang meng-update data yang ia bagi dengan thread lain. Hal ini akan menjadi<br />

masalah yang sulit apabila digunakan asynchronous cancellation. Sistem operasi akan<br />

mengambil kembali sumber daya dari thread yang dibatalkan tetapi seringkali sistem operasi<br />

tidak mengambil kembali semua sumber daya dari thread yang dibatalkan.<br />

Alternatifnya adalah dengan menggunakan deffered cancellation. Cara kerja dari deffered<br />

cancellation adalah dengan menggunakan satu thread yang berfungsi sebagai pengindikasi<br />

bahwa target thread hendak dibatalkan. Tetapi pembatalan hanya akan terjadi jika target thread<br />

memeriksa apakah ia harus batal atau tidak. Hal ini memperbolehkan thread untuk memeriksa<br />

apakah ia harus batal pada waktu dimana ia dapat dibatalkan secara aman yang aman. Pthread<br />

merujuk sebagai cancellation points.<br />

Pada umumnya sistem operasi memperbolehkan proses atau thread untuk dibatalkan secara<br />

asynchronous. Tetapi Pthread API menyediakan deferred cancellation. Hal ini berarti sistem<br />

operasi yang mengimplementasikan Pthread API akan mengizinkan deferred cancellation.


11.6. <strong>Thread</strong> Pools<br />

Pada web server yang menerapkan multithreading ada dua masalah yang timbul:<br />

a. Ukuran waktu yang diperlukan untuk menciptakan thread yang melayani permintaan yang<br />

diajukan pada kenyataannya thread dibuang seketika sesudah ia menyelesaikan tugasnya.<br />

b. Pembuatan thread yang tidak terbatas jumlahnya dapat menurunkan performa dari sistem.<br />

Solusinya adalah dengan penggunaan <strong>Thread</strong> Pools, yaitu sekumpulan thread yang mengantri<br />

untuk mengerjakan tugas Cara kerjanya adalah dengan membuat beberapa thread pada proses<br />

startup dan menempatkan mereka ke pools, dimana mereka duduk diam dan menunggu untuk<br />

bekerja. Jadi, ketika server menerima permintaan, ia akan membangunkan thread dari pool dan<br />

jika thread tersedia maka permintaan tersebut akan dilayani.<br />

Ketika thread sudah selesai mengerjakan tugasnya maka ia kembali ke pool dan menunggu<br />

pekerjaan lainnya. Bila tidak ada thread yang tersedia pada saat dibutuhkan maka server<br />

menunggu sampai ada satu thread yang bebas.<br />

Keuntungan thread pool adalah:<br />

a. Biasanya lebih cepat untuk melayani permintaan dengan thread yang ada dibandingkan<br />

menunggu thread baru dibuat.<br />

b. <strong>Thread</strong> pool membatasi jumlah thread yang ada pada suatu waktu. Hal ini penting pada sistem<br />

yang tidak dapat mendukung banyak thread yang berjalan secara concurrent. Jumlah thread<br />

dalam pool dapat tergantung dari jumlah CPU dalam sistem, jumlah memori fisik, dan jumlah<br />

permintaan klien yang concurrent.<br />

c. Pembuatan jumlah thread yang tepat dapat meningkatkan performa serta sistem yang lebih<br />

stabil


11.7. Penjadwalan <strong>Thread</strong><br />

Begitu dibuat, thread baru dapat dijalankan dengan berbagai macam penjadwalan. Kebijakan<br />

penjadwalanlah yang menentukan setiap proses, di mana proses tersebut akan ditaruh dalam<br />

daftar proses sesuai proritasnya dan bagaimana ia bergerak dalam daftar proses tersebut.<br />

Untuk menjadwalkan thread, sistem dengan model multithreading many to many atau many to<br />

one menggunakan:<br />

a. Process Contention Scope (PCS). Pustaka thread menjadwalkan thread pengguna untuk<br />

berjalan pada LWP (lightweight process) yang tersedia.<br />

b. System Contention Scope (SCS). SCS berfungsi untuk memilih satu dari banyak thread,<br />

kemudian menjadwalkannya ke satu thread tertentu (CPU / Kernel).<br />

Gambar 11.2. Penjadwalan <strong>Thread</strong>


11.8. <strong>Thread</strong> Linux<br />

Ketika pertama kali dikembangkan, Linux tidak didukung dengan threading di dalam kernelnya,<br />

tetapi dia mendukung proses-proses sebagai entitas yang dapat dijadwalkan melalui clone()<br />

system calls. Sekarang Linux mendukung penduplikasian proses menggunakan system call<br />

clone() dan fork(). Clone() mempunyai sifat mirip dengan fork(), kecuali dalam hal pembuatan<br />

salinan dari proses yang dipanggil dimana ia membuat sebuah proses yang terpisah yang<br />

berbagi address space dengan proses yang dipanggil. Pembagian address space dari parent<br />

process memungkinkan cloned task bersifat mirip dengan thread yang terpisah. Pembagian<br />

address space ini dimungkinkan karena proses direpresentasikan di dalam Kernel Linux. Di<br />

dalam Kernel Linux setiap proses direpresentasikan sebagai sebuah struktur data yang unik.<br />

Jadi, daripada menciptakan yang baru maka struktur data yang baru mengandung pointer yang<br />

menunjuk ke tempat dimana data berada. Jadi ketika fork() dipanggil, proses yang baru akan<br />

tercipta beserta duplikasi dari segala isi di struktur data di parent process, namun ketika clone()<br />

dipanggil, ia tidak menduplikasi parent process-nya tetapi menciptakan pointer ke struktur data<br />

pada parent process yang memungkinkan child process untuk berbagi memori dan sumber daya<br />

dari parent process-nya. Project Linux <strong>Thread</strong> menggunakan system call ini untuk mensimulasi<br />

thread di user space. Sayangnya, pendekatan ini mempunyai beberapa kekurangan, khususnya<br />

di area signal handling, scheduling, dan interprocess synchronization primitive.<br />

Untuk meningkatkan kemampuan <strong>Thread</strong> Linux, dukungan kernel dan penulisan ulang pustaka<br />

thread sangat diperlukan. Dua project yang saling bersaing menjawab tantangan ini. Sebuah tim<br />

yang terdiri dari pengembang dari IBM membuat NGPT (Next Generation POSIX <strong>Thread</strong>s).<br />

Sementara pengembang dari Red Hat membuat NPTL (Native POSIX <strong>Thread</strong> Library).<br />

Sebenarnya Linux tidak membedakan antara proses dan thread. Dalam kenyataannya, Linux<br />

lebih menggunakan istilah task dibandingkan proses dan thread ketika merujuk kepada<br />

pengaturan alur pengontrolan di dalam program.<br />

Pada sistem operasi lain seperti Mac OS X, terdapat lima thread API yang berbeda, yaitu: Mach<br />

thread, POSIX thread (pthreads), Cocoa thread (NS<strong>Thread</strong>s), Carbon MP tasks, dan Carbon<br />

<strong>Thread</strong> Manager. Akan tetapi tidak mudah untuk menentukan suatu thread mengerjakan tugas<br />

yang mana, sehingga dibuatlah suatu siklus yang membuat masing-masing thread bekerja<br />

secara bergantian.<br />

11.9. Rangkuman<br />

<strong>Thread</strong> adalah alur kontrol dari suatu proses.<br />

Keuntungan menggunakan Multithreading:<br />

a. Meningkatkan respon dari pengguna.<br />

b. Pembagian sumber daya.<br />

c. Ekonomis.<br />

d. Mengambil keuntungan dari arsitektur multiprosessor.<br />

Tiga model Multithreading:<br />

a. Model Many-to-One.<br />

b. Model One-to-One.<br />

c. Model Many-to-Many.<br />

Pustaka <strong>Thread</strong> bertugas untuk menyediakan API untuk programmer dalam menciptakan dan<br />

memanage thread. Cara untuk mengimplementasikannya yaitu:<br />

a. Menyediakan API dalam level pengguna tanpa dukungan dari kernel.<br />

b. Menyediakan API di level kernel yang didukung secara langsung oleh sistem operasi.


Pembatalan <strong>Thread</strong>: Tugas untuk membatalkan <strong>Thread</strong> sebelum menyelesaikan tugasnya.<br />

Pembatalan <strong>Thread</strong> terdiri dari 2 jenis:<br />

1. Asynchronous cancellation.<br />

2. Deffered cancellation.<br />

<strong>Thread</strong> Pools menciptakan sejumlah <strong>Thread</strong> yang ditempatkan di dalam pool dimana <strong>Thread</strong><br />

menunggu untuk dipanggil.<br />

<strong>Thread</strong> Schedulling ada 2 macam:<br />

1. Local Schedulling.<br />

2. Global Schedulling.<br />

Istilah thread di Linux adalah task.<br />

Pembuatan <strong>Thread</strong> di Linux menggunakan system call clone(). Sedangkan di Mac sekumpulan<br />

thread yang ada akan mengerjakan tugas bergantian dengan menggunakan siklus.


Rujukan<br />

[Silberschatz2005] Avi Silberschatz, Peter Galvin, dan Grag Gagne. 2005. Operating<br />

Systems Concepts. Seventh Edition. John Wiley & Sons.<br />

[WEBFSF1991a] Free Software Foundation. 1991. GNU General Public License –<br />

http://gnui.<strong>vLSM</strong>.org/ licenses/ gpl.txt . Diakses 29 Mei 2006.<br />

[WEBWIKI2007] Wikipedia. 2007. <strong>Thread</strong>_(computer_science) – http://en.wikipedia.org/<br />

wiki/<strong>Thread</strong>_(computer_science) . Diakses 16 Februari 2007.<br />

[WEBWIKI2007] Wikipedia.. 2007. Multiprocessing – http://en.wikipedia.org/ wiki/<br />

Multiprocessing . Diakses 16 Februari 2007.<br />

[WEBWIKI2007] Wikipedia.. 2007. Computer_multitasking – http://en.wikipedia.org/ wiki/<br />

Computer_mutitasking . Diakses 16 Februari 2007.<br />

[WEBIBM] IBM.. 2007. linux_threading – http://www-128.ibm.com/ developerworks/ java/<br />

library/j-prodcon . Diakses 16 Februari 2007.<br />

[WEBWIKI2008] Wikipedia.. 2008. <strong>Thread</strong>_pool_pattern – http://en.wikipedia.org/ wiki/<br />

<strong>Thread</strong>_pool_pattern . Diakses 8 April 2008.<br />

[WikiCH2008] Wikipedia, The Free Encyclopedia. 2008. Computer Hardware –<br />

http://en.wikipedia.org/wiki/Multithreading Diakses 7 April 2008<br />

[WikiCS2008] Wikipedia, The Free Encyclopedia. 2008. Computer Science –<br />

http://en.wikipedia.org/wiki/<strong>Thread</strong> Diakses 7 April 2008<br />

[TechnicalNote] Technical Note 2028. 2008. <strong>Thread</strong>ing Architectures –<br />

http://developer.apple.com/technotes/tn/tn2028.html. Diakses 9 April 2008


Hak Cipta © Mahasiswa Fakultas Ilmu Komputer Universitas Indonesia, peserta mata kuliah<br />

Sistem Operasi semester genap 2008<br />

1. Genta Moerita (A 0606104265)<br />

2. Yuan Hanif Syaniardi (B 0606031603)<br />

3. Yunus Kuntawi Aji (B 0606031616)<br />

Silakan menyalin, mengedarkan, dan/atau memodifikasi dokumen ini

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!