abclinuxu.cz AbcLinuxu.cz itbiz.cz ITBiz.cz HDmag.cz HDmag.cz abcprace.cz AbcPráce.cz
AbcLinuxu hledá autory!
Inzerujte na AbcPráce.cz od 950 Kč
Rozšířené hledání
×
včera 17:00 | Zajímavý článek

Asociace pokročilých výpočetních systémů USENIX publikovala na svém YouTube kanálu videozáznamy online přednášek ze svých posledních konferencí. Doporučit lze například videozáznamy z USENIX Security '20 (29th USENIX Security Symposium) nebo videozáznamy z WOOT '20 (14th USENIX Workshop on Offensive Technologies). Ocenění nejlepší článek (Best Paper) na WOOT '20 získal článek BLESA: Spoofing Attacks against Reconnections in Bluetooth Low Energy.

Ladislav Hagara | Komentářů: 0
19.9. 15:55 | Bezpečnostní upozornění

Samba, svobodná implementace síťového protokolu SMB/CIFS, byla vydána ve verzích 4.12.7, 4.11.13 a 4.10.18. Řešena je bezpečnostní chyba CVE-2020-1472 v protokolu Netlogon (Zerologon). Microsoft ji ve svých produktech opravil 11. srpna. Jedná se o chybu s CVSS 9.8. Neautentizovaný útočník se může stát správcem domény.

Ladislav Hagara | Komentářů: 0
18.9. 16:22 | Nová verze

Byla vydána eRouška 2.0 pro Android a iOS. Nově využívá systém oznámení o možném kontaktu vyvinutý společnostmi Google a Apple. Zdrojové kódy eRoušky jsou k dispozici na GitHubu (Android, iOS).

Ladislav Hagara | Komentářů: 49
18.9. 15:33 | Humor

Máte na klávesnici málo kláves? Pomoci vám může 433% Keyboard [reddit, Wayback Machine].

Ladislav Hagara | Komentářů: 18
18.9. 13:33 | Komunita

Otevřená certifikační autorita Let’s Encrypt (Wikipedie) včera na svém blogu oznámila vydání 6 svých nových certifikátů: 1 kořenový, 4 mezilehlé a 1 křížově podepsaný. Kořenový certifikát ISRG Root X2 a mezilehlé E1 a E2 jsou již ECDSA místo RSA. Certifikační autorita Let’s Encrypt byla představena v listopadu 2014. První certifikát vydala přesně před pěti lety, v září 2015. Dnes jich denně vydává milion a půl.

Ladislav Hagara | Komentářů: 0
17.9. 23:11 | Komunita

Mozilla Corporation na svém blogu informuje, že ukončila služby Firefox Send a Firefox Notes. Mozilla Foundation na druhé straně představila rozšíření RegretsReporter. Jedná se o rozšíření pro Firefox a Chrome umožňující Mozillu informovat o doporučených videích na YouTube, jejíchž zhlédnutí uživatel lituje.

Ladislav Hagara | Komentářů: 34
17.9. 15:44 | Zajímavý článek

Společnost Nethemba informuje o již opravené kritické zranitelnosti v aplikaci Moje eZdravie na Slovensku. Kdokoli si mohl stáhnout informace o všech osobách testovaných na COVID-19 (jméno, příjmení, rodné číslo, telefonní číslo, místo pobytu, datum a výsledek odběru).

Ladislav Hagara | Komentářů: 43
17.9. 13:55 | Zajímavý software

GitHub CLI dospěl do verze 1.0.0. GitHub CLI umožňuje pracovat s GitHubem z příkazové řádky (gh issue list; gh pr status; gh release create; gh repo view; …).

Ladislav Hagara | Komentářů: 3
17.9. 09:00 | Nová verze

LabPlot (Wikipedie) je svobodná multiplatformní KDE aplikace pro interaktivní vytváření grafů a analýzu vědeckých dat. Téměř po roce vývoje byla vydána nová verze 2.8.

Ladislav Hagara | Komentářů: 0
17.9. 07:00 | Nová verze

Bylo vydáno Eclipse IDE 2020-09 aneb Eclipse 4.17. Představení novinek tohoto vývojového prostředí také na YouTube.

Ladislav Hagara | Komentářů: 0
Používáte aplikaci eRouška?
 (16%)
 (4%)
 (2%)
 (12%)
 (52%)
 (8%)
 (7%)
Celkem 356 hlasů
 Komentářů: 35, poslední včera 21:50
Rozcestník

Dotaz: Využítí pipe v C

31.8. 21:21 steel rock | skóre: 18
Využítí pipe v C
Přečteno: 948×

Ahoj, potřeboval bych poradit, jak implementovat pipe do C. Jako příklad jsem napsal jednoduchý prográmek

#include <stdio.h>

int main(int argc, char **argv) {
	const char size=16;
	FILE *f;
	char c,buffer[size];
	f=fopen(*++argv, "r");
	
	int i=0;
	while((c=getc(f)) != EOF){
		if (i>=size) break;
		buffer[i]=c;
		i++;
	}
		
	printf("%s\n", buffer);
	
	fclose(f);
	return 0;
}

u kterého funguje

$ ./programek hello.txt

hello world

ale potřeboval bych nějaký kód, ve kterém by fungovalo i

$ echo "hello world" | ./programek

hello world

...a zkusili jste to vypnout a zapnout?

Řešení dotazu:


Odpovědi

Řešení 4× (Gréta, xkucf03, Bherzet, steel rock (tazatel))
31.8. 22:42 kvr
Rozbalit Rozbalit vše Re: Využítí pipe v C
Zjednodušeně:

    f = argc > 1 ? fopen(*++argv, "r") : stdin;

... pominu-li různé kontroly na options, jestli je víc argumentů než jeden apod...
Řešení 1× (steel rock (tazatel))
31.8. 23:16 Bherzet | skóre: 17 | blog: Bherzetův blog
Rozbalit Rozbalit vše Re: Využítí pipe v C
Případně by se ještě mohlo hodit isatty.
6.9. 18:52 steel rock | skóre: 18
Rozbalit Rozbalit vše Re: Využítí pipe v C
Děkuji. Tato i předchozí odpověď byly přesně to, co jsem hledal.
...a zkusili jste to vypnout a zapnout?
1.9. 09:37 debian+ | skóre: 15 | blog: analyzy
Rozbalit Rozbalit vše Re: Využítí pipe v C
stdin - standart input
stdout - standart output
stderr - standart error output
debian.plus@protonmail.com
Řešení 2× (Gréta, jiwopene)
1.9. 13:17 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C
…jak implementovat pipe do C…

