C Programmeerimine

Lugege Syscall Linuxi

Lugege Syscall Linuxi
Nii et peate lugema binaarandmeid? Võib-olla soovite lugeda FIFO-st või pistikupesast? Võite kasutada C-standardse teegi funktsiooni, kuid seda tehes ei saa te Linuxi kerneli ja POSIX-i pakutavatest erifunktsioonidest kasu. Näiteks võiksite kasutada teatud ajal lugemiseks aegumisi, ilma et küsitlust kasutataks. Samuti peate võib-olla midagi lugema hoolimata, kui see on spetsiaalne fail või pesa või midagi muud. Teie ainus ülesanne on lugeda mõnda binaarsisu ja saada see oma rakendusse. Seal särab loetud syscall.

Lugege tavalist faili Linuxi syscalliga

Parim viis selle funktsiooniga töötamiseks on tavalise faili lugemine. See on lihtsaim viis selle syscalli kasutamiseks ja põhjusel: sellel ei ole nii palju piiranguid kui muud tüüpi voogudel või torudel. Kui mõtlete selle peale, et see on loogika, peab teise rakenduse väljundit lugedes enne selle lugemist mõni väljund olema valmis ja seega peate ootama, kuni rakendus selle väljundi kirjutab.

Esiteks peamine erinevus tavapärase teegiga: puhverdamist pole üldse olemas. Iga kord, kui helistate lugemisfunktsioonile, helistate Linuxi kernelile ja see võtab aega - see on peaaegu kohene, kui helistate sellele üks kord, kuid võib teid aeglustada, kui helistate sellele tuhandeid kordi sekundis. Võrdluseks puhverdab standardkogu teie jaoks sisendi. Seega, kui helistate lugemiseks, peaksite lugema rohkem kui paar baiti, vaid pigem suure puhvri, nagu paar kilobaiti - välja arvatud juhul, kui vaja on tõepoolest vähe baite, näiteks kui kontrollite, kas fail on olemas ja kas see pole tühi.

Sellel on aga eelis: iga kord, kui helistate lugemiseks, olete kindel, et saate värskendatud andmed, kui mõni muu rakendus faili praegu muudab. See on eriti kasulik erifailide jaoks, näiteks failides / proc või / sys.

Aeg näidata teile tõelist näidet. See C-programm kontrollib, kas fail on PNG või mitte. Selleks loeb see faili, mis on määratud käsureaargumendis esitatud teele, ja kontrollib, kas esimesed 8 baiti vastavad PNG päisele.

Siin on kood:

# kaasata
# kaasata
# kaasata
# kaasata
# kaasata
# kaasata
# kaasata
 
typedef enum
IS_PNG,
LIIGA LÜHIKE,
INVALID_HEADER
pngStatus_t;
 
unsigned int isSyscallSuccessful (const ssize_t readStatus)
tagastage readStatus> = 0;
 

 
/ *
* checkPngHeader kontrollib, kas massiiv pngFileHeader vastab PNG-le
* faili päis.
*
* Praegu kontrollib see ainult massiivi esimest 8 baiti. Kui massiivi on vähem
* üle 8 baiti tagastatakse TOO_SHORT.
*
* pngFileHeaderLength peab siduma massiivi pikkuse. Mis tahes kehtetu väärtus
* võib põhjustada määratlemata käitumist, näiteks rakenduse krahhi.
*
* Annab vastuseks IS_PNG, kui see vastab PNG-faili päisele. Kui on vähemalt
* Massiivis on 8 baiti, kuid see pole PNG päis, tagastatakse INVALID_HEADER.
*
* /
pngStatus_t checkPngHeader (const märkimata char * const pngFileHeader,
size_t pngFileHeaderLength) const allkirjastamata täht eeldatavPngHeader [8] =
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A;
int i = 0;
 
if (pngFileHeaderLength < sizeof(expectedPngHeader))
tagasi TOO_SHORT;
 

 
jaoks (i = 0; i < sizeof(expectedPngHeader); i++)
if (pngFileHeader [i] != eeldatavPngHeader [i])
return INVALID_HEADER;
 


 
/ * Kui see jõuab siia, vastavad kõik esimesed 8 baiti PNG päisele. * /
tagastama IS_PNG;

 
int main (int argumentLength, char * argumentList [])
char * pngFileName = NULL;
allkirjastamata märk pngFileHeader [8] = 0;
 
ssize_t readStatus = 0;
/ * Linux kasutab avatud faili tuvastamiseks numbrit. * /
int pngFail = 0;
pngStatus_t pngCheckResult;
 
