Halo semuanya. Pada artikel ini saya ingin berbagi pengalaman membuat modul kernel Linux yang sederhana. Artikel ini akan berguna bagi mereka yang ingin memahami cara menulis modul kernel, tetapi tidak tahu harus mulai dari mana.
Saya sudah lama ingin memahami topik ini, tetapi sampai saat ini saya tidak tahu bagaimana cara mendekatinya. Saya ingin modulnya cukup sederhana, tetapi lebih rumit dari pesan βHalo dunia!β output dalam file log. Pada akhirnya, saya memutuskan untuk mencoba memasang LED. Tujuan tambahan adalah untuk menyimpulkan parameter yang bertanggung jawab atas frekuensi berkedip di sysfs.
Untuk percobaan, saya menggunakan papan Orange Pi One dengan Ubuntu Linux on board (kernel versi 3.4.113). Untuk membangun modul kernel, Anda memerlukan kompiler gcc, utilitas make, dan file header kernel. Untuk menginstal file header, jalankan perintah berikut:
sudo apt-get install linux-headers-$(uname -r)
Berikutnya, saya akan menganalisis menurut saya bagian paling menarik dari modul. Untuk menghemat ruang, saya tidak akan memberikan semua kode di sini, tersedia di
github bersama dengan file make.
Dalam modul, saya menggunakan file header:
#include <linux/kernel.h> #include <linux/module.h> #include <linux/gpio.h> #include <linux/hrtimer.h> #include <linux/moduleparam.h>
kernel.h dan module.h harus selalu diaktifkan ketika menulis modul kernel, gpio.h sebenarnya bertanggung jawab untuk bekerja dengan GPIO, hrtimer.h (timer resolusi tinggi) adalah file header timer, moduleparam.h diperlukan untuk menampilkan parameter dalam sysfs.
Agar tidak menyinari variabel dan fungsinya di kernel sistem, semuanya harus digambarkan sebagai statis. Untuk jaga-jaga, saya perhatikan bahwa kernel ditulis dalam C dan statis, tidak seperti C ++, itu berarti bahwa objek hanya tersedia di dalam file yang dapat dieksekusi.
Titik masuknya adalah:
static int blink_module_init(void)
Di sini saya menginisialisasi variabel yang akan saya gunakan di masa depan, termasuk:
gpio_timer_interval = ktime_set(gpio_blink_interval_s, 0);
ktime_set menginisialisasi tipe data ktime_t dengan memberikannya jumlah detik yang diinginkan (gpio_blink_interval_s) dan nanoseconds (0). Di masa depan, variabel ini akan digunakan oleh penghitung waktu.
Berikut ini adalah permintaan untuk menggunakan GPIO:
err = gpio_request(BLINK_PIN_NR, "blink_led");
Fungsi ini mengembalikan 0 jika berhasil, jadi di masa depan saya memeriksa apakah itu kembali. Selanjutnya, pin yang dipilih harus diatur ke output sinyal dan tentukan nilai default.
err = gpio_direction_output(BLINK_PIN_NR, GPIOF_INIT_LOW);
Jika tidak ada kesalahan, maka inisialisasi dan mulai timer
hrtimer_init(&gpio_blink_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); gpio_blink_timer.function = &gpio_blink_timer_callback; hrtimer_start(&gpio_blink_timer, gpio_timer_interval, HRTIMER_MODE_REL);
Fungsi panggilan balik waktu akan menjadi gpio_blink_timer_callback. Dalam fungsi ini, saya mengubah nilai pin ke kebalikannya
gpio_value ^= 0x01; gpio_set_value(BLINK_PIN_NR, gpio_value);
Saya bertanya kapan penghitung waktu akan bekerja di waktu berikutnya
hrtimer_forward_now(&gpio_blink_timer, gpio_timer_interval);
dan kembalikan HRTIMER_RESTART.
Sekarang, mari kita lihat bagaimana cara menunjukkan beberapa jenis variabel di sysfs. Untuk ini saya menggunakan makro
module_param_cb(gpio_blink_interval_s, &kp_ops, &gpio_blink_interval_s, 0660);
Parameter pertama makro ini adalah nama file di sysfs. Yang kedua adalah struktur data yang berisi fungsi panggilan balik. Parameter ketiga adalah pointer ke variabel nyata dan izin file keempat di sysfs.
Fungsi dari kp_ops dipanggil ketika pengguna mengubah nilai file sysfs atau membaca nilainya. Inilah cara saya menginisialisasi mereka:
static const struct kernel_param_ops kp_ops = { .set = &set_blink_interval, .get = &get_blink_interval };
Dalam hal ini, set menarik, karena set nilai baru, gpio_timer_interval.
gpio_timer_interval = ktime_set(gpio_blink_interval_s, 0);
Di titik keluar, saya menghapus semua sumber daya yang digunakan
static void blink_module_exit(void) { hrtimer_cancel(&gpio_blink_timer); gpio_set_value(BLINK_PIN_NR, 0); gpio_free(BLINK_PIN_NR); printk(KERN_ALERT "Blink module unloaded\n"); }
Titik masuk dan keluar harus ditunjukkan dalam makro yang sesuai
module_init(blink_module_init); module_exit(blink_module_exit);
Tampaknya menggambarkan semua poin penting. Jika pembaca memiliki pertanyaan, saya akan dengan senang hati menjawabnya di komentar.