Pengaturcaraan C

Baca Syscall Linux

Baca Syscall Linux
Oleh itu, anda perlu membaca data binari? Anda mungkin mahu membaca dari FIFO atau soket? Anda lihat, anda mungkin menggunakan fungsi pustaka standard C, tetapi dengan melakukannya, anda tidak akan mendapat keuntungan dari ciri khas yang disediakan oleh Linux Kernel dan POSIX. Contohnya, anda mungkin mahu menggunakan timeout untuk membaca pada waktu tertentu tanpa memerlukan pengundian. Anda juga mungkin perlu membaca sesuatu tanpa mempedulikannya jika fail khas atau soket atau yang lain. Tugas anda hanyalah membaca beberapa kandungan binari dan memasukkannya ke dalam aplikasi anda. Di sinilah syscall yang dibaca bersinar.

Baca fail biasa dengan syscall Linux

Cara terbaik untuk mula bekerja dengan fungsi ini adalah dengan membaca fail biasa. Ini adalah kaedah termudah untuk menggunakan syscall itu, dan untuk alasan: ia tidak mempunyai sekatan seperti jenis aliran atau paip lain. Sekiranya anda memikirkannya itu logik, apabila anda membaca output aplikasi lain, anda perlu menyediakan beberapa output sebelum membacanya dan oleh itu anda perlu menunggu aplikasi ini menulis output ini.

Pertama, perbezaan utama dengan pustaka standard: Tidak ada penyangga sama sekali. Setiap kali anda memanggil fungsi baca, anda akan memanggil Kernel Linux, dan ini memerlukan masa - hampir sekejap jika anda memanggilnya sekali, tetapi boleh melambatkan anda jika anda memanggilnya ribuan kali dalam satu saat. Sebagai perbandingan, pustaka standard akan menyekat input untuk anda. Oleh itu, setiap kali anda memanggil membaca, anda harus membaca lebih daripada beberapa bait, melainkan penyangga besar seperti beberapa kilobyte - kecuali jika apa yang anda perlukan benar-benar sedikit bait, contohnya jika anda memeriksa apakah fail ada dan tidak kosong.

Ini bagaimanapun mempunyai manfaat: setiap kali anda memanggil membaca, anda pasti akan mendapat data yang dikemas kini, jika ada aplikasi lain yang mengubah suai pada saat ini file. Ini amat berguna untuk fail khas seperti di / proc atau / sys.

Masa untuk menunjukkan kepada anda dengan contoh sebenar. Program C ini memeriksa sama ada fail itu PNG atau tidak. Untuk melakukannya, ia membaca fail yang ditentukan di jalan yang anda berikan dalam argumen baris perintah, dan memeriksa apakah 8 bait pertama sesuai dengan tajuk PNG.

Inilah kodnya:

#sertakan
#sertakan
#sertakan
#sertakan
#sertakan
#sertakan
#sertakan
 
typedef enum
IS_PNG,
TERLALU PENDEK,
INVALID_HEADER
pngStatus_t;
 
int yang tidak ditandatangani adalahSyscallSuccessful (const ssize_t readStatus)
kembali bacaStatus> = 0;
 

 
/ *
* checkPngHeader sedang memeriksa apakah array pngFileHeader sesuai dengan PNG
* tajuk fail.
*
* Pada masa ini ia hanya memeriksa 8 bait pertama dari array. Sekiranya susunan kurang
* daripada 8 bait, TOO_SHORT dikembalikan.
*
* pngFileHeaderLength mesti merangkumi panjang susunan tye. Sebarang nilai tidak sah
* boleh menyebabkan tingkah laku yang tidak ditentukan, seperti aplikasi mogok.
*
* Mengembalikan IS_PNG jika sesuai dengan tajuk fail PNG. Sekiranya ada sekurang-kurangnya
* 8 bait dalam array tetapi bukan tajuk PNG, INVALID_HEADER dikembalikan.
*
* /
pngStatus_t checkPngHeader (const tidak ditandatangani char * const pngFileHeader,
size_t pngFileHeaderLength) const unsigned char dijangkaPngHeader [8] =
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A;
int i = 0;
 