kui (argumentLength != 2)
fputs ("Peate helistama sellele programmile isPng your filename abil.\ n ", stderr);
tagastage EXIT_FAILURE;
 

 
pngFileName = argumentList [1];
pngFile = avatud (pngFileName, O_RDONLY);
 
kui (pngFail == -1)
perror ("Esitatud faili avamine ebaõnnestus");
tagastage EXIT_FAILURE;
 

 
/ * Lugege paar baiti, et tuvastada, kas fail on PNG. * /
readStatus = loe (pngFile, pngFileHeader, sizeof (pngFileHeader));
 
if (onSyscallSuccessful (readStatus))
/ * Kontrollige, kas fail on PNG, kuna see sai andmed. * /
pngCheckResult = checkPngHeader (pngFileHeader, readStatus);
 
if (pngCheckResult == TOO_SHORT)
printf ("Fail% s ei ole PNG-fail: see on liiga lühike.\ n ", pngFileName);
 
else if (pngCheckResult == IS_PNG)
printf ("Fail% s on PNG-fail!\ n ", pngFileName);
 
muu
printf ("Fail% s ei ole PNG-vormingus.\ n ", pngFileName);
 

 
muu
perror ("Faili lugemine ebaõnnestus");
tagastage EXIT_FAILURE;
 

 
/ * Sule fail ... * /
kui (sulge (pngFail) == -1)
perror ("Pakutava faili sulgemine ebaõnnestus");
tagastage EXIT_FAILURE;
 

 
pngFail = 0;
 
tagastage EXIT_SUCCESS;
 

Vaadake, see on täiesti puhas, toimiv ja kompileeritav näide. Ärge kartke seda ise koostada ja katsetada, see tõesti töötab. Programmile peaksite helistama järgmisest terminalist:

./ isPng teie faili nimi

Nüüd keskendume loetud kõnele endale:

pngFile = avatud (pngFileName, O_RDONLY);
kui (pngFail == -1)
perror ("Esitatud faili avamine ebaõnnestus");
tagastage EXIT_FAILURE;

/ * Lugege paar baiti, et tuvastada, kas fail on PNG. * /
readStatus = loe (pngFile, pngFileHeader, sizeof (pngFileHeader));

Loetud allkiri on järgmine (eraldatud Linuxi käsilehtedelt):

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

Esiteks tähistab argument fd faili kirjeldajat. Olen oma kahvliartiklis seda mõistet natuke selgitanud.  Faili kirjeldaja on int, mis tähistab avatud faili, pesa, toru, FIFO-seadet, noh, see on palju asju, kus andmeid saab lugeda või kirjutada, tavaliselt voogetaolisel viisil. Lähemalt uurin seda järgmises artiklis.

avatud funktsioon on üks viis, kuidas Linuxile öelda: ma tahan teha asju selle tee failiga, leidke see, kus see on, ja lubage mulle sellele juurdepääs. See annab teile selle int-nimelise failikirjeldaja tagasi ja kui soovite selle failiga midagi teha, kasutage seda numbrit. Ärge unustage helistada sulgemisele, kui olete failiga valmis, nagu näites.

Nii et lugemiseks peate esitama selle erinumbri. Siis on buf argument. Siin peaksite esitama massiivi osuti, kuhu lugemine teie andmeid salvestab. Lõpuks loe, mitu baiti see kõige rohkem loeb.

Tagastatav väärtus on tüüpi ssize_t. Imelik tüüp, kas pole? See tähendab "allkirjastatud suurus_t", põhimõtteliselt on see pikk int. See tagastab edukalt loetud baitide arvu või probleemi korral -1. Probleemi täpse põhjuse leiate Linuxi loodud errno globaalsest muutujast, mis on määratletud . Kuid tõrketeate printimiseks on perrori kasutamine parem, kuna see prindib teie nimel errno.

Tavalistes failides - ja ainult sel juhul tagastab lugemine vähem kui loenduse ainult siis, kui olete jõudnud faili lõppu. Teie antud bufi massiiv peab olla piisavalt suur, et mahutada vähemalt baiti, muidu võib teie programm kokku kukkuda või tekitada turvavea.

Nüüd pole lugemine kasulik ainult tavaliste failide jaoks ja kui soovite tunda selle ülivõimsust - Jah, ma tean, et see pole üheski Marveli koomiksis, kuid sellel on tõelised jõud - soovite seda kasutada koos teiste voogudega, näiteks torude või pistikupesadega. Vaatame seda:

Linuxi erifailid ja süsteemikõne lugemine

