Mengapa volatile
dibutuhkan C? Untuk apa ini digunakan? Apa yang akan dilakukannya?
c
declaration
volatile
Jonathan Leffler
sumber
sumber
Jawaban:
Volatile memberitahu compiler untuk tidak mengoptimalkan apa pun yang ada hubungannya dengan variabel volatile.
Setidaknya ada tiga alasan umum untuk menggunakannya, semua situasi yang melibatkan di mana nilai variabel dapat berubah tanpa tindakan dari kode yang terlihat: Ketika Anda berinteraksi dengan perangkat keras yang mengubah nilai itu sendiri; ketika ada thread lain yang berjalan yang juga menggunakan variabel; atau ketika ada penangan sinyal yang dapat mengubah nilai variabel.
Katakanlah Anda memiliki sedikit perangkat keras yang dipetakan ke dalam RAM di suatu tempat dan yang memiliki dua alamat: port perintah dan port data:
Sekarang Anda ingin mengirim beberapa perintah:
Terlihat mudah, tetapi bisa gagal karena kompiler bebas mengubah urutan penulisan data dan perintah. Ini akan menyebabkan gadget kecil kami mengeluarkan perintah dengan nilai data sebelumnya. Lihat juga lingkaran tunggu selagi sibuk. Yang itu akan dioptimalkan. Compiler akan mencoba menjadi pintar, membaca nilai isbusy hanya sekali dan kemudian masuk ke infinite loop. Bukan itu yang Anda inginkan.
Cara menyiasatinya adalah dengan menyatakan gadget penunjuk sebagai volatile. Dengan cara ini kompiler dipaksa untuk melakukan apa yang Anda tulis. Itu tidak dapat menghapus tugas memori, itu tidak bisa cache variabel dalam register dan itu tidak bisa mengubah urutan tugas baik:
Ini adalah versi yang benar:
sumber
volatile
di C sebenarnya muncul untuk tujuan tidak caching nilai-nilai variabel secara otomatis. Ini akan memberitahu kompiler untuk tidak men-cache nilai variabel ini. Jadi itu akan menghasilkan kode untuk mengambil nilai darivolatile
variabel yang diberikan dari memori utama setiap kali bertemu dengannya. Mekanisme ini digunakan karena sewaktu-waktu nilainya dapat dimodifikasi oleh OS atau interupsi apa pun. Jadi menggunakanvolatile
akan membantu kita mengakses nilai baru setiap waktu.sumber
volatile
adalah untuk memungkinkan kompiler untuk mengoptimalkan kode sambil tetap memungkinkan programmer untuk mencapai semantik yang akan dicapai tanpa optimasi tersebut. Para penulis Standar berharap bahwa implementasi kualitas akan mendukung semantik apa pun yang berguna mengingat platform target dan bidang aplikasi mereka, dan tidak berharap bahwa penulis kompiler akan berusaha menawarkan semantik kualitas terendah yang sesuai dengan Standar dan tidak 100% bodoh (perhatikan bahwa penulis Standar secara eksplisit mengakui dalam alasan ...Penggunaan lain untuk
volatile
penangan sinyal. Jika Anda memiliki kode seperti ini:Compiler diperbolehkan untuk melihat loop body tidak menyentuh
quit
variabel dan mengubah loop menjadiwhile (true)
loop. Bahkan jikaquit
variabel diatur pada penangan sinyal untukSIGINT
danSIGTERM
; kompiler tidak memiliki cara untuk mengetahui hal itu.Namun, jika
quit
variabel tersebut dideklarasikanvolatile
, kompiler dipaksa untuk memuatnya setiap waktu, karena dapat dimodifikasi di tempat lain. Inilah yang Anda inginkan dalam situasi ini.sumber
quit
, kompiler dapat mengoptimalkannya menjadi loop konstan, dengan asumsi bahwa tidak ada cara untukquit
diubah di antara iterasi. NB: Ini bukan pengganti yang baik untuk pemrograman threadsafe sebenarnya.volatile
atau penanda lain, ia akan menganggap bahwa tidak ada yang di luar loop memodifikasi variabel begitu memasuki loop, bahkan jika itu adalah variabel global.extern int global; void fn(void) { while (global != 0) { } }
dengangcc -O3 -S
dan lihat file rakitan yang dihasilkan, di mesin saya yamovl global(%rip), %eax
;testl %eax, %eax
;je .L1
;.L4: jmp .L4
, yaitu, infinite loop jika global tidak nol. Kemudian coba tambahkanvolatile
dan lihat perbedaannya.volatile
memberitahu kompiler bahwa variabel Anda dapat diubah dengan cara lain, daripada kode yang mengaksesnya. misalnya, mungkin lokasi memori yang dipetakan I / O. Jika ini tidak ditentukan dalam kasus seperti itu, beberapa akses variabel dapat dioptimalkan, misalnya, isinya dapat disimpan dalam register, dan lokasi memori tidak dibaca kembali lagi.sumber
Lihat artikel ini oleh Andrei Alexandrescu, " volatile - Best Friend Programmer Multithreaded "
Artikel ini berlaku untuk keduanya
C
danC++
.Juga lihat artikel " C ++ dan Perils of Double-Checked Locking " oleh Scott Meyers dan Andrei Alexandrescu:
sumber
volatile
tidak menjamin atomicity.Penjelasan sederhana saya adalah:
Dalam beberapa skenario, berdasarkan logika atau kode, kompiler akan melakukan optimalisasi variabel yang dianggapnya tidak berubah. Kata
volatile
kunci mencegah variabel dioptimalkan.Sebagai contoh:
Dari kode di atas, kompiler mungkin berpikir
usb_interface_flag
didefinisikan sebagai 0, dan bahwa dalam loop sementara itu akan menjadi nol selamanya. Setelah optimasi, kompiler akan memperlakukannyawhile(true)
sepanjang waktu, menghasilkan loop tak terbatas.Untuk menghindari skenario semacam ini, kami menyatakan flag sebagai volatile, kami mengatakan kepada kompiler bahwa nilai ini dapat diubah oleh antarmuka eksternal atau modul program lainnya, yaitu, tolong jangan optimalkan. Itulah use case untuk volatile.
sumber
Penggunaan marginal untuk volatile adalah sebagai berikut. Katakanlah Anda ingin menghitung turunan numerik dari suatu fungsi
f
:Masalahnya adalah bahwa
x+h-x
umumnya tidak sama denganh
karena kesalahan pembulatan. Pikirkan tentang hal ini: ketika Anda mengurangi angka yang sangat dekat, Anda kehilangan banyak digit signifikan yang dapat merusak perhitungan derivatif (pikirkan 1,00001 - 1). Solusi yang mungkin bisa dilakukantetapi tergantung pada platform Anda dan sakelar kompiler, baris kedua dari fungsi itu dapat dihilangkan oleh kompiler yang mengoptimalkan secara agresif. Jadi, Anda yang menulis
untuk memaksa kompiler membaca lokasi memori yang mengandung hh, kehilangan peluang optimasi akhirnya.
sumber
h
atauhh
dalam formula turunan? Ketikahh
dihitung, rumus terakhir menggunakannya seperti yang pertama, tanpa perbedaan. Mungkin seharusnya begitu(f(x+h) - f(x))/hh
?h
danhh
adalah yanghh
terpotong ke beberapa kekuatan negatif dari dua oleh operasix + h - x
. Dalam hal ini,x + hh
danx
berbeda persis olehhh
. Anda juga dapat mengambil formula Anda, itu akan memberikan hasil yang sama, karenax + h
danx + hh
sama (itu adalah penyebut yang penting di sini).x1=x+h; d = (f(x1)-f(x))/(x1-x)
? tanpa menggunakan volatile.-ffast-math
atau setara.Ada dua kegunaan. Ini secara khusus lebih sering digunakan dalam pengembangan tertanam.
Compiler tidak akan mengoptimalkan fungsi yang menggunakan variabel yang didefinisikan dengan kata kunci yang mudah menguap
Volatile digunakan untuk mengakses lokasi memori yang tepat dalam RAM, ROM, dll. Ini digunakan lebih sering untuk mengontrol perangkat yang dipetakan memori, mengakses register CPU dan menemukan lokasi memori tertentu.
Lihat contoh dengan daftar perakitan. Re: Penggunaan Kata Kunci C "volatile" dalam Pengembangan Tertanam
sumber
Volatile juga berguna, ketika Anda ingin memaksa kompiler untuk tidak mengoptimalkan urutan kode tertentu (misalnya untuk menulis tolok ukur mikro).
sumber
Saya akan menyebutkan skenario lain di mana volatil penting.
Misalkan Anda memetakan file memori untuk I / O yang lebih cepat dan file itu dapat berubah di belakang layar (mis. File tersebut tidak ada di hard drive lokal Anda, tetapi sebaliknya dilayani melalui jaringan oleh komputer lain).
Jika Anda mengakses data file yang dipetakan memori melalui pointer ke objek yang tidak mudah menguap (pada tingkat kode sumber), maka kode yang dihasilkan oleh kompiler dapat mengambil data yang sama beberapa kali tanpa Anda menyadarinya.
Jika data itu berubah, program Anda mungkin menggunakan dua atau lebih versi data yang berbeda dan masuk ke kondisi tidak konsisten. Hal ini tidak hanya menyebabkan perilaku program yang salah secara logis, tetapi juga lubang keamanan yang dapat dieksploitasi jika program memproses file yang tidak dipercaya atau file dari lokasi yang tidak terpercaya.
Jika Anda peduli dengan keamanan, dan Anda harus melakukannya, ini adalah skenario penting untuk dipertimbangkan.
sumber
volatile berarti penyimpanan cenderung berubah kapan saja dan diubah tetapi sesuatu di luar kendali program pengguna. Ini berarti bahwa jika Anda mereferensikan variabel, program harus selalu memeriksa alamat fisik (mis. Input fifo yang dipetakan), dan tidak menggunakannya dengan cara di-cache.
sumber
Wiki mengatakan segalanya tentang
volatile
:Dan kernel Linux doc juga membuat notasi yang bagus tentang
volatile
:sumber
Menurut pendapat saya, Anda tidak harus berharap terlalu banyak dari
volatile
. Untuk mengilustrasikannya, lihat contoh di jawaban yang sangat dipilih oleh Nils Pipenbrinck .Saya akan mengatakan, teladannya tidak cocok untuk
volatile
.volatile
hanya digunakan untuk: mencegah kompiler membuat optimasi yang berguna dan diinginkan . Ini bukan apa-apa tentang thread thread, akses atom atau bahkan urutan memori.Dalam contoh itu:
yang
gadget->data = data
sebelumnyagadget->command = command
hanya hanya dijamin dalam kode dikompilasi oleh compiler. Pada waktu berjalan, prosesor masih dapat menata ulang data dan penugasan perintah, terkait dengan arsitektur prosesor. Perangkat keras bisa mendapatkan data yang salah (misalkan gadget dipetakan ke perangkat keras I / O). Penghalang memori diperlukan antara data dan penugasan perintah.sumber
volatile
menurunkan kinerja tanpa alasan. Adapun apakah itu cukup, itu akan tergantung pada aspek-aspek lain dari sistem yang programmer mungkin tahu lebih banyak tentang daripada kompiler. Di sisi lain, jika prosesor menjamin bahwa instruksi untuk menulis ke alamat tertentu akan mem-flush cache CPU tetapi kompiler tidak memberikan cara untuk mem-flush variabel cache yang tidak diketahui CPU, pembilasan cache tidak akan berguna.Dalam bahasa yang dirancang oleh Dennis Ritchie, setiap akses ke objek apa pun, selain objek otomatis yang alamatnya tidak diambil, akan berperilaku seolah-olah itu menghitung alamat objek dan kemudian membaca atau menulis penyimpanan di alamat itu. Ini membuat bahasa ini sangat kuat, tetapi peluang optimasi sangat terbatas.
Walaupun mungkin mungkin untuk menambahkan kualifikasi yang akan mengundang kompiler untuk mengasumsikan bahwa objek tertentu tidak akan diubah dengan cara yang aneh, asumsi seperti itu akan sesuai untuk sebagian besar objek dalam program C, dan itu akan memiliki tidak praktis untuk menambahkan kualifikasi ke semua objek yang anggapan seperti itu akan sesuai. Di sisi lain, beberapa program perlu menggunakan beberapa objek yang asumsi seperti itu tidak berlaku. Untuk mengatasi masalah ini, Standar mengatakan bahwa kompilator dapat mengasumsikan bahwa objek yang tidak dideklarasikan tidak
volatile
akan memiliki nilainya diamati atau diubah dengan cara yang berada di luar kendali kompiler, atau akan berada di luar pemahaman kompiler yang masuk akal.Karena berbagai platform mungkin memiliki cara berbeda di mana objek dapat diamati atau dimodifikasi di luar kendali kompiler, sudah sepantasnya bahwa kompiler berkualitas untuk platform tersebut harus berbeda dalam penanganan
volatile
semantik yang tepat . Sayangnya, karena Standar gagal untuk menyarankan bahwa kompiler berkualitas yang dimaksudkan untuk pemrograman tingkat rendah pada platform harus menanganivolatile
dengan cara yang akan mengenali setiap dan semua efek yang relevan dari operasi baca / tulis tertentu pada platform itu, banyak kompiler gagal melakukan jadi dengan cara yang membuatnya lebih sulit untuk memproses hal-hal seperti latar belakang I / O dengan cara yang efisien tetapi tidak dapat dipecah oleh "optimisasi" kompiler.sumber
Secara sederhana, ini memberitahu kompiler untuk tidak melakukan optimasi pada variabel tertentu. Variabel yang dipetakan ke register perangkat dimodifikasi secara tidak langsung oleh perangkat. Dalam hal ini, volatile harus digunakan.
sumber
Volatile dapat diubah dari luar kode yang dikompilasi (misalnya, suatu program dapat memetakan variabel volatil ke register yang dipetakan memori.) Kompiler tidak akan menerapkan optimasi tertentu untuk kode yang menangani variabel volatil - misalnya, ia tidak akan t memuatnya ke dalam register tanpa menuliskannya ke memori. Ini penting ketika berhadapan dengan register perangkat keras.
sumber
Seperti yang disarankan oleh banyak orang di sini, penggunaan populer kata kunci yang mudah menguap adalah untuk melewatkan pengoptimalan variabel yang mudah menguap.
Keuntungan terbaik yang terlintas dalam pikiran, dan layak disebutkan setelah membaca tentang volatile adalah - untuk mencegah tergulingnya variabel dalam kasus a
longjmp
. Lompatan non-lokal.Apa artinya ini?
Ini berarti bahwa nilai terakhir akan dipertahankan setelah Anda melakukan stack unwinding , untuk kembali ke beberapa frame stack sebelumnya; biasanya dalam kasus beberapa skenario yang salah.
Karena itu akan keluar dari ruang lingkup pertanyaan ini, saya tidak akan membahas detail
setjmp/longjmp
sini, tetapi perlu membaca tentang hal itu; dan bagaimana fitur volatilitas dapat digunakan untuk mempertahankan nilai terakhir.sumber
itu tidak memungkinkan kompiler untuk secara otomatis mengubah nilai variabel. variabel volatil adalah untuk penggunaan dinamis.
sumber