Felicitari pentru articol.
Scrierea HTTP serverului in C cred ca a fost educativa chiar daca este in stadiu incipient.
Sa fiu sincer am mai făcut unul cu pthreads la facultate, era ceva mai compet, suporta și post și query params la get, însă a trecut mult timp de atunci… Este foarte educativ. O firma mare din București îți dădea la interviu exercițiul asta (de fapt era tema pentru acasă).
Recent am vrut să experimentez cu fork() mai ales că am văzut că e la modă (why does redis use fork to create child process other than create a thread to rewrite aof or rdb · Issue #6722 · redis/redis · GitHub) , și am înțeles că cel puțin pe Linux fork()-urile sunt destul de cheap de ceva timp.
Un tip pe Reddit i-a făcut niște teste și rezista eroic ceea ce mă bucura :). (Sursa: https://www.reddit.com/r/C_Programming/comments/u6eeno/a_blog_that_is_a_single_executable_binary/i587rd0?utm_medium=android_app&utm_source=share&context=3 )
Mulțumesc de apreciere.
Tehnic, daca ai doar de comunicat in retea, fork()
e cam cea mai proasta solutie urmata de 1 thread/conexiune.
Cel mai recent API pentru asta este io_uring - Wikipedia sub Linux. Sub Windows e I/O Completion Ports - Win32 apps | Microsoft Docs (de ceva timp), dar pentru HTTP, Windows iti da si server HTTP in kernel.
Nu știam de api-urile astea, par interesante, am sa ma documentez.
Legat de fork(), in cazul de față nu e asa de tragic. Din cate am citit folosește copy on write, si cum nu prea sunt write-uri, vad ca un fork() se creează in cateva milisecunde pe masina mea. Memoria nu creste, pentru ca array-ul care are content nu se modifică. pthreads clar ofera un control mai bun. Dar nu sunt asa la curent cu POSIX world si poate greșesc.
Problema e dupa. Ai 1000 de conexiuni de pe care astepti sa citesti date. Avand multe procese/threaduri trebuie schedulerul sa tot treaca prin ele sa vada ce mai fac, doar ca ele sa blocheze asteptand noi date.
Ideal e, daca tot vin datele asincron pe retea, cand a venit ceva sa ajunga unde e procesat. Numarul de threaduri sa fie > 1 doar daca sunt mai multe CPUs ce pot lucra in paralel.
In C++ Boost.Asio - 1.79.0 abstractizeaza usor peste APIul fiecarui sistem de operare si ofera o interfata uniforma. Cu coroutines din C++20 devine si mai interesant.
In C e GitHub - libuv/libuv: Cross-platform asynchronous I/O
Mersi de sfaturi. Studiez!
Daca deja e C++, lucrurile devin mult mai simple.
Daca folosesti std::thread deja codul devine mai portabil. Poti sa implementezi relativ usor un thread pool, ca sa scapi de overheadul repetat cand e creat un thread, dar de exemplu cu VC++ ai pe ‘gratis’ thread pooling daca folosesti std::async. Am facut ceva timing la chestiile astea si diferenta poate fi destul de mare.
Problema nu e mare daca ai cateva threaduri care ruleaza non stop, problema apare cand sunt implicate multe care ruleaza un timp relativ scurt.
Accentul cade pe “ruleaza”. Daca threadul doar sta sa astepte date dintr-o singura sursa, e overhead.
Acum 15-20 de ani, serverele IRC duceau mii de conexiuni long-running concomitente pe hardware al vremii (vreun P3 <1 GHz). S-ar fi descurcat daca aveau 1 fork/thread per conexiune?
Adica exact ce face Node.js. Nu?
Da. Node.js foloseste libuv mentionat mai sus: