GNU/Linux >> Znalost Linux >  >> Linux

jak je select() upozorněno na to, že se fd připravuje?

Hlásí, že je připraven do vracející se.

select čeká na události, které jsou obvykle mimo kontrolu vašeho programu. V podstatě voláním select , váš program říká:"Nemám co dělat, dokud ..., pozastavte prosím můj proces".

Podmínka, kterou určíte, je sada událostí, z nichž každá vás probudí.

Pokud například něco stahujete, vaše smyčka by musela čekat na příchod nových dat, časový limit, který by nastal, pokud se přenos zasekl, nebo na přerušení uživatelem, což je přesně to, co select ano.

Když máte více stahování, data přicházející na kterékoli z připojení spouští aktivitu ve vašem programu (data musíte zapsat na disk), takže seznam všech připojení ke stahování byste měli dát select v seznamu deskriptorů souborů, které se mají sledovat na "čtení".

Když někam nahrajete data ve stejnou dobu, opět použijete select abyste zjistili, zda připojení aktuálně přijímá data. Pokud je druhá strana na vytáčeném připojení, bude potvrzovat data jen pomalu, takže vaše místní vyrovnávací paměť pro odesílání je vždy plná a jakýkoli pokus o zápis dalších dat by se zablokoval, dokud nebude k dispozici vyrovnávací paměť, nebo se nezdaří. Předáním deskriptoru souboru, který posíláme na select jako deskriptor "write" jsme upozorněni, jakmile je k dispozici prostor ve vyrovnávací paměti pro odeslání.

Obecná myšlenka je, že váš program se stane řízeným událostmi , tj. reaguje na vnější události ze společné smyčky zpráv, spíše než aby prováděl sekvenční operace. Řeknete jádru "toto je soubor událostí, pro které chci něco udělat", a jádro vám poskytne sadu událostí, které se staly. To je docela běžné pro dvě události nastávající současně; například potvrzení TCP bylo zahrnuto do datového paketu, což může učinit stejný fd jak čitelným (data jsou k dispozici) i zapisovatelným (potvrzená data byla odstraněna z vyrovnávací paměti odesílání), takže byste měli být připraveni zvládnout všechny události před voláním select znovu.

Jedním z jemnějších bodů je select v podstatě vám dává příslib, že jedno vyvolání read nebo write nezablokuje, aniž by poskytl jakoukoli záruku ohledně samotného hovoru. Pokud je například k dispozici jeden bajt vyrovnávací paměti, můžete se pokusit zapsat 10 bajtů a jádro se vrátí a řekne „Napsal jsem 1 byte“, takže byste měli být připraveni zvládnout i tento případ. Typickým přístupem je mít vyrovnávací paměť „data, která mají být zapsána do tohoto fd“, a pokud není prázdná, je fd přidán do zapisovací sady a událost „writeable“ je zpracována pokusem o zápis všech data aktuálně ve vyrovnávací paměti. Pokud je buffer poté prázdný, v pořádku, pokud ne, počkejte znovu na "writeable".

Sada "výjimečná" se používá zřídka - používá se pro protokoly, které mají data mimo pásmo, kde je možné zablokovat přenos dat, zatímco ostatní data potřebují projít. Pokud váš program aktuálně nemůže přijímat data z „čitelného“ deskriptoru souboru (například stahujete a disk je plný), nechcete deskriptor zahrnout do „čitelné“ sady, protože nemůžete zpracovat událost a select by se okamžitě vrátil, pokud by byl znovu vyvolán. Pokud příjemce zahrne fd do „výjimečné“ sady a odesílatel požádá svůj zásobník IP o odeslání paketu s „urgentními“ daty, příjemce se pak probudí a může se rozhodnout, že nezpracovaná data zahodí a znovu se synchronizuje s odesílatelem. . telnet protokol to používá například pro manipulaci Ctrl-C. Pokud nenavrhujete protokol, který takovou funkci vyžaduje, můžete to snadno bez újmy vynechat.

Příklad povinného kódu:

#include <sys/types.h>
#include <sys/select.h>

#include <unistd.h>

#include <stdbool.h>

static inline int max(int lhs, int rhs) {
    if(lhs > rhs)
        return lhs;
    else
        return rhs;
}

void copy(int from, int to) {
    char buffer[10];
    int readp = 0;
    int writep = 0;
    bool eof = false;
    for(;;) {
        fd_set readfds, writefds;
        FD_ZERO(&readfds);
        FD_ZERO(&writefds);

        int ravail, wavail;
        if(readp < writep) {
            ravail = writep - readp - 1;
            wavail = sizeof buffer - writep;
        }
        else {
            ravail = sizeof buffer - readp;
            wavail = readp - writep;
        }

        if(!eof && ravail)
            FD_SET(from, &readfds);
        if(wavail)
            FD_SET(to, &writefds);
        else if(eof)
            break;
        int rc = select(max(from,to)+1, &readfds, &writefds, NULL, NULL);
        if(rc == -1)
            break;
        if(FD_ISSET(from, &readfds))
        {
            ssize_t nread = read(from, &buffer[readp], ravail);
            if(nread < 1)
                eof = true;
            readp = readp + nread;
        }
        if(FD_ISSET(to, &writefds))
        {
            ssize_t nwritten = write(to, &buffer[writep], wavail);
            if(nwritten < 1)
                break;
            writep = writep + nwritten;
        }
        if(readp == sizeof buffer && writep != 0)
            readp = 0;
        if(writep == sizeof buffer)
            writep = 0;
    }
}

Snažíme se číst, pokud máme k dispozici vyrovnávací paměť a nenastal žádný konec souboru nebo chyba na straně čtení, a pokoušíme se zapisovat, pokud máme ve vyrovnávací paměti data; pokud je dosaženo konce souboru a vyrovnávací paměť je prázdná, jsme hotovi.

Tento kód se bude chovat jasně neoptimálně (je to ukázkový kód), ale měli byste být schopni vidět, že je přijatelné, aby jádro dělalo méně, než jsme požadovali, jak při čtení, tak při zápisu, v takovém případě se prostě vrátíme a řekneme „kdykoli jste připraveni“, a že nikdy nečteme ani nepíšeme, aniž bychom se zeptali, zda bude blokován.


Ze stejné manuálové stránky:

Při ukončení se sady upraví na místě, aby indikovaly, které deskriptory souborů skutečně změnily stav.

Použijte tedy FD_ISSET() na sadách, které byly předány k výběru, abyste určili, které FD jsou připraveny.


Linux
  1. Jak potrubí omezují využití paměti?

  2. Jak získat přístup k phpMyAdmin

  3. Jak zabránit kybernetickému útoku

  1. Proč jsou data důležitá a jak je chránit

  2. Jak nainstalovat Ubuntu na VirtualBox

  3. Jak nainstaluji Duplicity na Ubuntu?

  1. Jak nainstalovat CentOS 7

  2. Jak rm funguje? Co dělá rm?

  3. Jak generovat data netflow v linuxu