Tutorial Panggilan Sistem Linux dengan C

Linux System Call Tutorial With C



Dalam artikel terakhir kami mengenai Panggilan Sistem Linux , Saya menentukan panggilan sistem, membincangkan sebab-sebab seseorang menggunakannya dalam program, dan mengetahui kelebihan dan kekurangannya. Saya bahkan memberikan contoh ringkas dalam perhimpunan di C. Ini menggambarkan intinya dan menerangkan cara membuat panggilan, tetapi tidak ada yang produktif. Bukan betul-betul latihan pembangunan yang mendebarkan, tetapi ini menggambarkan intinya.

Dalam artikel ini, kami akan menggunakan panggilan sistem yang sebenarnya untuk melakukan kerja sebenar dalam program C kami. Pertama, kami akan mengkaji jika anda perlu menggunakan panggilan sistem, kemudian memberikan contoh menggunakan panggilan sendfile () yang dapat meningkatkan prestasi penyalinan fail secara dramatik. Akhirnya, kami akan membahas beberapa perkara yang perlu diingat semasa menggunakan panggilan sistem Linux.







Walaupun tidak dapat dielakkan anda akan menggunakan panggilan sistem pada suatu ketika dalam karier pengembangan C anda, melainkan jika anda menyasarkan prestasi tinggi atau fungsi jenis tertentu, perpustakaan glibc dan perpustakaan asas lain yang termasuk dalam pengedaran Linux utama akan mengurus sebahagian besar keperluan anda.



Perpustakaan standard glibc menyediakan kerangka rentas platform yang diuji dengan baik untuk melaksanakan fungsi yang sebaliknya memerlukan panggilan sistem khusus sistem. Contohnya, anda boleh membaca fail dengan fscanf (), fread (), getc (), dll., Atau anda boleh menggunakan panggilan sistem baca () Linux. Fungsi glibc memberikan lebih banyak ciri (iaitu pengendalian ralat yang lebih baik, IO yang diformat, dll.) Dan akan berfungsi pada mana-mana sistem yang menyokong glibc.



Sebaliknya, ada kalanya prestasi tanpa kompromi dan pelaksanaan yang tepat sangat penting. Pembungkus yang disediakan oleh fread () akan menambahkan overhead, dan walaupun kecil, tidak sepenuhnya telus. Selain itu, anda mungkin tidak mahu atau memerlukan ciri tambahan yang disediakan pembungkus. Sekiranya demikian, anda paling senang dilayan dengan panggilan sistem.





Anda juga boleh menggunakan panggilan sistem untuk melakukan fungsi yang belum disokong oleh glibc. Sekiranya salinan glibc anda terkini, ini tidak akan menjadi masalah, tetapi pengembangan pada pengedaran yang lebih lama dengan kernel yang lebih baru mungkin memerlukan teknik ini.

Sekarang setelah anda membaca penafian, peringatan, dan kemungkinan jalan memutar, sekarang mari kita menggali beberapa contoh praktikal.



CPU Apa Yang Kita Hidup?

Satu soalan yang mungkin tidak difikirkan oleh kebanyakan program, tetapi tetap betul. Ini adalah contoh panggilan sistem yang tidak dapat digandakan dengan glibc dan tidak ditutup dengan pembungkus glibc. Dalam kod ini, kita akan memanggil panggilan getcpu () secara langsung melalui fungsi syscall (). Fungsi syscall berfungsi seperti berikut:

syscall(SYS_call,arg1,arg2,...);

Argumen pertama, SYS_call, adalah definisi yang mewakili bilangan panggilan sistem. Apabila anda memasukkan sys / syscall.h, ini disertakan. Bahagian pertama adalah SYS_ dan bahagian kedua adalah nama panggilan sistem.

Hujah untuk panggilan masuk ke arg1, arg2 di atas. Sebilangan panggilan memerlukan lebih banyak hujah, dan ia akan disambung mengikut urutan dari halaman manual mereka. Ingatlah bahawa kebanyakan argumen, terutama untuk pengembalian, memerlukan penunjuk untuk menyusun array atau memori yang diperuntukkan melalui fungsi malloc.