Huh? To, co popisuješ, není dýmka, nýbrž kočka.

A když už implementovat, tak co třeba

  • neomezovat to na jeden soubor,
  • neomezovat to na 16 znaků,
  • nečíst to po jednom znaku,
  • někdy aspoň trochu hlásit chyby…?

Takže třeba:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

static int copyfd(const int source, const int dest) {
  char buffer[4096];
  for (;;) {
    ssize_t to_write = read(source, buffer, sizeof(buffer));
    if (to_write <= 0) {
      return close(source) == -1 || to_write == -1 ? -1 : 0;
    }
    const char *wbuffer = buffer;
    for (;;) {
      const ssize_t written = write(dest, wbuffer, to_write);
      if (written < 0) {
        close(source);
        return -1;
      }
      to_write -= written;
      if (to_write == 0) break;
      wbuffer += written;
    }
  }
}

int main(int argc, const char* const* argv) {
  int result = EXIT_SUCCESS;
  if (argc > 1) {
    for (++argv; *argv; ++argv) {
      const int fd = open(strcmp(*argv, "-") == 0
                          ? "/dev/stdin"
                          : *argv,
                          O_RDONLY);
      if (fd == -1) {
        perror(*argv);
        result = EXIT_FAILURE;
        continue;
      }
      if (copyfd(fd, STDOUT_FILENO) == -1) {
        perror(*argv);
        result = EXIT_FAILURE;
      }
    }
  } else {
    if (copyfd(STDIN_FILENO, STDOUT_FILENO) == -1) {
      perror("dýmka");
      result = EXIT_FAILURE;
    }
  }
  return result;
}
ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
xkucf03 avatar 1.9. 14:28 xkucf03 | skóre: 49 | blog: xkucf03
Rozbalit Rozbalit vše Re: Využítí pipe v C
nečíst to po jednom znaku

Přes FILEgetc() se to nečte po jednom, ne?

$ echo ahoj | strace ./programek
…
read(0, "ahoj\n", 4096)                 = 5
read(0, "", 4096)                       = 0
…
write(1, "ahoj\n\n", 6)                 = 6
…

(s těmi ostatními připomínkami souhlasím)

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
1.9. 21:56 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C

Jasně, bufferuje se to. Potud fajn.

Dokonce getc() se může zainlajnovat, pokud ho (kni)hovny rozumně inlajnovatelně definují [[nevím; doufám]].

Ale ten test na EOF po každém bytu tam prostě přebývat bude; to by musel být hodně zázračný kompilátor, aby tohle zefektivnil na úroveň práce s celými buffery.

(Když se data tak či tak interpretují a procházejí, je to úplně jedno; stejně se bude na každý byte sahat. Nicméně když to má být jenom dýmka nebo kočka, čtení po znacích není zrovna super. (Vzpomínám si, že Java to kdysi takhle měla, ale to bylo jenom v těch (kni)hovnách, do kterých se člověk proklikal z IDE a které tam byly jenom na ukázku, zatímco v praxi [[praxe == běh mimo debugging]] tam bylo něco kolem JIT + JNI, co takové věci provádělo rozumně.))

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
Jendа avatar 1.9. 22:19 Jendа | skóre: 76 | blog: Výlevníček | JO70FB
Rozbalit Rozbalit vše Re: Využítí pipe v C
Inlajnování getc nevím (tuhle mi někdo vysvětloval že dělat LTO nad celou libc moc nejde), každopádně jednou jsem použil v cyklu fread(..., 1, 1, ...) a bylo to dost pomalé (operace s každým bajtem byla triviální, třeba řekněme sčítání). Overhead se stal zanedbatelným až když jsem freadem četl něco jako 8 bajtů najednou.
2.9. 08:53 debian+ | skóre: 15 | blog: analyzy
Rozbalit Rozbalit vše Re: Využítí pipe v C
Ono, zalezi to ako kolko das citas. Je rozdiel 6B/s voci 350MB/s. Pri kazdom volani fread dochadza k prepinany do kernelu (zvycajne, ale tiez C lib to trochu cachuje, takze kus menej prepinani), takze to moze zbytocne trochu spomalovat, ak sa to da urobit efektivnejsie.
debian.plus@protonmail.com
xkucf03 avatar 2.9. 09:13 xkucf03 | skóre: 49 | blog: xkucf03
Rozbalit Rozbalit vše FILE vs. FD
Pri kazdom volani fread dochadza k prepinany do kernelu (zvycajne, ale tiez C lib to trochu cachuje, takze kus menej prepinani)

Funkce fread() přece pracuje nad FILE, ne nad surovým FD, takže tam už buffer je a – jak ukazuje strace výše – systémové volání read() se volá méně často.

Jaký má smysl dělat další buffer v aplikaci? Je to opravdu jen kvůli té efektivnější kontrole na EOF, jak píše Andrej? Nebo je k tomu ještě nějaký jiný důvod?

