Ц Програмирање

Прочитајте Сисцалл Линук

Прочитајте Сисцалл Линук
Дакле, морате читати бинарне податке? Можда ћете желети да читате са ФИФО-а или из утичнице? Видите, можете користити функцију стандардне библиотеке Ц, али тиме нећете имати користи од посебних карактеристика које пружају Линук Кернел и ПОСИКС. На пример, можда ћете желети да користите временске ограничења за читање у одређено време без прибегавања анкетирању. Такође, можда ћете морати нешто прочитати без бриге да ли је у питању посебна датотека или утичница или било шта друго. Ваш једини задатак је да прочитате неки бинарни садржај и унесете га у своју апликацију. Ту блиста прочитани сисцалл.

Прочитајте нормалну датотеку с Линук сисцалл-ом

Најбољи начин да започнете рад са овом функцијом је читање нормалне датотеке. Ово је најједноставнији начин да се користи тај сисцалл, и то из разлога: нема толико ограничења као друге врсте тока или цеви. Ако размислите о томе да је логика, када читате излаз друге апликације, морате имати припремљен излаз пре него што га прочитате, па ћете морати сачекати да ова апликација напише овај излаз.

Прво, кључна разлика са стандардном библиотеком: Уопште нема међуспремника. Сваки пут када позовете функцију читања, позват ћете Линук кернел, па ће ово потрајати - готово је тренутно ако га једном позовете, али може вас успорити ако га позовете хиљадама пута у секунди. Поређења ради, стандардна библиотека ће баферисати унос уместо вас. Дакле, кад год назовете читање, требали бисте прочитати више од неколико бајтова, већ велики бафер попут неколико килобајта - осим ако је оно што вам треба заиста мало бајтова, на пример ако проверите да ли датотека постоји и није ли празна.

Ово, међутим, има предност: сваки пут када назовете читање, сигурни сте да добијате ажуриране податке, ако било која друга апликација тренутно модификује датотеку. Ово је посебно корисно за посебне датотеке попут датотека у / проц или / сис.

Време је да вам покажем прави пример. Овај Ц програм проверава да ли је датотека ПНГ или није. Да би то урадио, чита датотеку наведену у путањи коју наведете у аргументу командне линије и проверава да ли првих 8 бајтова одговара ПНГ заглављу.

Ево кода:

#инцлуде
#инцлуде
#инцлуде
#инцлуде
#инцлуде
#инцлуде
#инцлуде
 
типедеф енум
ИС_ПНГ,
ПРЕКРАТАК,
ИНВАЛИД_ХЕАДЕР
пнгСтатус_т;
 
унсигнед инт исСисцаллСуццессфул (цонст ссизе_т реадСтатус)
повратак реадСтатус> = 0;
 

 
/ *
* цхецкПнгХеадер проверава да ли низ пнгФилеХеадер одговара ПНГ-у
* заглавље датотеке.
*
* Тренутно проверава само првих 8 бајтова низа. Ако је низ мањи
* од 8 бајтова, враћа се ТОО_СХОРТ.
*
* пнгФилеХеадерЛенгтх мора да одржи кенгтх низа. Било која неваљана вредност
* може довести до недефинисаног понашања, попут пада система.
*
* Приказује ИС_ПНГ ако одговара заглављу ПНГ датотеке. Ако постоји бар
* 8 бајтова у пољу, али то није ПНГ заглавље, враћа се ИНВАЛИД_ХЕАДЕР.
*
* /
пнгСтатус_т цхецкПнгХеадер (цонст непотписани цхар * цонст пнгФилеХеадер,
сизе_т пнгФилеХеадерЛенгтх) цонст непотписани знак очекујеПнгХеадер [8] =
0к89, 0к50, 0к4Е, 0к47, 0к0Д, 0к0А, 0к1А, 0к0А;
инт и = 0;
 
иф (пнгФилеХеадерЛенгтх < sizeof(expectedPngHeader))
ретурн ТОО_СХОРТ;
 

 
за (и = 0; и < sizeof(expectedPngHeader); i++)
иф (пнгФилеХеадер [и] != очекиваниПнгХеадер [и])
ретурн ИНВАЛИД_ХЕАДЕР;
 


 
/ * Ако стигне овде, свих првих 8 бајтова одговара ПНГ заглављу. * /
ретурн ИС_ПНГ;

 
инт маин (инт аргументЛенгтх, цхар * аргументЛист [])
цхар * пнгФилеНаме = НУЛЛ;
непотписани знак пнгФилеХеадер [8] = 0;
 
