GNU/Linux >> Belajar Linux >  >> Linux

Mengapa beberapa pemrogram kernel menggunakan goto alih-alih simple while loops?

Konteks historis: Kita harus ingat bahwa Dijkstra menulis Goto Dianggap Berbahaya pada tahun 1968, ketika banyak programmer menggunakan goto sebagai pengganti pemrograman terstruktur (if , while , for , dll.).

Ini 44 tahun kemudian, dan jarang ditemukan penggunaan goto ini di alam liar. Pemrograman terstruktur sudah lama menang.

Analisis kasus:

Kode contoh terlihat seperti ini:

    SETUP...
again:
    COMPUTE SOME VALUES...
    if (cmpxchg64(ptr, old_val, val) != old_val)
        goto again;

Versi terstrukturnya terlihat seperti ini:

SETUP...
do {
    COMPUTE SOME VALUES...
} while (cmpxchg64(ptr, old_val, val) != old_val);

Saat saya melihat versi terstrukturnya, saya langsung berpikir, "ini sebuah lingkaran". Ketika saya melihat goto versi, saya menganggapnya sebagai garis lurus dengan kasus "coba lagi" di bagian akhir.

goto versi keduanya memiliki SETUP dan COMPUTE SOME VALUES pada kolom yang sama, yang menekankan bahwa sebagian besar waktu, aliran kontrol melewati keduanya. Versi terstruktur menempatkan SETUP dan COMPUTE SOME VALUES pada kolom yang berbeda, yang menekankan bahwa kontrol dapat melewatinya secara berbeda.

Pertanyaannya di sini adalah penekanan seperti apa yang ingin Anda masukkan ke dalam kode? Anda dapat membandingkannya dengan goto untuk penanganan kesalahan:

Versi terstruktur:

if (do_something() != ERR) {
    if (do_something2() != ERR) {
        if (do_something3() != ERR) {
            if (do_something4() != ERR) {
                ...

Versi Goto:

if (do_something() == ERR)  // Straight line
    goto error;             // |
if (do_something2() == ERR) // |
    goto error;             // |
if (do_something3() == ERR) // |
    goto error;             // V
if (do_something4() == ERR) // emphasizes normal control flow
    goto error;

Kode yang dihasilkan pada dasarnya sama, jadi kita dapat menganggapnya sebagai masalah tipografi, seperti lekukan.


Pertanyaan yang sangat bagus, dan menurut saya hanya penulis yang dapat memberikan jawaban pasti. Saya akan menambahkan sedikit spekulasi saya dengan mengatakan bahwa itu bisa dimulai dengan menggunakannya untuk penanganan kesalahan, seperti yang dijelaskan oleh @Izkata dan gerbang kemudian terbuka untuk menggunakannya untuk loop dasar juga.

Penggunaan penanganan kesalahan adalah yang sah dalam pemrograman sistem, menurut pendapat saya. Suatu fungsi secara bertahap mengalokasikan memori seiring perkembangannya, dan jika terjadi kesalahan, itu akan goto label yang sesuai untuk membebaskan sumber daya dalam urutan terbalik dari titik tersebut.

Jadi, jika kesalahan terjadi setelah alokasi pertama, itu akan melompat ke label kesalahan terakhir, untuk membebaskan satu sumber daya saja. Demikian pula, jika kesalahan terjadi setelah alokasi terakhir, itu akan melompat ke label kesalahan pertama dan dijalankan dari sana, membebaskan semua sumber daya hingga akhir fungsi. Pola penanganan kesalahan seperti itu masih perlu digunakan dengan hati-hati, terutama saat memodifikasi kode, pengujian valgrind dan unit sangat dianjurkan. Tapi ini bisa dibilang lebih mudah dibaca dan dipelihara daripada pendekatan alternatif.

Satu aturan emas menggunakan goto adalah untuk menghindari apa yang disebut kode spageti. Cobalah menggambar garis di antara setiap goto pernyataan dan labelnya masing-masing. Jika Anda memiliki garis yang bersilangan, Anda telah melewati garis :). Seperti penggunaan goto sangat sulit dibaca dan merupakan sumber umum bug yang sulit dilacak, karena dapat ditemukan dalam bahasa seperti BASIC yang mengandalkannya untuk kontrol alur.

Jika Anda melakukan hanya satu putaran sederhana, Anda tidak akan mendapatkan garis yang bersilangan, jadi itu masih dapat dibaca dan dipertahankan, sebagian besar menjadi masalah gaya. Yang mengatakan, karena mereka dapat dengan mudah dilakukan dengan kata kunci loop yang disediakan bahasa, seperti yang telah Anda tunjukkan dalam pertanyaan Anda, rekomendasi saya tetap untuk menghindari penggunaan goto untuk loop, hanya karena for , do/while atau while konstruksi lebih elegan dengan desain.


Dalam kasus contoh ini, saya curiga ini tentang retrofit dukungan SMP ke dalam kode yang awalnya ditulis dengan cara yang tidak aman untuk SMP. Menambahkan goto again; path jauh lebih sederhana dan tidak terlalu invasif daripada merestrukturisasi fungsi.

Saya tidak bisa mengatakan saya sangat menyukai gaya ini, tetapi saya juga berpikir bahwa menghindari goto adalah salah arah karena alasan ideologis. Satu kasus khusus goto penggunaan (berbeda dari contoh ini) adalah tempat goto hanya digunakan untuk bergerak maju dalam suatu fungsi, tidak pernah mundur. Kelas penggunaan ini tidak pernah menghasilkan konstruksi lingkaran yang muncul dari goto , dan ini hampir selalu merupakan cara paling sederhana dan paling jelas untuk menerapkan perilaku yang diperlukan (yang biasanya membersihkan dan mengembalikan kesalahan).


Linux
  1. Gunakan pengatur waktu systemd alih-alih cronjobs

  2. Mengapa beberapa pemrogram kernel menggunakan goto alih-alih simple while loops?

  3. Mengapa fclose(NULL) glibc menyebabkan kesalahan segmentasi alih-alih mengembalikan kesalahan?

  1. Mengapa `sementara Ifs=Read` Sering Digunakan, Alih-alih `ifs=; Saat Baca..`?

  2. Linux – Mengapa Kami Menggunakan Su – Dan Bukan Hanya Su?

  3. Gunakan sed dengan huruf besar-kecil sambil menambahkan teks sebelum beberapa pola

  1. Gunakan $[ Ekspr ] Alih-alih $(( Ekspr ))?

  2. Mengapa Ubuntu 14.04 Lts Menggunakan Versi Kernel Non-lts?

  3. Kesalahan "peta sedang digunakan" saat menghapus perangkat multipath di CentOS/RHEL