Když chci mít buffer v aplikaci, není pak lepší se vykašlat na FILE a pracovat rovnou s FD?

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
2.9. 13:25 debian+ | skóre: 15 | blog: analyzy
Rozbalit Rozbalit vše Re: FILE vs. FD
Když chci mít buffer v aplikaci, není pak lepší se vykašlat na FILE a pracovat rovnou s FD?
s FILE su osetrene kadejake signali (takze ak nie si expert) tak je lepsie spracovat s tym. Samozrejme, ak sa da citat po blokov, tak citat po blokov.
Jaký má smysl dělat další buffer v aplikaci? Je to opravdu jen kvůli té efektivnější kontrole na EOF, jak píše Andrej? Nebo je k tomu ještě nějaký jiný důvod?
Programoval si nieco? Svet nie je len o http streamoch so stahovanim suborov. Lepsie je, ak prikazy (trebars v nejakej GUI hre) prichadzaju po blokoch. Ak neuplny prikaz, tak sa docita a sparcuje. Zas aj sietovy protokol funguje ze sa posiela minimalne po blokoch (ak chces poslat bajt po siety, tak to stoji rovnako ak posles naraz 5B (po MTU).

rovno pristup je citlevejsie na chyby - nejake chyba = poslanie signal() od OS (co casto byva kill aplikacie).
debian.plus@protonmail.com
2.9. 09:18 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C
Pri kazdom volani fread dochadza k prepinany do kernelu

To právě při testu fread(..., 1, 1, ...) nebude platit ani zdaleka. Tam bude spíš jiný problém, protože fread() je obecná funkce, která bude mít pro speciální případ čtení jednoho byte oproti getc() nebo fgetc() dost velký overhead.

Pokud se těch dat kopíruje opravdu hodně a jde o výkon, tak už může podle situace být výhodnější použít třeba něco jako mmap(), splice() nebo sendfile().

2.9. 09:51 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C
Schválně jsem si to teď změřil. Rozdíl mezi fgetc() a getc() je zanedbatelný, fread(..., 1, 1, ...) je skoro pětkrát pomalejší - volání jádra je samozřejmě stejně. Další zrychlení (asi na čtyřnásobek) se dá docílit použitím _unlocked verzí, s tím už jsem se s fgetc_unlocked() a getc_unlocked() dostal asi na 1.7-1.8 GB/s (4GB soubor na tmpfs).
2.9. 13:26 debian+ | skóre: 15 | blog: analyzy
Rozbalit Rozbalit vše Re: Využítí pipe v C
skus aj cez write().
debian.plus@protonmail.com
2.9. 15:39 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C

Tady jsou výsledky pro všechny možné varianty na 4GB souboru, kromě read_1, kde jde o extrapolaci z výsledku pro 1GB soubor (pouštět to znovu na čtyřikrát větší se mi opravdu nechtělo).

Je to načtení celého souboru a aby se vyloučily nějaké špinavé triky optimalizace, udělá po bytech xor. Soubor je samozřejmě na tmpfs. Čte se to pomocí fgetc(), getc(), fread() a read(). U libc funkcí navíc i s unlocked variantami. U fread() a read() se čte po blocích o 1B, 4KB nebo 1MB.

  fgetc                8.315s     492.6MB/s
  fgetc_unlocked       2.237s    1831.2MB/s
  getc                 8.247s     496.7MB/s
  getc_unlocked        2.236s    1831.5MB/s
  fread_1             70.633s      58.0MB/s
  fread_4K             1.521s    2693.5MB/s
  fread_1M             1.585s    2584.1MB/s
  fread_unlocked_1     5.439s     753.1MB/s
  fread_unlocked_4K    1.493s    2743.5MB/s
  fread_unlocked_1M    1.644s    2491.6MB/s
  read_1             750.744s       5.5MB/s
  read_4K              0.531s    7714.1MB/s
  read_1M              0.400s   10237.2MB/s

Zajímavé výsledky jsou víceméně jen dva:

  • {f,}getc_unlocked() není nijak propastně pomalejší než fread() nebo fread_unlocked() s rozumnou velikostí bloku
  • fread() s příliš velkým blokem může být i pomalejší než s 4KB (totéž platí i pro read(), ale tam je optimální velikost větší)

Zbytek je podle očekávání:

  • read() po 1B vychází naprosto tragicky
  • rozdíl mezi locked a unlocked stdio je výrazný u čtení po bytech, u delších bloků moc ne
  • libc funkce mají oproti syscallu nějakou tu režii, takže jde-li o výkon "na krev", může stát za to použí low level API (a nakonec třeba i něco jiného než read())
  • bude-li se to číst z běžného disku, bude nakonec jedno, co se použije, kromě teoretických extrémů (fread_1, read_1)
1.9. 14:28 debian+ | skóre: 15 | blog: analyzy
Rozbalit Rozbalit vše Re: Využítí pipe v C
Toto "/dev/stdin" robi zbytocnu zavistlost na udev fs /dev a tym padom zbytocne zrusenie multiplatformovnosti.
debian.plus@protonmail.com
1.9. 16:31 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C

Ano, tak už to bývá, že kočka není multiplatformní. Vnější projevy kočky multiplatformní jsou a musí být, ale kočka samotná ani moc ne.

To /dev/stdin je tam — zcela záměrně — kvůli chování podobnému cat v podivných okrajových případech. Třeba cat - -, cat - - - a tak podobně. Samozřejmě je to okrajová hloupost a neexistuje jedno jediné správné chování v takové situaci, takže nezbývá než zvolit prostě nějaké chování.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
xkucf03 avatar 1.9. 18:01 xkucf03 | skóre: 49 | blog: xkucf03
Rozbalit Rozbalit vše Re: Využítí pipe v C

Nebude STDIN_FILENO (či stdin) přenositelnější než /dev/stdin?

Navíc u toho STDIN_FILENO používáš již otevřený FD.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
1.9. 18:18 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C
Podle toho komentáře je to asi kvůli napodobení chování "cat - -" při spuštění z terminálu (je mu potřeba ukončit vstup dvakrát). Ale i to by asi šlo napsat bez spoléhání na /dev/stdin
1.9. 18:36 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C

Já si chování kočky představuju tak, že bez parametrů bere stdin a posílá to na stdout, bez velkého přemýšlení, zatímco s parametry vždycky něco otevírá (od čehož se bude odvíjet taky počáteční stav věcí kolem lseek() a tak.

Samozřejmě ten případ s několika pomlčkami nedává rozumný smysl, takže asi nestojí za to přehnaně se jím zabývat.

Jo, šlo by to klidně i jenom pomocí STDIN_FILENO a bez /dev/stdin. Pokud je standardní vstup seekovatelný, můžu si zapamatovat pozici, někam to převinout a kdesicosi. Pokud standardní vstup není seekovatelný, je úplně jedno, jestli ho znovu otevírám nebo ne; výsledek už nemůže být stejný. (Například když je to dýmka.)

Že používám už otevřený deskriptor, to je normální. Můžu třeba něco rozečíst a potom to dočíst kočkou. Například: Vypiš všechny řádky z /etc/passwd pod uživatelem ntp.

(
  while IFS=: read user discard; do
    [[ "$user" = 'ntp' ]] && break
  done
  cat
) < /etc/passwd

Tady^^^ se využívá toho, že vstup už je otevřený a v nějaké pozici.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
1.9. 14:37 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C
    ssize_t to_write = read(source, buffer, sizeof(buffer));
    if (to_write <= 0) {
      return close(source) == -1 || to_write == -1 ? -1 : 0;
    }

EINTR anyone?

1.9. 16:18 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C

Ne. Až tomu bude chtít uživatel opravdu posílat signály, pak ať si to ošetří. Do té doby je to úplně jedno.

A ani pak ať to raději explicitně neošetřuje — k tomu máme SA_RESTART, ne?

Implicitně je reakce na podstatné signály celkem předvídatelná — u SIGHUP / SIGBUS / SIGPIPE (tedy u toho, co opravdu v praxi může přijít samo od sebe a má to význam) beztak nejde o stav, ve kterém by se mělo něco zkoušet znova.

U ostatních signálů platí, že EINTR (nebo raději a lépe SA_RESTART) budu řešit jedině tehdy, pokud budu mít aspoň jeden netriviální handler signálu.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
1.9. 16:29 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C
Podle vás je naprosto v pořádku, že když se ten program stopne (např. pomocí Ctrl-Z) a pak nechá opět pokračovat, případně dostane nějaký defaultně ignorovaný signál jako SIGWINCH (protože se změní velikost terminálu), tak skončí "chybou"? Podle mne ne.
1.9. 17:16 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C

V pořádku je to, co neodporuje specifikaci. Bez specifikace je v pořádku téměř cokoliv.

Zkoušel jste vůbec něco z uvedeného? Nebo jenom zbytečně střílíte od pasu a úplně vedle? To druhé, že jo! :-D

Ne, neselže to na SIGWINCH, protože implicitní akce je Ign. EINTR se týká jen případů, kdy proběhne handler. To není tento případ.

Jednoduchý test 1: ./program /dev/zero > /dev/null (a změnit velikost okna, a zkusit Ctrl+Z a pak fg)

Jednoduchý test 2: ./program (a psát ptákoviny na terminálu a změnit mezitím velikost okna a párkrát zkusit Ctrl+Z a fg)

Ne a ne selhat, co???

No a pak třeba poslat kill -USR1 ... Teprve tohle ten proces (celkem správně) zabije, protože implicitní akce je Term.

Jak už jsem psal níže: „Potřeba“ ošetřit EINTR pramení ze všeho nejčastěji z nepochopení problematiky.

Jednoduchý test 3: Pomocí sigaction() si nastavte jednoduchý handler na SIGWINCH. Teprve s handlerem to odletí na SIGWINCH —> EINTR. S implicitní akci (Ign) rozhodně ne.

Takže résumé ještě jednou: Uživatel si EINTR ošetří až teprve tehdy, kdy bude chtít posílat a ošetřovat signály. Nebo se na to (raději a lépe) vybodne a nastaví si SA_RESTART. Ale to až bude používat vlastní handlery; dřív to ani nejde.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
1.9. 17:38 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C
Ne a ne selhat, co???

A zkusil jste se taky podívat pomocí strace, co přesně se tam děje? Viděl byste totiž něco jako

24241 read(0, 0x7ffcf84071d0, 4096)     = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
24241 --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
24241 read(0, 0x7ffcf84071d0, 4096)     = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
24241 --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
24241 read(0, 0x7ffcf84071d0, 4096)     = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
24241 --- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=22361, si_uid=1000} ---
24241 read(0, 0x7ffcf84071d0, 4096)     = ? ERESTARTSYS (To be restarted if SA_RESTART is set)

Jestli chcete psát své programy tak, aby byla funkčnost závislá na tom, že vždy poběží na systému, který bude nastavovat flagy přesně tak, jako ten jeden, na kterém jste si to empircky vyzkoušel, je to váš problém. Jen to, prosím, neučte ostatní.

EINTR se týká jen případů, kdy proběhne handler.

Ne, u read() se EINTR se týká jakéhokoli případu, kdy je syscall přerušen signálem, aniž by přečetl data. Máte to napsané i v manuálové stránce, možná by bylo lepší si ji přečíst, než začnete psát věci jako

„Potřeba“ ošetřit EINTR pramení ze všeho nejčastěji z nepochopení problematiky.

Jinak taková věta vyzní poněkud ironicky.

1.9. 18:47 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C
A zkusil jste se taky podívat pomocí strace, co přesně se tam děje? Viděl byste totiž něco jako

Napadlo vás někdy, že syscally z strace jsou interní implementační detail, který se může a nemusí promítnout do výsledného chování toho API v C? ;-)