Loetud faktid töötavad mitmesuguste failidega, nagu torud, pistikupesad, FIFO-d või spetsiaalsed seadmed, näiteks ketas või jadaport, on see, mis muudab selle tõesti võimsamaks. Mõningate kohandustega saate teha tõeliselt huvitavaid asju. Esiteks tähendab see seda, et saate failis töötavaid funktsioone sõna otseses mõttes kirjutada ja kasutada selle asemel toruga. Parim jõudlus on huvitav edastada andmeid ilma kettale löömata.

Kuid see käivitab ka erireeglid. Võtame näiteks terminalist rea lugemise tavalise failiga. Kui helistate tavalises failis loetuks, vajab see teie soovitud andmemahu saamiseks Linuxile vaid mõnda millisekundit.

Aga mis puudutab terminali, siis see on teine ​​lugu: oletame, et küsite kasutajanime. Kasutaja sisestab oma kasutajanime ja vajutab Enter. Nüüd järgite minu ülaltoodud nõuandeid ja helistate loendisse suure puhvriga, näiteks 256 baiti.

Kui lugemine toimiks nagu failidega, ootaks see enne naasmist, kuni kasutaja sisestab 256 tähemärki! Teie kasutaja ootaks igavesti ja tapaks siis teie rakenduse kahjuks. See pole kindlasti see, mida soovite, ja teil oleks suur probleem.

Okei, võite lugeda üks bait korraga, kuid see lahendus on kohutavalt ebaefektiivne, nagu ma teile eespool ütlesin. See peab paremini töötama.

Kuid Linuxi arendajad arvasid selle probleemi vältimiseks teisiti:

  • Tavaliste failide lugemisel proovib see võimalikult palju lugeda baitide arvu ja saab aktiivselt baidid kettalt, kui see on vajalik.
  • Kõigi teiste failitüüpide korral see naaseb niipea kui seal on mõned andmed saadaval ja kõige rohkem loota baiti:
    1. Terminalide jaoks on see nii üldiselt kui kasutaja vajutab sisestusklahvi.
    2. TCP-pistikupesade puhul pole vahet, kui palju baite see teie arvutisse midagi saab.
    3. FIFO või torude puhul on see üldjuhul sama palju kui teise rakenduse kirjutatud, kuid Linuxi kernel suudab pakkuda vähem korraga, kui see on mugavam.

Nii saate oma 2 KiB puhvriga turvaliselt helistada, jäädes igavesti lukustamata. Pange tähele, et see võib katkeda ka siis, kui rakendus saab signaali. Kuna kõigi nende allikate lugemine võib võtta sekundeid või isegi tunde - seni, kuni teine ​​pool otsustab kirjutada - signaalidega katkestamine võimaldab peatada liiga kaua blokeerituks jäämise.

Sellel on siiski ka puudus: kui soovite nende spetsiaalsete failidega täpselt lugeda 2 KiB, peate kontrollima read tagastusväärtust ja kõne loetud mitu korda. lugemine täidab harva kogu teie puhvri. Kui teie rakendus kasutab signaale, peate ka errno abil kontrollima, kas lugemine ebaõnnestus väärtusega -1, kuna selle katkestas signaal.

Lubage mul näidata teile, kuidas võib olla huvitav kasutada seda erilist omadust lugeda:

#define _POSIX_C_SOURCE 1 / * pole selle #define'ita saadaval. * /
# kaasata
# kaasata
# kaasata
# kaasata
# kaasata
# kaasata
/ *
* isSignal ütleb, kas loetud syscalli on signaal katkestanud.
*
* Tagastab väärtuse TÕENE, kui loetud süsteemikõne on signaaliga katkestatud.
*
* Globaalsed muutujad: see loeb errnos määratletud errno.h
* /
unsigned int isSignal (const ssize_t readStatus)
tagastamine (readStatus == -1 && errno == EINTR);

unsigned int isSyscallSuccessful (const ssize_t readStatus)
tagastage readStatus> = 0;

/ *
* shouldRestartRead annab teada, kui lugenud syscalli on katkestanud a
* signaali sündmus või mitte, ja arvestades, et see "vea" põhjus on mööduv, võime seda teha
* taaskäivitage loetud kõne uuesti.
*
* Praegu kontrollib see ainult seda, kas lugemine on signaaliga katkenud, kuid seda
* võiks parandada, et kontrollida, kas baitide sihtarv on loetud ja kas see on
* pole nii, tagastage TRUE uuesti lugemiseks.
*
* /
unsigned int shouldRestartRead (const ssize_t readStatus)
return onSignal (readStatus);

/ *
* Vajame tühja käitlejat, kuna loetud syscall katkestatakse ainult siis, kui
* signaali käsitletakse.
* /
void emptyHandler (int ignoreeritud)
tagasi;