jika (pngFileHeaderLength < sizeof(expectedPngHeader))
kembali TOO_SHORT;
 

 
untuk (i = 0; i < sizeof(expectedPngHeader); i++)
jika (pngFileHeader [i] != dijangkaPngHeader [i])
pulangkan INVALID_HEADER;
 


 
/ * Jika sampai di sini, semua 8 bait pertama sesuai dengan tajuk PNG. * /
pulangkan IS_PNG;

 
int main (int argumenLength, char * argumentList [])
char * pngFileName = NULL;
char yang tidak bertanda pngFileHeader [8] = 0;
 
ssize_t readStatus = 0;
/ * Linux menggunakan nombor untuk mengenal pasti fail yang terbuka. * /
int pngFile = 0;
pngStatus_t pngCheckResult;
 
sekiranya (panjang hujah != 2)
fputs ("Anda mesti memanggil program ini menggunakan isPng nama fail anda.\ n ", stderr);
pulangkan EXIT_FAILURE;
 

 
pngFileName = argumentList [1];
pngFile = terbuka (pngFileName, O_RDONLY);
 
jika (pngFile == -1)
kesalahan ("Membuka fail yang disediakan gagal");
pulangkan EXIT_FAILURE;
 

 
/ * Baca beberapa bait untuk mengenal pasti sama ada fail itu PNG. * /
readStatus = baca (pngFile, pngFileHeader, sizeof (pngFileHeader));
 
jika (isSyscallSuccessful (readStatus))
/ * Periksa sama ada fail tersebut adalah PNG sejak mendapat data. * /
pngCheckResult = checkPngHeader (pngFileHeader, readStatus);
 
jika (pngCheckResult == TOO_SHORT)
printf ("Fail% s bukan fail PNG: terlalu pendek.\ n ", pngFileName);
 
lain jika (pngCheckResult == IS_PNG)
printf ("Fail% s adalah fail PNG!\ n ", pngFileName);
 
lain
printf ("Fail% s tidak dalam format PNG.\ n ", pngFileName);
 

 
lain
kesalahan ("Membaca fail gagal");
pulangkan EXIT_FAILURE;
 

 
/ * Tutup fail… * /
jika (tutup (pngFile) == -1)
kesalahan ("Menutup fail yang disediakan gagal");
pulangkan EXIT_FAILURE;
 

 
pngFile = 0;
 
pulangkan EXIT_SUCCESS;
 

Lihat, ini adalah contoh penuh, berfungsi dan dapat disusun. Jangan ragu untuk menyusunnya sendiri dan mengujinya, ia benar-benar berkesan. Anda harus memanggil program dari terminal seperti ini:

./ isPng nama fail anda

Sekarang, mari fokus pada panggilan baca itu sendiri:

pngFile = terbuka (pngFileName, O_RDONLY);
jika (pngFile == -1)
kesalahan ("Membuka fail yang disediakan gagal");
pulangkan EXIT_FAILURE;

/ * Baca beberapa bait untuk mengenal pasti sama ada fail itu PNG. * /
readStatus = baca (pngFile, pngFileHeader, sizeof (pngFileHeader));

Tandatangan baca adalah yang berikut (diekstrak dari halaman manual Linux):

ssize_t read (int fd, void * buf, size_t count);

Pertama, argumen fd mewakili deskriptor fail. Saya telah menerangkan sedikit konsep ini dalam artikel garpu saya.  Deskriptor fail adalah int yang mewakili fail terbuka, soket, paip, FIFO, peranti, baik itu banyak perkara di mana data dapat dibaca atau ditulis, umumnya dengan cara seperti aliran. Saya akan mengulas lebih mendalam mengenai perkara itu dalam artikel akan datang.