Máte to napsané i v manuálové stránce, možná by bylo lepší si ji přečíst

Nemám.

Takže nejenom střílíte od pasu, ale ani tu manuálovou stránku jste si nepřečetl. Výborně! Tak tohle prosím hlavně neučte ostatní.

Pojďme se, prosím, podívat do té manuálové stránky (kterékoliv, která pojednává o EINTR).

EINTR  While blocked waiting to complete an open of a slow device (e.g., a FIFO; see fifo(7)),
       the call was interrupted by a signal handler; see signal(7).

Co tam^^^ stojí? Signal handler.

…handler…

…handler…

…handler…

Už? "signal handler" != "signal"

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
1.9. 19:22 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C
Napadlo vás někdy, že syscally z strace jsou interní implementační detail, který se může a nemusí promítnout do výsledného chování toho API v C? ;-)

A napadlo někdy vás, že jste prostě jen měl štěstí a zdědil SA_RESTART flag od bashe?

Nemám. Takže nejenom střílíte od pasu, ale ani tu manuálovou stránku jste si nepřečetl.

Četl - a na rozdíl od vás dostatečně pozorně. Zkusme začít s read(3p), tj. oficiální posixovou dokumentací:

EINTR The read operation was terminated due to the receipt of a signal, and no data was transferred.

Ať čtu, jak čtu, žádné slovo "handler", bez ohledu na tučnost nebo velikost, tam není.

No a když se podíváme na read(2), tedy linuxovou verzi:

EINTR The call was interrupted by a signal before any data was read; see signal(7).

Zase žádné handler, bez ohledu na font. V signal(7) sice slovo handler je, ale z kontextu je zřejmé, že tím není myšlen pouze vlastní handler, který by si proces zaregistroval pomocí signal() nebo sigaction(), ale handler obecně, což zahrnuje i případ ignorovaného signálu.

1.9. 21:34 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C
Napadlo vás někdy, že syscally z strace jsou interní implementační detail, který se může a nemusí promítnout do výsledného chování toho API v C? ;-)

A napadlo někdy vás, že jste prostě jen měl štěstí a zdědil SA_RESTART flag od bashe?

Vaříte z vody. Už zase jste to totiž nedočetl:

A child created via fork(2) inherits a copy of its parent's signal dispositions. 
During an execve(2), the dispositions of handled signals are reset to the default; the dispositions of ignored signals are left unchanged.

S tím čtením prostě nemůžete skončit v půlce, po první větě. :-D Je třeba to dočíst.

Nebo, když už v tom tápete, můžete si to přece snadno vyzkoušet, ne? Tak, jestlipak se SA_RESTART dědí?

#define _POSIX_C_SOURCE 200809L
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static void print_sigaction(const char* name,
                            const struct sigaction* sa) {
  printf("%s %s SA_RESTART\n",
         name,
         sa->sa_flags & SA_RESTART ? "has" : "doesn't have");
}

