<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=Content-Type content="text/html; charset=iso-8859-2">
<META content="MSHTML 6.00.6000.16397" name=GENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=#ffffff>
<DIV><FONT face=Arial
size=2> Scriu acest mail pentru
că m-am apucat să corectez temele 1 (și sper să le și termin la timp :-D, ținând
cont că sunt și eu în sesiune) și am găsit unele greșeli extrem de frecvent. Să
nu credeți că dacă tema vi s-a părut ușoară și ați luat note mari este ok. În
multe cazuri s-au luat note mari pentru că testele se pare că erau prea ușoare
și pentru că m-am ținut după barem. În multe cazuri notele nu reflectă calitatea
temelor. Mailul ăsta ar trebui să vă facă să vă simțiți puțin stânjeniți dacă
sunteți cu musca pe căciulă și, în cel mai bun caz, să aflați ce puteți face mai
bine în viitor (de exemplu când veți fi anganjați unii dintre voi). Dacă vreți
doar nota la această materie puteți foarte bine să nu citiți ce urmează. Cei
care nu sunt vizați de acest mail o să se recunoască singuri.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> De departe
cea mai frecventă greșeală este buffer overflow. Vă mărturisesc sincer că până
acuma nu înțelegeam cum de poate fi o problemă reală, dar când am văzut cât de
mulți sunt comozi când scriu cod și nu scriu 2-3 linii în plus doar ca să
termine mai repede, m-am lămurit cum stă treaba și de ce așa de multe programe
cunoscute au bug-uri. Dacă nu știți ce înseamnă buffer overflow și de ce e rău
(mai ales când este exploatabil, dar nu numai), puteți căuta pe net (există și o
carte destul de bună, "Writing Secure Code" cu ceva detalii și exemple). Oricum,
chiar dacă nu știați, folosirea funcțiilor strcpy și strcat tot greșită e
(pentru că ele nu verifică depășirile (nu au cum), trebuie să verificați voi
pentru ele). Vă rog să nu mai folosiți buffer-e (liste de parametri, etc.) de
dimensiune fixă unde nu e cazul și să nu mai folosiți funcțiile din string.h
până nu aflați ce e aia un buffer overflow și cum se scrie codul corect. Dacă o
să ajung să folosesc software scris de voi, vă mai rog încă o dată.
:-P</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> Pe locul doi
sunt memory leak-urile. malloc fără free înseamnă că risipiți resursele
sistemului inutil, și, în cel mai rău caz, expuneți aplicația la DoS. Unii au
evitat leak-uri fără să își dea seama, pentru că au făcut malloc chiar înainte
de exec (pun pariu că mulți nu ar fi dat free, chiar dacă lipsea exec-ul de
acolo). Mulți au uitat că au malloc în funcțiile care expandează variabile de
mediu și nu au mai dat free decât în unele cazuri. Dacă vi se pare prea greu
managementul memoriei alocate dinamic (nu era cazul în 90% din temele 1 pe care
le-am văzut), treceți pe C++ și nu folosiți pointeri, ci doar copieri de
obiecte, sau folosiți liste de obiecte alocate dinamic. Se găsesc soluții. Cel
mai ușor e să citiți odată codul și la fiecare malloc să vă întrebați unde dați
free. Apoi să verificați cu unelte automate că ați avut dreptate.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> O altă
greșeală, la care nu mă așteptam deloc, și care poate e și din vina noastră că
nu v-am explicat la laborator, sau a voastră că nu ați citit man și nu ați fost
la curs :-P, este că ați specificat foarte mulți drepturile de acces la funcția
open, chiar dacă lipsea O_CREAT. Dacă vă gândiți puțin și ați înțeles ce e aia
drept de acces la fișiere vă dați seama care e problema. Desigur, asta poate fi
din cauză că ați dat copy/paste la cod (am văzut și O_CREAT la input:
discutabil), ceea ce ne duce la altă problemă pe care am observat-o: unii dintre
voi au incredibil de mult cod duplicat. E preferabil să faceți funcții în acest
caz. Cred ca unele teme s-ar fi redus la 75% cu 1-2 funcții definite în
plus.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> Altă problemă
este că mulți nu au înțeles ce face dup2. dup2 închide vechiul file descriptor,
deci dacă acolo era deschis stdin, de exemplu, un close ulterior nu o să-l
deschidă la loc. Trebuie fie să-l salvați în alt file descriptor, fie să faceți
dup2 după fork și înainte de exec. Multe implementări ar fi eșuat pe un test
care făcea o redirectare într-un fișier și pe urmă rula încă o comandă fără acea
redirectare. Vă rog să citiți manualul funcțiilor pe care le folosiți cu
atenție.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> Ceea ce
ne duce la următoarea problemă: mulți nu au citit cu atenție comentariile din
parser.h, și fie nu au folosit câmpul expand (și au expandat întotdeauna
variabilele de mediu), fie au neglijat că și câmpul verb putea fi alcătuit din
mai multe părți. Doar dacă în teste nu apare așa ceva nu înseamnă că nu poate
ieși din parser așa ceva (culmea e că se poate să iasă și chiar e de dorit :-P).
Oameni buni: orice chestiuță (cât de mică o fi ea) pe lumea asta, dacă vine
cu documentație, asta înseamnă ceva. De obicei înseamnă că e bine să citiți cu
atenție documentația (complet). Chiar și la un card de memorie, un ceas, un set
de baterii, etc., darămite la o funcție despre care nu știi ce face decât dacă
citești documentația (repet: complet și cu atenție).</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> Mai e un
set de probleme care țin în particular de tema 1 sau sunt mai
"exotice".</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> Cea mai mare
problemă ar fi faptul că mulți care au rezolvat tema pe linux au folosit
"orbește" fork recursiv, doar pentru că așa era parcurs arborele în exemplele
parser-ului, fără să pună în balanță avantajele și dezavantajele. Cel mai mare
dezavantaj este că se face mai greu comunicarea cu noua instanța a shell-ului.
Asta duce la probleme: exit care nu face shell-ul să iasă, chdir care nu are
efect, etc. cu soluții incomode (pipe-uri de comunicare între procese) sau
greșite (exit code particular pentru a comunica cu părintele, exit code ce ar
putea fi la fel de bine generat de un program). Nu spun că nu se poate face ceva
corect așa, doar ca prea mulți au făcut fără să se gândească la alternative și
mulți dintre ei au multe scăpări la ce a rezultat.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> La fel cu
"copierea" modelului de pe Linux pe Windows. Doar pentru că în linux *trebuie*
să faci redirectările suprascriind in/out standard și făcând apoi exec, nu
înseamnă că așa e cel mai bine pe Windows. Există un parametru special la
CreateProcess pentru aceste redirectări. Asta evită apeluri inutile de sistem și
posibile erori de implementare. Care erori există. De exemplu (și asta se aplică
și pe Linux) foarte puțini au luat în considerare un aspect ceva mai subtil de
securitate: s-ar putea ca noul proces creat să moștenească handle-uri de care nu
are nevoie și la care nu ar trebui să aibă acces, doar pentru că un proces de
dinainte trebuia să moștenească acel handle care a rămas deschis (valabil
și pe Linux pentru file descriptori). Asta e valabil și pentru implementările
care au folosit thread-uri și acesta este motivul prinicipal pentru care eu nu
aș recomanda thread-uri pentru această temă (pe Windows, că pe Linux mai sunt
câteva, cum ar fi interacțiunea foaaaaarte ambiguă/slab documentată între
fork și lpthread).</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> Alte greșeli
au variat de la a încerca să faci tema să meargă doar pentru teste (nu pentru
cazul general), până la implementarea incorectă a rulării în paralel (doar ca să
iasă tema), wait doar după unii dintre copii (pentru că altfel era greu de făcut
paralelismul), înțelegerea greșită a versiunilor funcțiilor ANSI/Unicode pe
Windows, etc.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> În cazul în
care doriți să programați pe bani și/sau țineți la calitatea lucrurilor pe care
le faceți vă rog să citiți cu atenție ce am scris și să evitați în viitor
greșelile pe care le-ați făcut.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2> Deoarece
voi presupune că marea majoritate citiți acest mail, nu o să mai fiu la fel de
explicit în comentariile pe care le fac la teme de acum înainte (pentru cazurile
frecvente pe care le-am spus mai sus). De exemplu, o să scriu doar "-0.1 leak",
și o să dau explicații doar acolo unde e un leak mai subtil. Eu nu o să fiu în
stare să vă găsesc toate bug-urile, de obicei citesc temele doar o dată, așa că
dacă nu am găsit o problemă într-o temă nu înseamnă că nu există.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2>
Maximilian Machedon</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV>
<DIV><FONT face=Arial size=2>PS: Știu că este "doar o temă", dar vă asigur că
există o diferență între a scrie cod bun și a scrie cod care să meargă. Adică
dacă ați scris codul doar că să meargă asta nu v-a ajutat cu nimic pentru a
învăța să scrieți cod bun.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT> </DIV></BODY></HTML>