fungsi terbuka adalah salah satu cara untuk memberitahu kepada Linux: Saya ingin melakukan perkara dengan fail di jalan itu, sila cari di mana ia berada dan beri saya akses ke sana. Ini akan memberi anda kembali deskriptor fail yang disebut ini dan sekarang, jika anda ingin melakukan apa-apa dengan fail ini, gunakan nombor itu. Jangan lupa untuk menelefon apabila anda selesai menggunakan fail, seperti dalam contoh.

Oleh itu, anda perlu memberikan nombor khas ini untuk dibaca. Kemudian ada hujah buf. Anda di sini harus memberikan penunjuk kepada array di mana membaca akan menyimpan data anda. Akhirnya, hitungan adalah berapa banyak bait yang akan dibaca paling banyak.

Nilai pulangan adalah jenis ssize_t. Jenis pelik, bukan? Ini bermaksud "size_t yang ditandatangani", pada dasarnya ia adalah int panjang. Ia mengembalikan bilangan bait yang berjaya dibaca, atau -1 jika ada masalah. Anda boleh mencari punca sebenar masalah dalam pemboleh ubah global errno yang dibuat oleh Linux, yang ditentukan dalam . Tetapi untuk mencetak mesej ralat, menggunakan kesalahan adalah lebih baik kerana mencetak errno bagi pihak anda.

Dalam fail biasa - dan hanya dalam kes ini - baca akan kembali kurang daripada jumlah hanya jika anda telah mencapai akhir fail. Susunan buf yang anda berikan mesti cukup besar untuk memuat sekurang-kurangnya kiraan bait, atau program anda mungkin rosak atau membuat pepijat keselamatan.

Sekarang, membaca tidak hanya berguna untuk fail biasa dan jika anda ingin merasakan kehebatannya - Ya, saya tahu itu bukan komik Marvel tetapi mempunyai kekuatan yang sebenarnya - anda pasti mahu menggunakannya dengan aliran lain seperti paip atau soket. Mari kita perhatikan:

Fail khas Linux dan panggilan sistem baca

Fakta yang dibaca berfungsi dengan pelbagai fail seperti paip, soket, FIFO atau peranti khas seperti cakera atau port bersiri yang menjadikannya lebih kuat. Dengan beberapa penyesuaian, anda dapat melakukan perkara yang sangat menarik. Pertama, ini bermaksud anda boleh menulis fungsi yang berfungsi pada fail dan menggunakannya dengan paip sebagai gantinya. Itu menarik untuk menyebarkan data tanpa memukul cakera, memastikan prestasi terbaik.

Walau bagaimanapun ini mencetuskan peraturan khas juga. Mari kita ambil contoh membaca baris dari terminal berbanding fail biasa. Apabila anda memanggil baca pada fail biasa, hanya memerlukan beberapa milisaat ke Linux untuk mendapatkan jumlah data yang anda minta.

Tetapi apabila sampai ke terminal, itu kisah lain: katakan anda meminta nama pengguna. Pengguna menaip di terminal nama pengguna dan tekan Enter. Sekarang anda mengikuti nasihat saya di atas dan anda memanggil membaca dengan penyangga besar seperti 256 bait.

Sekiranya dibaca berfungsi seperti yang dilakukannya dengan fail, pengguna akan menaip 256 aksara sebelum kembali! Pengguna anda akan menunggu selama-lamanya, dan kemudian dengan sedih membunuh aplikasi anda. Sudah tentu tidak seperti yang anda mahukan, dan anda akan menghadapi masalah besar.

Baiklah, anda boleh membaca satu bait pada satu masa tetapi penyelesaian ini sangat tidak berkesan, seperti yang saya katakan di atas. Ia mesti berfungsi lebih baik daripada itu.