static void useless(int sig) {
  puts(strsignal(sig));
}

int main(int argc, char* const* argv) {
  struct sigaction old_sa;
  if (argc == 1) {  // BEFORE exec
    const struct sigaction new_sa = {
      .sa_handler = useless,
      .sa_flags = SA_RESTART,
    };
    if (sigaction(SIGWINCH, &new_sa, &old_sa) == -1) {
      perror(argv[0]);
      return EXIT_FAILURE;
    }
    print_sigaction("old sigaction BEFORE exec", &old_sa);
    print_sigaction("new sigaction BEFORE exec", &new_sa);
    char* const one_empty[] = {argv[0], "", NULL};
    if (execv(argv[0], one_empty) == -1) {
      perror(argv[0]);
      return EXIT_FAILURE;
    }
  } else if (argc == 2) {  // AFTER exec
    if (sigaction(SIGWINCH, NULL, &old_sa)) {
      return EXIT_FAILURE;
    }
    print_sigaction("old sigaction AFTER exec", &old_sa);
  } else {
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}

Kvízová otázka za 10 bodů: Co tohle^^^ vypíše?

old sigaction BEFORE exec doesn't have SA_RESTART
new sigaction BEFORE exec has SA_RESTART
old sigaction AFTER exec doesn't have SA_RESTART

Pro příště: Nevymýšlejte si, prosím, ptákoviny. :-) Ptákoviny (jako že se dědí SA_RESTART) se snadno vymýšlejí, ale obtížně vyvracejí, protože to můžou být přece jen okrajové případy a signal disposition může znít příliš vágně. Příští tvrzení tohoto druhu bych prosil s manuálovou stránkou (bez vynechaného kontextu) a se snippetem, který to ukazuje. ;-)

Nemám. Takže nejenom střílíte od pasu, ale ani tu manuálovou stránku jste si nepřečetl.

Četl - a na rozdíl od vás dostatečně pozorně. Zkusme začít s read(3p), tj. oficiální posixovou dokumentací:

EINTR The read operation was terminated due to the receipt of a signal, and no data was transferred.

Ať čtu, jak čtu, žádné slovo "handler", bez ohledu na tučnost nebo velikost, tam není.

Takže ani tu POSIXovou stránku neinterpretujete správně, jak jste se koneckonců už sám přesvědčil experimentem, že? :-)

Tentokrát vám unikl přesný význam pojmu receipt of a signal; do této kategorie se implicitní akce Ign nepočítá.

Výmluvné jsou také některé kousky textu, které najdete v man 7 signal:

Interruption of system calls and library functions by signal handlers
    If a signal handler is invoked while a system call or library function call is blocked, then either:
    ...

Klíčová slova: handler, invoked

No a když se podíváme na read(2), tedy linuxovou verzi:

EINTR The call was interrupted by a signal before any data was read; see signal(7).

Tedy najednou má specifická linuxová verze (o které jste se už sám přesvědčil, že je nepřesná) přednost? Jenže o pár komentářů výše jste napsal toto:

Jestli chcete psát své programy tak, aby byla funkčnost závislá na tom, že vždy poběží na systému, který bude nastavovat flagy přesně tak, jako ten jeden, na kterém jste si to empircky vyzkoušel…

Aha? Jenže najednou má podle vás linuxová verze manuálové stránky jakousi váhu? Nezměnil jste nějak rychle názor, doslova obratem? :-D

Zase žádné handler, bez ohledu na font. V signal(7) sice slovo handler je, ale z kontextu je zřejmé, že tím není myšlen pouze vlastní handler, který by si proces zaregistroval pomocí signal() nebo sigaction(), ale handler obecně, což zahrnuje i případ ignorovaného signálu.

Proto jsou ty POSIXové stránky většinou lepší než linuxové. Rozlišují velmi důsledně pojmy action, handler a signal. To jsou velmi důležité nuance.

Upřímně řečeno, já jsem opravdu (ale opravdu (ale opravdu ...)) ten poslední, komu má smysl cpát tohle profláknuté EINTR-náboženství. Jsem na něj alergický hlavně proto, že zbytečné rádoby-ošetření EINTR vyhazuju z kódu už asi tak dekádu. Vyhazuju to vidlema. Vyhazuju to oknem. Vyhazuju to vraty. Splachuju to do hajzlu. Ale ono se to vždycky vrátí komínem, protože někdo (už zase) nepochopil, že POSIX je navržený tak, aby se s tím API snadno / rychle / korektně programovalo, nikoliv tak, aby se člověk musel na 50 řádcích věnovat ošetření signálů, než si přečte jeden soubor.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
1.9. 22:32 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C

OK, nezdědí se, tak za to holt může glibc, ale to je ve výsledku úplně jedno. Pořád to není garantované chování, na které by se bylo možné spolehnout.

Takže ani tu POSIXovou stránku neinterpretujete správně, jak jste se koneckonců už sám přesvědčil experimentem, že? :-)

Experimentem se můžu přesvědčit nanejvýš o tom, že se v jednom konkrétním případě (nebo několika) ten syscall defaultně restartuje, i když to tak explicitně nenastavím. Na rozdíl od vás mi takový experiment nestačí k závěru, že se na to dá spoléhat vždy a všude.

Tentokrát vám unikl přesný význam pojmu receipt of a signal; do této kategorie se implicitní akce Ign nepočítá.

Vůbec ne, to je jen vaše interpretace. A strace vás usvědčuje z omylu, protože je z něj naprosto jasně vidět, že ten syscall přerušen byl a vy jste to nepoznal jen proto, že byl restartován.

Tedy najednou má specifická linuxová verze (o které jste se už sám přesvědčil, že je nepřesná) přednost? Nezměnil jste nějak rychle názor, doslova obratem?

A to jste si vymyslel jakým myšlenkovým pochodem? Jen jsem vám ukázal, že obě verze manuálové stránky (1) v rozporu s vaším trvzením EINTR zmiňují a (2) o žádném handleru nemluví. A pokud, ja sám tvrdíte, upřednostňujete posixovou dokumentaci tak to signal(7) není.

že zbytečné rádoby-ošetření EINTR vyhazuju z kódu už asi tak dekádu. Vyhazuju to vidlema. Vyhazuju to oknem. Vyhazuju to vraty. Splachuju to do hajzlu.

Inu, jen si vyhazujte, splachujte, jak je ctěná libost. Jen se pak holt nedivte, až vás s tím někde vyhodí maintainer, který na rozdíl od vás nechce spoléhat na to, že vaše emprická zkušenost ("zkusil jsem to a funguje mi to") má univerzální platnost.