contoh1.c

#sertakan
#sertakan
#sertakan
#sertakan

intutama() {

tidak bertandatanganCPU,simpul;

// Dapatkan inti CPU dan nod NUMA semasa melalui panggilan sistem
// Perhatikan ini tidak mempunyai pembungkus glibc jadi kita mesti memanggilnya secara langsung
syscall(SYS_getcpu, &CPU, &simpul,BULAN);

// Paparkan maklumat
printf ('Program ini dijalankan pada CPU core% u dan NUMA node% u. n n',CPU,simpul);

kembali 0;

}

Untuk menyusun dan menjalankan:

contoh gcc1.c -o contoh1
./contoh1

Untuk hasil yang lebih menarik, anda boleh memutar utas melalui pustaka pthreads dan kemudian memanggil fungsi ini untuk melihat pemproses utas yang anda jalankan.

Sendfile: Prestasi Unggul

Sendfile memberikan contoh terbaik untuk meningkatkan prestasi melalui panggilan sistem. Fungsi sendfile () menyalin data dari satu deskriptor fail ke yang lain. Daripada menggunakan beberapa fungsi fread () dan fwrite (), sendfile melakukan pemindahan di ruang kernel, mengurangkan overhead dan dengan itu meningkatkan prestasi.

Dalam contoh ini, kita akan menyalin 64 MB data dari satu fail ke fail lain. Dalam satu ujian, kami akan menggunakan kaedah membaca / menulis standard di perpustakaan standard. Di tempat lain, kami akan menggunakan panggilan sistem dan panggilan sendfile () untuk melancarkan data ini dari satu lokasi ke lokasi lain.

ujian1.c (glibc)

#sertakan
#sertakan
#sertakan
#sertakan

#tentukan BUFFER_SIZE 67108864
#tentukan BUFFER_1 'penyangga1'
#tentukan BUFFER_2 'buffer2'

intutama() {

FILE*salah, *akhir;

printf (' nUji I / O dengan fungsi glibc tradisional. n n');

// Rebut buffer BUFFER_SIZE.
// Penyangga akan mempunyai data rawak di dalamnya tetapi kami tidak mempedulikannya.
printf ('Memperuntukkan penyangga 64 MB:');
char *penyangga= (char *) malloc (BUFFER_SIZE);
printf ('SELESAI n');

// Tulis penyangga ke fOut
printf ('Menulis data ke penyangga pertama:');
salah= fopen (BUFFER_1, 'wb');
menuliskan (penyangga, saiz(char),BUFFER_SIZE,salah);
fclose (salah);
printf ('SELESAI n');

printf ('Menyalin data dari fail pertama ke kedua:');
akhir= fopen (BUFFER_1, 'rb');
salah= fopen (BUFFER_2, 'wb');
penakut (penyangga, saiz(char),BUFFER_SIZE,akhir);
menuliskan (penyangga, saiz(char),BUFFER_SIZE,salah);
fclose (akhir);
fclose (salah);
printf ('SELESAI n');

printf ('Membebaskan penampan:');
percuma (penyangga);
printf ('SELESAI n');

printf ('Memadam fail:');
buang (BUFFER_1);
buang (BUFFER_2);
printf ('SELESAI n');

kembali 0;

}

test2.c (panggilan sistem)

#sertakan
#sertakan
#sertakan
#sertakan
#sertakan
#sertakan
#sertakan
#sertakan
#sertakan

#tentukan BUFFER_SIZE 67108864

intutama() {

intsalah,akhir;

printf (' nUjian I / O dengan sendfile () dan panggilan sistem yang berkaitan. n n');

// Rebut buffer BUFFER_SIZE.
// Penyangga akan mempunyai data rawak di dalamnya tetapi kami tidak mempedulikannya.
printf ('Memperuntukkan penyangga 64 MB:');
char *penyangga= (char *) malloc (BUFFER_SIZE);
printf ('SELESAI n');


// Tulis penyangga ke fOut
printf ('Menulis data ke penyangga pertama:');
salah=buka('penyangga1',O_RDONLY);
menulis(salah, &penyangga,BUFFER_SIZE);
tutup(salah);
printf ('SELESAI n');

printf ('Menyalin data dari fail pertama ke kedua:');
akhir=buka('penyangga1',O_RDONLY);
salah=buka('penyangga2',O_RDONLY);
hantar fail(salah,akhir, 0,BUFFER_SIZE);
tutup(akhir);
tutup(salah);
printf ('SELESAI n');

printf ('Membebaskan penampan:');
percuma (penyangga);
printf ('SELESAI n');

printf ('Memadam fail:');
nyahpaut('penyangga1');
nyahpaut('penyangga2');
printf ('SELESAI n');

kembali 0;

}