ссизе_т реадСтатус = 0;
/ * Линук користи број за идентификацију отворене датотеке. * /
инт пнгФиле = 0;
пнгСтатус_т пнгЦхецкРесулт;
 
иф (аргументЛенгтх != 2)
фпутс ("Морате да позовете овај програм помоћу исПнг ваше име датотеке.\ н ", стдерр);
ретурн ЕКСИТ_ФАИЛУРЕ;
 

 
пнгФилеНаме = аргументЛист [1];
пнгФиле = отворен (пнгФилеНаме, О_РДОНЛИ);
 
иф (пнгФиле == -1)
перрор („Отварање пружене датотеке није успело“);
ретурн ЕКСИТ_ФАИЛУРЕ;
 

 
/ * Прочитајте неколико бајтова да бисте утврдили да ли је датотека ПНГ. * /
реадСтатус = реад (пнгФиле, пнгФилеХеадер, сизеоф (пнгФилеХеадер));
 
иф (исСисцаллСуццессфул (реадСтатус))
/ * Проверите да ли је датотека ПНГ јер је добила податке. * /
пнгЦхецкРесулт = цхецкПнгХеадер (пнгФилеХеадер, реадСтатус);
 
иф (пнгЦхецкРесулт == ТОО_СХОРТ)
принтф ("Датотека% с није ПНГ датотека: прекратка је.\ н ", пнгФилеНаме);
 
иначе ако (пнгЦхецкРесулт == ИС_ПНГ)
принтф ("Датотека% с је ПНГ датотека!\ н ", пнгФилеНаме);
 
остало
принтф ("Датотека% с није у ПНГ формату.\ н ", пнгФилеНаме);
 

 
остало
перрор („Читање датотеке није успело“);
ретурн ЕКСИТ_ФАИЛУРЕ;
 

 
/ * Затворите датотеку… * /
иф (цлосе (пнгФиле) == -1)
перрор („Затварање пружене датотеке није успело“);
ретурн ЕКСИТ_ФАИЛУРЕ;
 

 
пнгФиле = 0;
 
ретурн ЕКСИТ_СУЦЦЕСС;
 

Видите, то је потпун пример, рад и састављање. Не устручавајте се да га сами саставите и тестирате, стварно делује. Програм бисте требали позвати са терминала попут овог:

./ исПнг ваше име датотеке

Сада се фокусирајмо на сам позив за читање:

пнгФиле = отворен (пнгФилеНаме, О_РДОНЛИ);
иф (пнгФиле == -1)
перрор („Отварање пружене датотеке није успело“);
ретурн ЕКСИТ_ФАИЛУРЕ;

/ * Прочитајте неколико бајтова да бисте утврдили да ли је датотека ПНГ. * /
реадСтатус = реад (пнгФиле, пнгФилеХеадер, сизеоф (пнгФилеХеадер));

Потпис за читање је следећи (извучен са Линук страница):

ссизе_т реад (инт фд, воид * буф, сизе_т цоунт);

Прво, фд аргумент представља дескриптор датотеке. Мало сам објаснио овај концепт у свом чланку о виљушкама.  Дескриптор датотеке је инт који представља отворену датотеку, сокет, цев, ФИФО, уређај, па, има пуно ствари у којима се подаци могу читати или писати, углавном на начин сличан току. О томе ћу детаљније у будућем чланку.

опен функција је један од начина да се Линук-у каже: желим да радим са датотеком на том путу, пронађите је тамо где је и дајте ми приступ њој. Вратит ће вам овај инт који се назива дескриптор датотеке и сада, ако желите нешто учинити с овом датотеком, користите тај број. Не заборавите да позовете цлосе када завршите са датотеком, као у примеру.

Зато морате да дате овај посебан број за читање. Затим је ту аргумент буф. Овде треба да наведете показивач на низ у коме ће читање чувати ваше податке. Коначно, рачунајте колико ће бајтова прочитати највише.

Повратна вредност је типа ссизе_т. Чудан тип, зар не?? То значи „потписана величина_т“, у основи је дугачка инт. Враћа број бајтова које је успешно прочитао или -1 ако постоји проблем. Тачан узрок проблема можете пронаћи у еррно глобалној променљивој коју је креирао Линук, дефинисаној у . Али да бисте одштампали поруку о грешци, боље је користити перрор јер се у ваше име штампа погрешно.

