Florin Malita scria la data de 27 Decembrie 2005: > On Tue, 2005-12-27 at 12:16 +0200, Liviu Daia wrote: > > > Este un bug, pentru ca nu respecta litera standardului. Conform > > POSIX 1 libc ar trebui sa aloce cel putin BUFSIZ pentru buffer > > (poate aloca mai mult, dar minimul garantat e BUFSIZ). Mentiunea ca > > buffer-ul poate sa nu fie folosit in intregime pentru asta a aparut > > de-abia in 1999 sau 2000, intr-o propunere de standard a IEEE (iti > > pot da referinta exacta dupa Anul Nou, cand voi reveni in tara). > > Feature, not a bug :) Astept referinta dar intre timp ar fi cel mult o > neconformanta POSIX _daca_ urmatoarele nu ar fi adevarate: > > 1) set{v,}buf nici nu pretinde ca ar fi conform POSIX :P
Poate. Aici am citat din amintiri. Ok, e momentul sa reamintesc si eu afirmatia mea, pe care tu iti propui sa o contrazici: Conform standardelor (POSIX, ANSI, sau oricare altul relevant pentru contextul asta), buffer-ul alocat default pentru fisierele stdio(3) poate avea o lungime fi mai mare sau egala cu BUFSIZ, dar nu mai mica. Accentuez aici cuvantul _default_. In sensul asta toata discutia despre setvbuf(3) si familia nu e relevanta pentru ce spun eu, pentru simplu motiv ca aceste functii se pot apela numai dupa ce fisierul a fost deschis (nu inainte), pentru a _modifica_ buffer-ul care i-a fost alocat default. De dragul discutiei iti raspund totusi si la asta, pentru ca, tipic, dintr-o asemenea discutie au de invatat ambele parti. O consecinta imediata a afirmatiei de mai sus este ca buffering-ul din glibc (verificat de mine doar pentru versiunile >= 2.0) nu e conform standardelor, pentru ca glibc aloca, de exemplu pentru fisiere create sub ext3, buffer-e mai mici decat BUFSIZ (4 KB in loc de 8 KB). > CONFORMING TO > The setbuf and setvbuf functions conform to ANSI X3.159-1989 (''ANSI > C''). Cu atat mai bine. ANSI a aparut inaintea POSIX si se refera la toate implementarile C. > 2) *BSD se comporta similar (cand determina dimensiunea bufferului se > uita _doar_ la st_blksize si nu foloseste BUFSIZ decat daca nu poate > determina aceasta valoare): Poate ca asta se intampla pentru ca sub *BSD BUFSIZ = 1 KB, iar st_blksize nu poate fi mai mic. Deci nu mai e nevoie sa testeze nimic. > http://developer.apple.com/documentation/Darwin/Reference/ManPages/man3/setbuf.3.html > > The size argument may be given as zero to obtain deferred optimal-size > buffer allocation as usual. Doua obiectii. Prima e legata de logica: imi dai un exemplu un man page de sub Darwin si tragi concluzii despre toate *BSD. Contraexemplu: OpenBSD, unde functia setbuffer(3) nu stie sa faca asta. A doua obiectie e legata de continut: faptul ca poti sa-i spui functiei setbuffer(3) sa aleaga buffer-ul optim nu inseamna ca buffer-ul _default_ al fisierelor stdio(3) poate fi mai mic decat BUFSIZ. Cu alte cuvinte nu contrazici cu nimic ce am spus eu. > http://www.mit.edu/afs/athena/user/d/a/daveg/SIPB/ATHENA/NetBSD/Current/src/lib/libc/stdio/makebuf.c > http://darwinsource.opendarwin.org/10.0/Libc-166/stdio.subproj/makebuf.c > > if (st.st_blksize <= 0) { > *bufsize = BUFSIZ; > return (__SNPT); > } > ... > *bufsize = st.st_blksize; Nici asta nu am negat ca se intampla. Atat timp cat st_blksize >= BUFSIZ, asta este o optimizare. Problema apare doar daca st_blksize < BUFSIZ. Da-mi un exemplu de filesystem cu st_blksize < 1 KB sub un *BSD, si mi-ai demonstrat ca problema nu e specifica Linux. > 3) ca sa-ti citez din Stevens ("Advanced Programming in the UNIX > Environment", 2nd Edition, 2005): > > 5.4 Buffering > ... > Some C library implementations use the value from st_blksize > member of the stat structure to determine the optimal I/O buffer > size. As we will see later in this chapter, the GNU C library > uses this method. > ... Inca o data, asta nu e o problema daca st_blksize >= BUFSIZ. > 5.12 Implementation Details > ... > When we redirect these 2 streams to regular files, they become > fully buffered , with buffer sizes equal to the preferred I/O > size - the st_blksize value from the stat structure - for the > file system. > > (Mi se pare mie sau nici Stevens nu vede un bug aici? ;) Nici eu nu il vad, daca st_blksize >= BUFSIZ. > Parerea mea e ca amesteci mentiunea ce se aplica doar la setbuf() (size > == BUFSIZ & "With setbuf(), allocating a buffer of BUFSIZ bytes does not > necessarily imply that all of BUFSIZ bytes are used for the buffer > area." - > http://www.opengroup.org/onlinepubs/009695399/functions/setbuf.html) cu > politica de alocare initiala/automata a bufferului atunci cand size nu e > specificat. Nu. Cel care ai adus in discutie buffer-ul alocat de setbuf(3) esti tu. Eu m-am referit de la inceput la alocarea default. > Aceasta din urma (sau invocarea setvbuf() cu size == 0) nu pare sa > fie explicita in standard si lasa libertatea de a alege o dimensiune > optima diferita si eventual mai mica decat BUFSIZ). Aici te contrazic, desi deocamdata doar din amintiri. > > Nicaieri nu se spune insa ca libc poate schimba pe la spate > > buffer-ul cu altul mai mic. > > Irelevant, o chestie nespecificata in standard ramane la latitudinea > implementarii. Hai sa fim seriosi. Nu scrie nici ca libc nu are voie sa schimbe valoarea constantelor, sau ca nu are voie sa formateze discul fara sa-ti spuna, in timp ce calculeaza zecimalele lui PI. Cred insa ca putini oameni ar considera asa ceva "la latitudinea implementarii". Dar, adevarat, m-am exprimat gresit si incomplet. Ma refeream la lungimea buffer-ului alocat default. > Intrebarea e daca spune undeva ca NU POATE SA-L SCHIMBE. Dupa ce l-a alocat? Buna intrebare. Iarasi din amintiri, se spune explicit ca nu ai voie sa-l schimbi tu, decat fie inainte de a scrie in fisier, fie dupa fflush(3). Daca poate insa sa-l schimbe libc fara stirea ta, nu stiu. [...] > > > => cand stdout == pipe bufferul alocat pentru stream va fi de 4096 > > > bytes => datele pleaca in chunk-uri de 4KB din glibc/stdio + (se > > > pare ca) tcpdump nu face flush pe streamuri la SIGINT => datele > > > apar pe stdout in multiplii de 4KB iar ultimul buffer se pierde. > > > > Asta e un alt mod de a privi ce spuneam si eu. Totusi teoria > > mea (implicand st_blksize) explica si ce se intampla sub OpenBSD > > (care se conformeaza POSIX), pe cand a ta nu. > > Ba explica foarte bine: e doar o coincidenta faptul ca in OpenBSD > BUFSIZ e 1024 (mai mic decat oricare st_blksize), nu e o chestiune de > conformitate. Nu explica faptul ca daca faci explicit buffer-ul mai mare decat st_blksize, lungimea chunk-urilor va deveni egala cu cea a noului buffer, nu va ramane st_blksize. Vezi experimentul de mai jos. > For the record, "teoria" mea: > > Atat glibc cat si libc-urile *BSD folosesc st_blksize pentru > dimesionarea bufferelor stdio atunci cand opereaza in mod fully > buffered si nu se specifica explicit o dimensiune folosind > setvbuf() (sau implicit - BUFSIZ - folosind setbuf). BUFSIZ nu > este luat in considerare decat atunci cand nu se poate determina > st_blksize. Nu am negat asta. > Teoria ta (ultima dintre ele) era ca segmentarea se produce mai jos > de glibc/stdio (adica in kernel, pentru ca intre stdio si apelurile > sistem read()/write() nu mai intervine nici un buffer). Unde am spus eu asta? > Asta e usor de invalidat folosind strace care arata clar ca datele > sunt segmentate _inainte_ de apelul sistem write(), adica in > glibc/stdio: > > [EMAIL PROTECTED] ~]# strace tcpdump -i wlan0 2>&1 >/tmp/tst | grep "write(1" > write(1, "14:00:50.224841 IP 192.168.77.20"..., 4096) = 4096 > write(1, "amp 10145173 2190099611>\n14:00:5"..., 4096) = 4096 > write(1, "7.202.33219: P 2474:2565(91) ack"..., 4096) = 4096 > write(1, "estamp 10147075 2190101472>\n14:0"..., 4096) = 4096 > write(1, "14:00:20.833800 IP 192.168.77.202"..., 4096) = 4096 > write(1, "maps: . ack 6743 win 16022 <nop,"..., 4096) = 4096 > write(1, " win 16022 <nop,nop,timestamp 10"..., 4096) = 4096 Si ce arata asta din ce nu stiam pana acum? > A mea "teorie" inceteaza a mai fi o simpla teorie in momentul in > care vezi sursele :) mai devreme in thread ti-am aratat codul din > glibc care face exact ce spun eu (alege st_blksize pentru dimensiunea > bufferelor stdio). Acum iti dau si codul din OpenBSD care se comporta > similar, fara a incerca sa impuna un minim BUFSIZ: Omule, intelege te rog ca eu nu am negat ca buffer-ul pus de glibc este st_blksize. Chiar eu am afirmat asta in discutia initiala. Teoria mea e ca asta e un bug in glibc, pentru ca exista cazuri in care st_blksize < BUFSIZ. Iar despre asta nu imi poti demonstra ca nu e asa cu citate din codul glibc, ci doar cu citate din standarde. > http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdio/makebuf.c?rev=1.7&content-type=text/x-cvsweb-markup > > if ((fp->_flags & __SSTR) == 0) { > *bufsize = st.st_blksize; > fp->_blksize = st.st_blksize; > } > > Ciudatenia (fata de alte BSDuri) e ca foloseste st_blksize conditionat, > doar cand __SSTR nu e setat > (http://idsa.irisa.fr/cgi-bin/kame/http/source/freebsd4/include/stdio.h#L144 ) > dar implementarea e defectuasa: cand/daca flagul __SSTR _este_ > activat, bufsize ramane neinitializat iar pe urma e folosit pentru > malloc() si altele. Urmareste codul __smakebuf() & __swhatbuf() te rog > si confirma-mi. Sau nu e posibil ca __SSTR sa fie activ (atunci if-ul > nu-si mai are rostul si codul devine identic cu al celorlalte BSDuri), > sau bufsize ramane neinitializat - bug in OpenBSD :) $ grep __SSTR /usr/include/stdio.h #define __SSTR 0x0200 /* this is an sprintf/snprintf string */ > > Sub OpenBSD chunk-urile in care se scrie pe disc (in sfarsit, cele > > care pleaca din stdio(3), cum spui tu) au ca dimensiune maximum > > dintre st_blksize si dimensiunea buffer-ului definit de setvbuf(3). > > Din nou: > http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdio/makebuf.c?rev=1.7&content-type=text/x-cvsweb-markup > > Arata-mi si mie in cod unde se impune minimul ala BUFSIZ. Arata-mi in paragraful pe care l-ai citat unde e vorba de BUFSIZ. Pot insa sa-ti arat ca afirmatia de acolo e adevarata, facand buffer-ul mai mare decat st_blksize (ce se intampla cand e mai mic am vazut in episoadele precedente). Dimensiunea st_blksize: $ ./s >c 16384 ("s" e programul dintr-unul din post-urile precedente). Apoi: $ cat a.c #include <stdio.h> #include <unistd.h> main () { char buf[65536]; int i; setvbuf (stdout, buf, _IOFBF, 65536); while (1) { for (i = 0; i < 8192; i++) printf ("x"); sleep (1); } } $ cat b #! /bin/sh let i=0 while true; do echo -ne "$i\t" ls -l c let i=$i+1 sleep 1 done $ ./a >c $ ./b 0 -rw-r--r-- 1 daia daia 0 Dec 28 01:32 c 1 -rw-r--r-- 1 daia daia 0 Dec 28 01:32 c 2 -rw-r--r-- 1 daia daia 0 Dec 28 01:32 c 3 -rw-r--r-- 1 daia daia 0 Dec 28 01:32 c 4 -rw-r--r-- 1 daia daia 0 Dec 28 01:32 c 5 -rw-r--r-- 1 daia daia 0 Dec 28 01:32 c 6 -rw-r--r-- 1 daia daia 0 Dec 28 01:32 c 7 -rw-r--r-- 1 daia daia 0 Dec 28 01:32 c 8 -rw-r--r-- 1 daia daia 65536 Dec 28 01:33 c 9 -rw-r--r-- 1 daia daia 65536 Dec 28 01:33 c 10 -rw-r--r-- 1 daia daia 65536 Dec 28 01:33 c > E clar ca dimensiunea bufferului se alege _exact_ st_blksize bytes > daca aceasta valoare se poate determina. Faptul ca st_blksize e mai > mare ca BUFSIZ (1024 in OpenBSD) e doar o coincidenta, daca gasesti un > filesystem cu st_blksize == 512 atunci si in OpenBSD o sa ai buffer > stdio de 512 bytes < BUFSIZ => ai acelasi "bug" ca si in Linux. Si, poti gasi asa ceva? :-) Salutari, Liviu Daia -- Dr. Liviu Daia http://www.imar.ro/~daia _______________________________________________ RLUG mailing list RLUG@lists.lug.ro http://lists.lug.ro/mailman/listinfo/rlug