Menyusun dan Menjalankan Ujian 1 & 2

Untuk membina contoh-contoh ini, anda memerlukan alat pengembangan yang dipasang pada pengedaran anda. Di Debian dan Ubuntu, anda boleh memasangnya dengan:

tepatpasangmembina-keperluan

Kemudian kompilasi dengan:

gccujian1.c-atauujian1&& gccujian2.c-atauujian2

Untuk menjalankan kedua-duanya dan menguji prestasinya, jalankan:

masa./ujian1&& masa./ujian2

Anda akan mendapat hasil seperti ini:

Uji I / O dengan fungsi glibc tradisional.

Memperuntukkan penimbal 64 MB: SELESAI
Menulis data ke penyangga pertama: SELESAI
Menyalin data dari fail pertama ke kedua: SELESAI
Membebaskan penampan: SELESAI
Memadam fail: SELESAI
sebenar 0m0.397s
pengguna 0m0.000s
sys 0m0.203s
Ujian I / O dengan sendfile () dan panggilan sistem yang berkaitan.
Memperuntukkan penimbal 64 MB: SELESAI
Menulis data ke penyangga pertama: SELESAI
Menyalin data dari fail pertama ke kedua: SELESAI
Membebaskan penampan: SELESAI
Memadam fail: SELESAI
0m0.019s sebenar
pengguna 0m0.000s
sys 0m0.016s

Seperti yang anda lihat, kod yang menggunakan panggilan sistem berjalan lebih pantas daripada setara glibc.

Perkara yang Perlu Diingat

Panggilan sistem dapat meningkatkan prestasi dan memberikan fungsi tambahan, tetapi bukan tanpa kekurangannya. Anda harus mempertimbangkan faedah yang diberikan oleh sistem panggilan daripada kekurangan portabiliti platform dan kadang-kadang mengurangkan fungsi berbanding fungsi perpustakaan.

Semasa menggunakan beberapa panggilan sistem, anda mesti berhati-hati menggunakan sumber yang dikembalikan dari panggilan sistem dan bukannya fungsi perpustakaan. Sebagai contoh, struktur FILE yang digunakan untuk fungsi glibc's fopen (), fread (), fwrite (), dan fclose () tidak sama dengan nombor deskriptor fail dari panggilan sistem terbuka () (dikembalikan sebagai bilangan bulat). Mencampurkan ini boleh menimbulkan masalah.

Secara umum, panggilan sistem Linux mempunyai jalur bumper yang lebih sedikit daripada fungsi glibc. Walaupun benar bahawa panggilan sistem mempunyai beberapa pengendalian dan pelaporan ralat, anda akan mendapat fungsi yang lebih terperinci dari fungsi glibc.

Dan akhirnya, kata keselamatan. Panggilan sistem bersambung terus dengan kernel. Kernel Linux mempunyai perlindungan yang luas terhadap shenanigans dari tanah pengguna, tetapi ada bug yang belum ditemui. Jangan percaya bahawa panggilan sistem akan mengesahkan input anda atau mengasingkan anda dari masalah keselamatan. Adalah bijak untuk memastikan data yang anda berikan kepada panggilan sistem dibersihkan. Secara semula jadi, ini adalah nasihat yang baik untuk sebarang panggilan API, tetapi anda tidak boleh berhati-hati ketika bekerja dengan kernel.

Saya harap anda menikmati penyelaman sistem Linux yang lebih mendalam ini. Untuk senarai lengkap Panggilan Sistem Linux, lihat senarai induk kami.