Blog literacki, portal erotyczny - seks i humor nie z tej ziemi
Oto zadanie poświęcone funkcji systemowej fork.
Wydawać by sie mogło, że nic się nie da tutaj wymyślić, gdyż
fork dobrze wykonuje swoją robotę. Można jednak usprawnić tą funkcję
w przypadku, gdy proces potomny wywołuje
jedną z funkcji z rodziny exec. W takim przypadku nie musi być kopiowana
żadna ze struktur vm_area_struct opisujaca pamieć wirtualną procesu. Odpowiada
to wywołaniu funkcji do_fork z ustawioną flagą CLONE_VM. Tak więc, chciałoby
się mieć nową funkcję, powiedzmy fork2, która wywoływałaby w ten sposób
do_fork, a którą byśmy używali w naszych programach testowych (zmiana funkcji
sys_fork w jądrze na pewno by zawiesiła system). Są na to dwie metody.
Sposób 1.
Stworzyć własną funkcję w standardowej bibliotece lib.c, ktora by korzystała
z funkcji systemowej sys_clone (sys_clone(0, SIGCLD | CLONE_VM)) Co prawda
dawniej istniała stadardowa funkcja C clone wywołująca sys_clone, ale zniknęła
z powodu złego działania.
Sposób 2.
Zmodyfikować istniejacą w źródłach jądra funkcję execve. Np. jeśli
jej pierwszym parametrem jest NULL wówczas dziala jak nasz fork2. Odpowiednia
zmiana funkcji w pliku process.c wygląda tak:
asmlinkage int sys_execve(struct pt_regs regs) {
int error;
char * filename;
/********************* poczatek dodanego kodu *****************/
if (!regs.ebx) {
return do_fork(SIGCHLD | CLONE_VM, regs.esp, regs);
}
/********************* koniec dodanego kodu ******************/
error = getname((char *) regs.ebx, &filename);
if (error) return error;
error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx,
regs);
putname(filename);
return error;
}
Osobiście polecam raczej drugi sposób, choć jest mniej elegancki. Kompilacja
biblioteki C jest równie czasochłonna jak jądra oraz przy tworzeniu wlasnej
funkcji wywołującej funkcję systemową trzeba stworzyć kilka plików bazujących
w dużej mierze na niezrozumiałych na pierwszy rzut oka makrach. Próbowałem
to uzyskać, ale się zniechęciłem - nie miałem pewności, czy będzie to działać.
Pozostaje ponownie skompilować jądro, a następnie zrobić jakiś eksperyment.
Np. porównać czasy wykonania programów zawierających fork i exec, dla normalnego
forka i fork2 (czyli dla naszego execve(NULL,NULL,NULL)).
Oczekiwane wyniki eksperymentu:
Skopiowanie listy i drzewa AVL struktur vm_area_strukt, ktore nam udało
się wyeliminować z tak wywołanej funkcji fork2, znacznie przyspieszy samą
operację tworzenia nowego procesu. Wydaje się jednak, że operacją dominującą
jest wywołanie funkcji exec. Mimo wszystko w sytuacji gdy proces-rodzic
jest dosyć duży, a sam plik uruchamiany w funkcji exec niewielki, możemy
uzyskać nawet niezłą poprawę szybkości takiego programu.
P.S. Wydawać by sie mogło, że to, co opisuję odpowiada w pewnym stopniu
istniejącej w unixowych systemach funkcji vfork. Otóż, nic takiego nie
ma miejsca, jak można przekonać się ze źródeł biblioteki libc.a funkcja
vfork dokładnie pokrywa sie z fork.
Autor: Michał Smoktunowicz