int main ()
/ * On sekundites. * /
const int alarmInterval = 5;
const struct sigaction emptySigaction = tühiKäitleja;
char lineBuf [256] = 0;
ssize_t readStatus = 0;
allkirjastamata int ooteaeg = 0;
/ * Ärge muutke sigactioni, välja arvatud juhul, kui teate täpselt, mida teete. * /
sigaction (SIGALRM, & emptySigaction, NULL);
alarm (alarmInterval);
sisendid ("Teie tekst: \ n", stderr);
tee
/ * Ära unusta '\ 0' * /
readStatus = loe (STDIN_FILENO, lineBuf, sizeof (lineBuf) - 1);
if (isSignal (readStatus))
waitTime + = alarmInterval;
alarm (alarmInterval);
fprintf (stderr, "mitteaktiivsuse% u sekundit ... \ n", waitTime);

while (peaksRestartRead (readStatus));
if (onSyscallSuccessful (readStatus))
/ * Vea vältimiseks lõpetage string fprintf-ile pakkumisel. * /
lineBuf [readStatus] = '\ 0';
fprintf (stderr, "Sisestasite% lu tähemärki. Siin on teie string: \ n% s \ n ", strlen (lineBuf),
lineBuf);
muu
perror ("stdinilt lugemine ebaõnnestus");
tagastage EXIT_FAILURE;

tagastage EXIT_SUCCESS;

Taaskord on tegemist täieliku C-rakendusega, mille saate kompileerida ja reaalselt käivitada.

See teeb järgmist: loeb rida standardsisendist. Iga 5 sekundi tagant prindib see rida, mis ütleb kasutajale, et sisendit pole veel antud.

Näide, kui ootan enne pingviini kirjutamist 23 sekundit:

$ alarm_read
Teie tekst:
5 sekundit tegevusetust ..
10 sekundit tegevusetust ..
15 sekundit tegevusetust ..
20 sekundit tegevusetust…
Pingviin
Sisestasite 8 tähemärki. Siin on teie string:
Pingviin

See on uskumatult kasulik. Seda saab kasutada sageli kasutajaliidese värskendamiseks, et printida teie rakenduse lugemise või töötlemise edenemine. Seda saab kasutada ka ajalõpu mehhanismina. Samuti võite katkestada mis tahes muu signaaliga, mis võib teie rakenduse jaoks kasulik olla. Igatahes tähendab see, et teie rakendus saab nüüd igavesti ummikusse jäämise asemel reageerida.

Nii et eelised kaaluvad üles eespool kirjeldatud puuduse. Kui te ei tea, kas peaksite toetama spetsiaalseid faile rakenduses, mis tavaliselt töötab tavaliste failidega - ja nii kutsudes lugeda silmusena - Ma ütleksin, et tehke seda, välja arvatud juhul, kui teil on kiire, näitas minu isiklik kogemus sageli, et faili asendamine toru või FIFO-ga võib sõna otseses mõttes muuta rakenduse väikeste jõupingutustega palju kasulikumaks. Internetis on isegi C-tüüpi funktsioone, mis selle lingi teie jaoks rakendavad: seda nimetatakse loetud funktsioonideks.

Järeldus

Nagu näete, võib leib ja lugemine sarnaneda, kuid mitte nii. Ja ainult mõne muudatusega selle osas, kuidas lugemine C-arendaja jaoks töötab, on lugemine palju huvitavam uute lahenduste väljatöötamiseks probleemidele, millega rakenduse väljatöötamisel kokku puutute.

Järgmine kord ütlen teile, kuidas syscalli kirjutamine töötab, kuna lugemine on lahe, kuid mõlemat teha on palju parem. Seniks katsetage lugemist, õppige seda tundma ja soovin teile head uut aastat!

League of Legendsi installimine Ubuntu 14-le.04
Kui olete League of Legends fänn, siis on see teile võimalus proovida League of Legendsit. Pange tähele, et LOL-i toetab PlayOnLinux, kui olete Linuxi...
Installige uusim OpenRA strateegiamäng Ubuntu Linuxi
OpenRA on tasuta / tasuta reaalajas strateegiamängumootor, mis loob uuesti Westwoodi varased mängud nagu klassikaline Command & Conquer: Red Alert. Ja...
Installige Linuxile uusim Dolphin Emulator for Gamecube & Wii
Dolphini emulaator võimaldab teil mängida valitud Gamecube & Wii mänge Linuxi personaalarvutites (PC). Vabalt saadaval oleva ja avatud lähtekoodiga m...