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

Raspunde prin e-mail lui