Mně může být koneckonců jedno, co si děláte na svém písečku, to je váš problém. Pokud vás nepřesvědčil ani naprosto jednoznačný výstup strace, pak asi nemá smysl, abych se o cokoli snažil. Žijte si blaze.

1.9. 23:03 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C
Vůbec ne, to je jen vaše interpretace. A strace vás usvědčuje z omylu, protože je z něj naprosto jasně vidět, že ten syscall přerušen byl a vy jste to nepoznal jen proto, že byl restartován.

Zdá se, že nechápete, že to API nepracuje (navenek) s konceptem syscallů (které POSIXové API jo?) a že interně si může bez vašeho vědomí (ne)používat syscally, jak (ne)chce. No, tak to prostě je. :-)

A to jste si vymyslel jakým myšlenkovým pochodem? Jen jsem vám ukázal, že obě verze manuálové stránky (1) v rozporu s vaším trvzením EINTR zmiňují a (2) o žádném handleru nemluví. A pokud, ja sám tvrdíte, upřednostňujete posixovou dokumentaci tak to signal(7) není.

Obě manuálové stránky jsou v souladu s mým tvrzením. Že jste je nepochopil a/nebo nedočetl, to už není můj problém. ;-)

Inu, jen si vyhazujte, splachujte, jak je ctěná libost. Jen se pak holt nedivte, až vás s tím někde vyhodí maintainer, který na rozdíl od vás nechce spoléhat na to, že vaše emprická zkušenost ("zkusil jsem to a funguje mi to") má univerzální platnost.

Nebojte se, nevyhodí. Chápe totiž psaný text, chápe, jak tohle API funguje, chápe, jak tenhle svět funguje, atd.

Mně může být koneckonců jedno, co si děláte na svém písečku, to je váš problém. Pokud vás nepřesvědčil ani naprosto jednoznačný výstup strace, pak asi nemá smysl, abych se o cokoli snažil. Žijte si blaze.

Jednoznačný výstup strace? To má být vtip? Vy opravdu nechápete rozdíl mezi rozhraním a implementací? :-D To myslíte fakt vážně? Uf. To snad ne.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
1.9. 23:18 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C
Jak už jsem řekl: vzdávám to, dělejte si co chcete.
2.9. 19:14 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Využítí pipe v C
Tentokrát vám unikl přesný význam pojmu receipt of a signal; do této kategorie se implicitní akce Ign nepočítá.
A odkázal bys pls, kde je tohle v POSIXu deklarováno?

Osobně bych taky očekával, že pokud jsem si nenastavil signal handlery, neměl by EINTR nastat, ale zajímalo by mě, jestli to POSIX skutečně takhle říká.

No jinak ale samozřejmě je tu stále ten argument, že i když program aktuálně žádný signal handlery nemá, někdo může přidat handler v budoucnu a pak procházet kód a hledat syscally je opruz. (Už jsem to musel dělat na netriviálním SW a není to dobrý.)
Your rice wet, you fucked up.
2.9. 20:20 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C
Tentokrát vám unikl přesný význam pojmu receipt of a signal; do této kategorie se implicitní akce Ign nepočítá.
A odkázal bys pls, kde je tohle v POSIXu deklarováno?

Osobně bych taky očekával, že pokud jsem si nenastavil signal handlery, neměl by EINTR nastat, ale zajímalo by mě, jestli to POSIX skutečně takhle říká.

SIG_IGN: Ignore signal.
Delivery of the signal shall have no effect on the process.

Tohle^^^ je implicitní nastavení třeba zrovna pro SIGWINCH. Kdyby se procesu zničehonic přerušilo blokující volání s chybou EINTR, byl by to setsakra veliký effect. Specifikace ovšem nařizuje no effect.

Nikde to POSIX neříká zpříma v jedné větě. Je to rozprostřené v drobných vágních náznacích na mnoha místech v popisu signálů. (Proto v tom má tolik lidí nejasnosti a proto se tyhle „chyby“ (ošetření chyb, které nenastanou) vidí tak často.)

No jinak ale samozřejmě je tu stále ten argument, že i když program aktuálně žádný signal handlery nemá, někdo může přidat handler v budoucnu a pak procházet kód a hledat syscally je opruz. (Už jsem to musel dělat na netriviálním SW a není to dobrý.)

Na tohle mám jednoduchý protiargument: Zbytečné ošetření neexistujícího EINTR přidává netestovaný / netestovatelný kód, který nikdy neběžel. Neměl jak. Někde tam sedí a čeká, může být úplně špatně, může se zacyklit napořád, může leakovat paměť, může to celé shodit.

Pár let to takhle funguje. Pak někdo přidá handler na SIGWINCH… (Nebo na jiný implicitně ignorovaný signál.) Najednou se vrátí chyba EINTR. A celé to zařve, spousta věcí bude špatně, třeba to nezopakuje správně zápis a poškodí integritu dat atd. A bude se to fakt špatně debuggovat.

Lepší scénář: EINTR zprvu nikdo neošetřuje — což je v nepřítomnosti handlerů správně —, později se přidá handler signálu, nastane EINTR a celé to korektně odletí s chybou EINTR. V té chvíli se bude (teprve) řešit osetření EINTR — správné, testovatelné, projde valgrindem při změnách velikosti okna atd. A od té doby to bude fungovat šťastně a spokojeně se SIGWINCH —> EINTR, bez ošklivých překvapení.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
2.9. 22:08 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Využítí pipe v C
Zbytečné ošetření neexistujícího EINTR přidává netestovaný / netestovatelný kód, který nikdy neběžel. Neměl jak.
Myslíš ten loop? Vždyť je to poměrně velmi jednoduchá věc, nakterou by nemělo být velký problém napsat makro (v GNU libc už existuje), to otestovat a pak prostě nasadit víceméně kdekoliv. Nebo v čem tam vidíš tu kompliaci?
Lepší scénář: EINTR zprvu nikdo neošetřuje — což je v nepřítomnosti handlerů správně —, později se přidá handler signálu, nastane EINTR a celé to korektně odletí s chybou EINTR.
Jak celé to korektně odletí? Ten EINTR může nastat někde kdesi daleko v kódu třeba někdy o roky později. Jak se dozvíš, na všech místech v programu (který může být rozsáhlý), kde by mohl nastat EINTR, je tato možnost správně ošetřena?