У нормалним датотекама - и само у овом случају - читање ће вратити мање од броја само ако сте дошли до краја датотеке. Низ буф који пружате мора бити довољно велик да стане барем на бајтове, или ће се ваш програм можда срушити или створити сигурносну грешку.

Читање сада није корисно само за нормалне датотеке и ако желите да осетите његове супер моћи - Да, знам да то није ни у једном Марвеловом стрипу, али има истинске моћи - желећете да га користите са другим токовима као што су цеви или утичнице. Погледајмо то:

Линук посебне датотеке и чита системски позив

Чињеница која се чита ради са разним датотекама као што су цеви, утичнице, ФИФО-ови или посебни уређаји попут диска или серијског порта је оно што је чини заиста моћнијом. Уз неке адаптације можете учинити заиста занимљиве ствари. Прво, то значи да можете дословно писати функције које раде на датотеци и уместо тога их користити са цевчицом. Занимљиво је преносити податке без ударања на диск, осигуравајући најбоље перформансе.

Међутим, ово покреће и посебна правила. Узмимо пример читања линије са терминала у поређењу са нормалном датотеком. Када назовете читање у нормалној датотеци, Линуку треба само неколико милисекунди да добије количину података коју тражите.

Али што се тиче терминала, то је друга прича: рецимо да тражите корисничко име. Корисник куца на терминалу своје корисничко име и притисните Ентер. Сада следите мој горњи савет и позивате читање са великим бафером као што је 256 бајтова.

Ако би читање функционисало као код датотека, сачекало би да корисник унесе 256 знакова пре него што се врати! Ваш корисник би сачекао заувек, а затим нажалост убио вашу апликацију. То сигурно није оно што желите и имали бисте велики проблем.

Добро, могли бисте читати по један бајт, али ово заобилазно решење је ужасно неефикасно, као што сам вам рекао горе. Мора да делује боље од тога.

Али програмери Линука мислили су да читају другачије да би избегли овај проблем:

  • Када читате нормалне датотеке, покушава што је више могуће прочитати пребројане бајтове и активно ће добити бајтове са диска ако је то потребно.
  • За све остале типове датотека вратиће се чим доступни су неки подаци и Највише цоунт битес:
    1. За терминале је обично када корисник притисне тастер Ентер.
    2. За ТЦП утичнице, чим ваш рачунар нешто прими, није битно колико бајтова добије.
    3. За ФИФО или цеви обично је то иста количина као што је написала друга апликација, али Линук кернел може истовремено испоручивати мање ако је то погодније.

Тако можете безбедно да зовете са бафером од 2 КиБ, а да не будете заувек закључани. Имајте на уму да се такође може прекинути ако апликација прими сигнал. Како читање из свих ових извора може трајати секунде или чак сате - све док друга страна ипак не одлучи да пише - прекид сигнала омогућава да престанете да останете предуго блокирани.

Ово такође има недостатак: када желите да тачно прочитате 2 КиБ са овим специјалним датотекама, мораћете више пута да проверите повратну вредност читања и позив прочитате. читање ће ретко попунити читав ваш бафер. Ако ваша апликација користи сигнале, такође ћете морати да проверите да ли читање није успело са -1, јер га је прекинуо сигнал, користећи еррно.

Показаћу вам како може бити занимљиво користити ово посебно својство читања:

#дефине _ПОСИКС_Ц_СОУРЦЕ 1 / * преусмеравање није доступно без овог #дефине. * /
#инцлуде
#инцлуде
#инцлуде
#инцлуде
#инцлуде
#инцлуде
/ *
* исСигнал говори је ли читање сисцалл-а прекинуо сигнал.
*
* Приказује ТРУЕ ако је сигнални сигнал прекинут читани системски позив.
*
* Глобалне променљиве: чита еррно дефинисано у еррно.х
* /
унсигнед инт исСигнал (цонст ссизе_т реадСтатус)
повратак (реадСтатус == -1 && еррно == ЕИНТР);

унсигнед инт исСисцаллСуццессфул (цонст ссизе_т реадСтатус)
повратак реадСтатус> = 0;

/ *
* схоулдРестартРеад говори када је сисцалл читања прекинут а
* сигнални догађај или не, а с обзиром да је овај разлог „грешке“ привремен, можемо
* сигурно поново покрените прочитани позив.
*
* Тренутно проверава само да ли је читање прекинуо сигнал, али да
* може се побољшати како би се проверило да ли је прочитан циљни број бајтова и да ли је
* није случај, вратите ТРУЕ да бисте поново прочитали.
*
* /
унсигнед инт схоулдРестартРеад (цонст ссизе_т реадСтатус)
ретурн исСигнал (реадСтатус);