Tetapi pemaju Linux berpendapat membaca berbeza untuk mengelakkan masalah ini:

  • Apabila anda membaca fail biasa, ia cuba seboleh-bolehnya membaca kiraan bait dan secara aktif akan mendapatkan bait dari cakera jika diperlukan.
  • Untuk semua jenis fail lain, fail akan kembali sebaik sahaja terdapat beberapa data yang ada dan kebanyakannya hitung bait:
    1. Untuk terminal, itu amnya semasa pengguna menekan kekunci Enter.
    2. Untuk soket TCP, komputer anda akan menerima sesuatu, tidak kira jumlah bait yang diterima.
    3. Untuk FIFO atau paip, umumnya jumlahnya sama dengan yang ditulis oleh aplikasi lain, tetapi kernel Linux dapat memberikan lebih sedikit pada satu masa jika itu lebih mudah.

Oleh itu, anda boleh membuat panggilan dengan selamat dengan 2 KiB buffer anda tanpa terkunci selama-lamanya. Perhatikan bahawa ia juga boleh terganggu jika aplikasi menerima isyarat. Sebagai bacaan dari semua sumber ini boleh memakan masa beberapa saat atau bahkan berjam-jam - sehingga pihak lain memutuskan untuk menulis - diganggu oleh isyarat membolehkan berhenti disekat terlalu lama.

Ini juga mempunyai kelemahan walaupun: apabila anda ingin membaca 2 KiB dengan fail khas ini, anda perlu memeriksa nilai pengembalian baca dan membaca panggilan beberapa kali. membaca jarang akan mengisi keseluruhan penyangga anda. Sekiranya aplikasi anda menggunakan isyarat, anda juga perlu memeriksa apakah pembacaan gagal dengan -1 kerana terganggu oleh isyarat, menggunakan errno.

Izinkan saya menunjukkan kepada anda bagaimana menarik untuk menggunakan harta khas ini:

#define _POSIX_C_SOURCE 1 / * sigaction tidak tersedia tanpa #define ini. * /
#sertakan
#sertakan
#sertakan
#sertakan
#sertakan
#sertakan
/ *
* isSignal memberitahu jika baca syscall telah terganggu oleh isyarat.
*
* Mengembalikan BENAR jika syscall yang dibaca telah terganggu oleh isyarat.
*
* Pemboleh ubah global: ia membaca errno yang ditentukan dalam errno.h
* /
int isigned yang tidak ditandatangani (const ssize_t readStatus)
kembali (readStatus == -1 && errno == EINTR);

int yang tidak ditandatangani adalahSyscallSuccessful (const ssize_t readStatus)
kembali bacaStatus> = 0;

/ *
* shouldRestartRead memberitahu bila sysall membaca telah terganggu oleh a
* peristiwa isyarat atau tidak, dan memandangkan alasan "ralat" ini sementara, kita boleh
* mulakan semula panggilan baca dengan selamat.
*
* Pada masa ini, ia hanya memeriksa jika pembacaannya terganggu oleh isyarat, tetapi itu
* dapat diperbaiki untuk memeriksa apakah jumlah target byte telah dibaca dan apakah itu
* bukan masalahnya, kembalikan BENAR untuk membaca lagi.
*
* /
int tidak bertanda tanganRestartRead (const ssize_t readStatus)
return isSignal (readStatus);

/ *
* Kami memerlukan pengendali kosong kerana syscall baca akan terganggu hanya jika
* isyarat dikendalikan.
* /
batal kosongHandler (int diabaikan)
kembali;

int utama ()
/ * Dalam beberapa saat. * /
const int alarmInterval = 5;
const struct sigactionptySigaction = blankHandler;
char lineBuf [256] = 0;
ssize_t readStatus = 0;
int waitingTime = 0;
/ * Jangan ubah suai kecuali jika anda betul-betul tahu apa yang anda lakukan. * /
sigaction (SIGALRM, & blankSigaction, NULL);
penggera (alarmInterval);
fputs ("Teks anda: \ n", stderr);
buat
/ * Jangan lupa '\ 0' * /
readStatus = baca (STDIN_FILENO, lineBuf, sizeof (lineBuf) - 1);
jika (isSignal (readStatus))
waitTime + = alarmInterval;
penggera (alarmInterval);
fprintf (stderr, "% u detik tidak aktif ... \ n", waitTime);