Např. bys mohl spouštět testy a u toho mít v test harnessu nějaký background thread, který bude spamovat signály. Všimni si ale, že tím se zároveň řeší i tvůj problém s netestovaným/netestovatelným kódem.
Your rice wet, you fucked up.
2.9. 23:52 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C
Např. bys mohl spouštět testy a u toho mít v test harnessu nějaký background thread, který bude spamovat signály. Všimni si ale, že tím se zároveň řeší i tvůj problém s netestovaným/netestovatelným kódem.

Tak potom bych vůbec nic nenamítal a všechno by bylo v nejlepším pořádku. :-)

Jen jsme se tím dostali příliš daleko od snippetu v mém původním komentáři, co do rozsahu a celkové složitosti příslušného softwaru.

Že něco korektně odletí, tím myslím, že to ohlásí rozumnou chybu, zavře správně (aspoň) všechno, co nezavírá samotný konec programu (různé IPC prostředky), nenadělá paseku v souborech nebo jiných „perzistentních“ datech a neskončí karambolem typu SIGSEGV, SIGILL ani ničím podobným. (SIGABRT je tak na hraně; pokud to má někdo dobře ošetřené, tak fajn.)

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
Jendа avatar 1.9. 22:29 Jendа | skóre: 76 | blog: Výlevníček | JO70FB
Rozbalit Rozbalit vše Re: Využítí pipe v C
Dobré vlákno, ale nechtěl bys jen tak bokem zmínit, jak by to tedy mělo být správně? :-) Jde tedy o to, že read/write můžou vrátit 0 nebo -1, i když je všechno v pořádku (jenom přišel signál), a tedy ho stačí zavolat znovu a zápis/čtení proběhne?

Takže pro read by podmínka měla být
ssize_t r = read(...);
if (r == 0) // konec souboru
if (r < 0 && errno != EINTR) // chyba
// jinak pokračovat ve čtení na pozici buf+r
A pro write
ssize_t r = write(...);
if (r <= 0 && errno != EINTR) // chyba
// jinak pokračovat v zápisu na pozici buf+MIN(0,r) (fakt? protože r může být -1 a errno EINTR?)
?
1.9. 22:59 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C

Pokud dostanu chybu a errno je EINTR, pak se "nic nestalo" a typická akce je zopakovat to volání se stejnými parametry. V glibc je na to makro TEMP_FAILURE_RETRY():

  ret = TEMP_FAILURE_RETRY(read(...));

ten read() opakuje, tak dlouho, dokud buď neuspěje nebo nedostane jinou errno než EINTR. Pokud to budeš dělat ručně (a s jednou smyčkou), tak je prostě potřeba v případě -1/EINTR neupdatovat pozici (ani zbývající délku).

if (r <= 0 && errno != EINTR)

Na tohle taky pozor. Standard pouze stanoví, že skončí-li volání chybou, nastaví se errno na odpovídající hodnotu, ale neříká nic o tom, co se s errno stane, pokud volání uspěje. Empirická zkušenost je taková, že obvykle zůstane beze změny, a už jsem párkrát viděl kód, který na to spoléhal (nastavil před voláním errno na nulu a testoval hodnotu po návratu). Obecně to ale není zaručeno a i když jsem takovou implementaci v praxi neviděl, teoreticky je možné, že hodnota po úspěšném volání nebude ani nula, ani původní.

1.9. 23:18 Bherzet | skóre: 17 | blog: Bherzetův blog
Rozbalit Rozbalit vše Re: Využítí pipe v C
Standard pouze stanoví, že skončí-li volání chybou, nastaví se errno na odpovídající hodnotu, ale neříká nic o tom, co se s errno stane, pokud volání uspěje.
Takže by to mohlo nastavit errno = EINTR po neúspěšném volání a po úspěšném ho nezměnit, tzn. že by TEMP_FAILURE_RETRY vedl na nekonečnou smyčku?
1.9. 23:21 Bherzet | skóre: 17 | blog: Bherzetův blog
Rozbalit Rozbalit vše Re: Využítí pipe v C
Sorry, blbost, ono ještě záleží na tom, co vrací read.
1.9. 23:22 Michal Kubeček | skóre: 71 | Luštěnice
Rozbalit Rozbalit vše Re: Využítí pipe v C

Ne, to makro testuje obě podmínky, tj. opakuje jen pokud se argument vyhodnotí jako -1 a navíc je errno rovno EINTR. Na mém systému např. vypadá takhle (je v /usr/include/unistd.h):

/* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno'
   set to EINTR.  */

# define TEMP_FAILURE_RETRY(expression) \
  (__extension__                                                              \
    ({ long int __result;                                                     \
       do __result = (long int) (expression);                                 \
       while (__result == -1L && errno == EINTR);                             \
       __result; }))
1.9. 23:24 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C

Jediná berná mince je návratová hodnota z příslušné funkce. Při/po úspěchu se na errno nesahá.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
1.9. 23:16 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C

0 z read() je EOF, tedy v podstatě the ultimate úspěch.

0 z write() při nenulové délce je snad leda chyba v kernelu nebo v (kni)hovnách. Ale já se v počítačích nevyznám, tak nevím.

-1 z read() nebo write() je

  • nenávratný průser, pokud jsem si nehrál s handlery; v tom případě to můžu (pro daný file descriptor) zabalit, prostě se to posralo.
  • možná jenom EINTR, pokud jsem si hrál s handlery a někde jsem si nějaký netriviální handler na daný signál nastavil a nemám signál zamaskovaný a dostal jsem ten signál, handler proběhl atd. Jenže kdybych si byl radši nastavil SA_RESTART, tak jsem se touhle věcí nemusel vůbec zabývat. (Jinak -1 vždycky říká, že se nezapsalo nic, takže po EINTR se to má znova zkusit se stejnými parametry. Ale megafonem všem místo toho doporučuju: SA_RESTART.)
ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
1.9. 23:20 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C
0 z read() je EOF, tedy v podstatě the ultimate úspěch.

(*) při nenulové délce. (Než aby mě někdo zase bral za slovo, radši se za něj vezmu sám.)

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
xkucf03 avatar 1.9. 18:09 xkucf03 | skóre: 49 | blog: xkucf03
Rozbalit Rozbalit vše LetsEncrypt a padání při změně velikosti okna
protože se změní velikost terminálu

Tahle hrůza byla (nebo možná ještě je) v LetsEncrypt, když se používá v manuálním režimu. (jasně, certifikáty by se měly obnovovat automaticky, ale to teď nechme stranou) To si takhle člověk projde přes X domén, vykopíruje si jejich kódy, odskočí si do jiného terminálu… a když se vrátí zpět, tak zjistí, že LetsEncrypt spadl (kvůli změně okna – třeba při otevřeném vyhledávacím panelu v Konsoli) a je třeba začít znova.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
1.9. 18:53 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: LetsEncrypt a padání při změně velikosti okna