/ *
* Потребан нам је празан руковатељ јер ће читани сисцалл бити прекинут само ако
* рукује се сигналом.
* /
воид емптиХандлер (инт занемарен)
повратак;

инт маин ()
/ * То је за неколико секунди. * /
цонст инт алармИнтервал = 5;
цонст струцт сигацтион емптиСигацтион = емптиХандлер;
цхар линеБуф [256] = 0;
ссизе_т реадСтатус = 0;
непотписано инт ваитТиме = 0;
/ * Не мењајте преписивање осим ако тачно знате шта радите. * /
сигакција (СИГАЛРМ, & емптиСигацтион, НУЛЛ);
аларм (алармИнтервал);
фпутс ("Ваш текст: \ н", стдерр);
урадите
/ * Не заборавите на '\ 0' * /
реадСтатус = читање (СТДИН_ФИЛЕНО, линеБуф, сизеоф (линеБуф) - 1);
иф (исСигнал (реадСтатус))
ваитТиме + = алармИнтервал;
аларм (алармИнтервал);
фпринтф (стдерр, "% у секунди неактивности ... \ н", време чекања);

вхиле (схоулдРестартРеад (реадСтатус));
иф (исСисцаллСуццессфул (реадСтатус))
/ * Завршите низ да бисте избегли грешку приликом пружања фпринтф-у. * /
линеБуф [реадСтатус] = '\ 0';
фпринтф (стдерр, "Укуцали сте знакове% лу. Ево вашег низа: \ н% с \ н ", стрлен (линеБуф),
линеБуф);
остало
перрор („Читање са стдина није успело“);
ретурн ЕКСИТ_ФАИЛУРЕ;

ретурн ЕКСИТ_СУЦЦЕСС;

Још једном, ово је пуна Ц апликација коју можете компајлирати и стварно покренути.

Ради следеће: чита ред са стандардног уноса. Међутим, сваких 5 секунди исписује ред који говори кориснику да још увек није дат унос.

Пример ако сачекам 23 секунде пре него што откуцам „Пенгуин“:

$ аларм_реад
Ваш текст:
5 секунди неактивности ..
10 секунди неактивности ..
15 секунди неактивности ..
20 секунди неактивности ..
Пингвин
Откуцали сте 8 знакова. Ево вашег низа:
Пингвин

То је невероватно корисно. Може се користити за често ажурирање корисничког интерфејса за испис напретка читања или обраде ваше апликације коју радите. Такође се може користити као механизам временског ограничења. Такође вас може прекинути било који други сигнал који би могао бити користан за вашу апликацију. У сваком случају, то значи да ваша апликација сада може да реагује, уместо да заувек остане заглављена.

Дакле, користи премашују горе описани недостатак. Ако се питате да ли бисте требали подржавати посебне датотеке у апликацији која нормално ради са нормалним датотекама - и тако зове читати у петљи - Рекао бих да то учините, осим ако се жури, моје лично искуство је често показало да замена датотеке цевчицом или ФИФО-ом дословно може учинити апликацију много кориснијом уз мале напоре. На Интернету постоје чак и унапред израђене функције Ц које имплементирају ту петљу за вас: зову се реадн функције.

Закључак

Као што видите, фреад и реад можда изгледају слично, нису. А са само неколико промена о томе како читање функционише за програмера Ц, читање је много занимљивије за дизајнирање нових решења проблема са којима се сусрећете током развоја апликације.

Следећи пут ћу вам рећи како функционише писање сисцалл-а, јер је читање у реду, али много боље је бити способан за обоје. У међувремену, експериментишите са читањем, упознајте се и желим вам срећну нову годину!

Како снимити и стримовати своју играћу сесију на Линук-у
У прошлости се играње игара сматрало само хобијем, али с временом је играчка индустрија забележила огроман раст у погледу технологије и броја играча. ...
Најбоље игре за ручно праћење
Оцулус Куест је недавно представио сјајну идеју ручног праћења без контролера. Са све већим бројем игара и активности које извршавају подршку формално...
Како приказати ОСД прекривач у целом екрану Линук апликација и игара
Играње игара преко целог екрана или коришћење апликација у режиму целог екрана без ометања може вам одсећи релевантне системске информације видљиве на...