sementara (harusRestartRead (readStatus));
jika (isSyscallSuccessful (readStatus))
/ * Hentikan rentetan untuk mengelakkan bug ketika memberikannya ke fprintf. * /
lineBuf [readStatus] = '\ 0';
fprintf (stderr, "Anda menaip% lu chars. Inilah rentetan anda: \ n% s \ n ", strlen (lineBuf),
lineBuf);
lain
kesalahan ("Membaca dari stdin gagal");
pulangkan EXIT_FAILURE;

pulangkan EXIT_SUCCESS;

Sekali lagi, ini adalah aplikasi C penuh yang boleh anda kumpulkan dan jalankan.

Ia melakukan perkara berikut: membaca baris dari input standard. Namun, setiap 5 saat, ia mencetak garis yang memberitahu pengguna bahawa belum ada input yang diberikan.

Contoh jika saya menunggu 23 saat sebelum menaip "Penguin":

$ alarm_read
Teks anda:
5 saat tidak aktif ..
10 saat tidak aktif ..
15 saat tidak aktif ..
20 saat tidak aktif ..
Penguin
Anda menaip 8 watak. Inilah rentetan anda:
Penguin

Itu sangat berguna. Ia dapat digunakan untuk mengemas kini UI dengan kerap untuk mencetak kemajuan pembacaan atau pemprosesan aplikasi yang anda lakukan. Ia juga dapat digunakan sebagai mekanisme timeout. Anda juga boleh terganggu oleh isyarat lain yang mungkin berguna untuk aplikasi anda. Bagaimanapun, ini bermakna aplikasi anda kini boleh responsif dan bukannya terus kekal selamanya.

Jadi faedahnya melebihi kelemahan yang dinyatakan di atas. Sekiranya anda tertanya-tanya adakah anda harus menyokong fail khas dalam aplikasi yang biasanya berfungsi dengan fail biasa - dan begitu memanggil membaca dalam gelung - Saya katakan melakukannya kecuali jika anda terburu-buru, pengalaman peribadi saya sering membuktikan bahawa mengganti fail dengan paip atau FIFO secara harfiah dapat membuat aplikasi lebih berguna dengan usaha kecil. Bahkan ada fungsi C yang sudah dibuat di Internet yang menggunakan gelung itu untuk anda: ia dipanggil fungsi baca.

Kesimpulannya

Seperti yang anda lihat, membaca dan membaca mungkin kelihatan serupa, tidak. Dan dengan hanya sedikit perubahan pada cara membaca berfungsi untuk pembangun C, membaca jauh lebih menarik untuk merancang penyelesaian baru untuk masalah yang anda hadapi semasa pembangunan aplikasi.

Lain kali, saya akan memberitahu anda bagaimana menulis syscall berfungsi, kerana membaca itu bagus, tetapi dapat melakukan kedua-duanya jauh lebih baik. Sementara itu, bereksperimen dengan membaca, mengenalinya dan saya mengucapkan Selamat Tahun Baru!

Cara Menggunakan Xdotool untuk Merangsang Klik dan Penekanan Tetikus di Linux
Xdotool adalah alat baris arahan sumber terbuka dan bebas untuk mensimulasikan klik dan tekan kekunci tetikus. Artikel ini akan merangkumi panduan rin...
5 Produk Tetikus Komputer Ergonomik Teratas untuk Linux
Adakah penggunaan komputer yang berpanjangan menyebabkan rasa sakit di pergelangan tangan atau jari anda? Adakah anda mengalami sendi kaku dan selalu ...
Cara Mengubah Tetapan Tetikus dan Pad Sentuh Menggunakan Xinput di Linux
Sebilangan besar pengedaran Linux dihantar dengan perpustakaan "libinput" secara lalai untuk menangani peristiwa input pada sistem. Ia dapat memproses...