Tahle hrůza postihuje kdekoho, kdo si zbytečně a neopatrně hraje s handlery. :-) Někdy se to týká skriptovacích jazyků, někdy zase některých nastavení ncurses atd. atp.

Možnosti jsou v podstatě dvojí: Buď z toho udělat téměř neinteraktivní utilitu, bez chytrých promptů, editace v terminálu atd., a nechat všechno v implicitní konfiguraci; pak to zkrátka funguje, podle očekávání. Nebo dělat různé triky se správou terminálu a se signály s ní souvisejícími. To je sice bezva, jenže pak se musí všechno nastavit od začátku do konce, nikoliv jen napůl.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
xkucf03 avatar 1.9. 19:10 xkucf03 | skóre: 49 | blog: xkucf03
Rozbalit Rozbalit vše Re: LetsEncrypt a padání při změně velikosti okna

Ona ta chyba dost možná není v těch aplikacích, ale ve výchozím chování Pythonu nebo nějaké jeho knihovny…

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
1.9. 21:41 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: LetsEncrypt a padání při změně velikosti okna

To rozhodně. Ale těžko to těm (kni)hovnám vyčítat, když takový interpret Pythonu má single-source portabilitu přes velké N platforem. Pak tam možná bude něco jako ncurses přes swig nebo cojávímco a pak už není vůbec jasné, co se tam děje za paseku.

(Jinak samozřejmě je to bug, to bezesporu, když se to takhle posírá. Nikdy jsem u běžící Let's Encrypt nezkoušel změnit velikost terminálu a ani to zkoušet nehodlám, no nicméně tohle přece musí ustát bez debat. Přesně jako (výše uvedený) kód, který si se signály vůbec nehraje a jen spoléhá na to, že implicitně je vše nastavené rozumně (což je!).

(Protože upřímně, kdo by v praxi chtěl, aby nějaká dvacetiřádková prasárna v C, kterou narychlo sesmolil jako prototyp, náhodně zařvala kvůli SIGWINCH? (No nehlaste se všichni.))
ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
1.9. 15:36 debian+ | skóre: 15 | blog: analyzy
Rozbalit Rozbalit vše Re: Využítí pipe v C
A ak nie je nevyhnutne pouzivat read(), write(), close(), tak ma (urcite zaciatocnik) pouzivat s prefixom f. Napr. s f funckie osetruju vynimky signal(7).
debian.plus@protonmail.com
1.9. 16:22 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C
Napr. s f funckie osetruju vynimky signal(7).

Což není potřeba ošetřovat (resp. „potřeba“ nejčastěji plyne z nepochopení problematiky), ale jinak souhlasím, že používat čistě pro dobrý pocit (a malý kousek portovatelnosti navíc) funkce f.* je správné.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
2.9. 13:39 debian+ | skóre: 15 | blog: analyzy
Rozbalit Rozbalit vše Re: Využítí pipe v C
Ono bavit sa ci fwrite() alebo write() je mozno usmevne pri jednoduchych prikladov. I mne sa pacili write(), alebo ked som potom dostaval signaly, tak som usudil, ze fwrite je fajn. Daco som v zivote aj C programoval a upravoval prehladavac, tak vstupuje aj clovek do neznamych vod - nevie co tam presne je a co vsetko moze nastat (a nechce sa mu kompletne studovat kod). Tak naco si robit problemy.

Ak chceme efektivitu, mozme rovno pouzivat API kernelu resp. pisat v ASM
debian.plus@protonmail.com
2.9. 19:59 rastos | skóre: 62 | blog: rastos
Rozbalit Rozbalit vše Re: Využítí pipe v C
Páni, páni! Oceňujem vašu hlbokú znalosť systému a runtime knižnice ako aj dôslednosť v ošetrovaní hraničných situácií, ale nemyslíte, že by vaša odpoveď mala zodpovedať úrovni toho, kto položil otázku?
2.9. 20:25 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C

Tak já jsem odpověděl konkrétním kódem, který si může tazatel spustit.

Nemůžu za to, že pak (už zase) někdo vytáhl na světlo EINTR pověru.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
6.9. 19:17 steel rock | skóre: 18
Rozbalit Rozbalit vše Re: Využítí pipe v C
Andreji, sice jste odpověděl, ale nemusíte nutně dělat chytrého. Já jsem psal, že uvádím kód jako příklad a opravdu jsem se v příkladu nezabýval všemi ošetřeními a velikostí bufferu (text "hello world" se tam vejde). Nechci do poradny dávat lidem moc rozsáhlý kód, aby se s ním museli přelouskávat, když jsem chtěl stejně radu, jak tam přidat dva nebo tři příkazy. Přesto děkuji za pomoc, po vyřešení mého problému byla diskuze zajímavé čtení :D
...a zkusili jste to vypnout a zapnout?
7.9. 09:56 Andrej | skóre: 48 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Využítí pipe v C

Když už prý „dělám chytrého“, tak dobrá, začnu „dělat chytrého“:

Na původním příkladu nevadilo, že byl krátký — to by byla velká přednost, kdyby fungoval.

Jenže on nefunguje — nejen kvůli omezení velikosti souboru na 16 bytů; to nechme stranou. Horší je, že dělá nedefinované operace s neinicializivanou pamětí.

Jak printf() zjistí, kde končí string? Co za string zapíše koncovou nulu? Nic. :-( Nula tam může být díky šťastné náhodě. Za 16. bytem bufferu už je to spíš výhra v loterii, že to neodletí (sahání na blíže neurčenou část zásobníku). (Na 17. byte (a možná dál) sáhne printf() pokaždé, když vstup bude mít aspoň 16 bytů nebo když buffer nebude šťastnou náhodou ukončený nulou.)

Tedy celý rádoby-jednoduchý příklad nikdy, ani jednou neběžel s valgrindem.

Teď se zbytečně povaluje na webu další totálně rozbitý příklad, který má v C nedefinované chování.

(A ano, chápu, že předmět dotazu byl úplně jiný.)

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
3.9. 13:34 Tomas
Rozbalit Rozbalit vše Re: Využítí pipe v C
libpipeline. Nemá smysl vymýšlet kolo.

Založit nové vláknoNahoru

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

ISSN 1214-1267   www.czech-server.cz
© 1999-2015 Nitemedia s. r. o. Všechna práva vyhrazena.