Analitzant les crides a sistema
Les crides a sistema són la interfície entre els programes d'usuari i el nucli del sistema operatiu. Aquestes crides són les que permeten als programes d'usuari accedir a les funcionalitats del sistema operatiu. En aquest laboratori, analitzarem la complexitat de les crides a sistema i les compararem amb les crides a procediments.
Les preguntes que ens fem són:
- Quina és la diferència de temps entre una crida a sistema i una crida a procediment?
- Quina és la complexitat d'una crida a sistema?
- Per què una crida es més costosa que l'altre?
Objectius
- Comprendre el funcionament de les crides a sistema.
- Comparar el cost d'una crida a sistema amb el cost d'una crida a procediment.
- Dissenyar experiments en C.
Tasca
Per respondre a les preguntes plantejades, dissenyarem un experiment en C que ens permeti mesurar el temps que triga una crida a sistema i una crida a procediment. Aquest experiment consistirà en cridar una funció simple i una crida a sistema un nombre determinat de vegades, i mesurar el temps que triga aquestes crides. Per exemple, podem utiltizar una crida a sistema com getpid()
i una funció simple com funcio()
que retorna un valor constant i calcular el temps que triga aquestes crides. Com a tota experimentació, caldrà repetir l'experiment un nombre suficient de vegades per obtenir resultats significatius, per exemple, 1000000 vegades i calcular el temps mitjà de cada crida.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#define N_ITERATIONS 1000000
int funcio() {
return 20;
}
int main() {
struct timespec start, end;
double totalTimeSysCall, totalTimeFuncCall;
float avgTimeSysCall, avgTimeFuncCall;
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < N_ITERATIONS; i++) {
funcio();
}
clock_gettime(CLOCK_MONOTONIC, &end);
totalTimeFuncCall = (end.tv_nsec - start.tv_nsec);
avgTimeFuncCall = totalTimeFuncCall / N_ITERATIONS;
printf("Temps mitjà de la funció: %f ns\n", avgTimeFuncCall);
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < N_ITERATIONS; i++) {
getpid();
}
clock_gettime(CLOCK_MONOTONIC, &end);
totalTimeSysCall = (end.tv_nsec - start.tv_nsec);
avgTimeSysCall = totalTimeSysCall / N_ITERATIONS;
printf("Temps mitjà de la crida a sistema: %f ns\n", avgTimeSysCall);
return 0;
}
En aquest codi, s'utilitza la funció clock_gettime()
per mesurar el temps que triga una crida a sistema i una crida a procediment. Aquesta funció ens retorna una estructura timespec
que conté el temps en segons i nanosegons (tv_sec o tv_nsec). Per a més informació, podeu consultar el manual de linux man clock_gettime
o man timespec
.
Per tal d'obtenir el temps transcorregut entre dues crides, es calcula la diferència entre el temps final i el temps inicial. Per tant, definim dues variables start
i end
de tipus timespec
que contindran el temps abans i després de les crides. El temps total de les crides es calcula restant el temps final i el temps inicial. Finalment, es calcula el temps mitjà de les crides dividint el temps total pel nombre de crides realitzades.
Nota: En aquest exemple, he simplificat el càlcul del temps per a facilitar la comprensió i per que cada mesura no excedira el segon. Si es vol fer una mesura més universal, es recomana utilitzar la següent fórmula:
totalTime = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9
. Ja que cada segon, la variabletv_sec
incrementa en 1 i la variabletv_nsec
es reinicia a 0. Això permet mesurar temps superiors a 1 segon.
Nota: La variable
CLOCK_MONOTONIC
segons el manual de linux mesura el temps de forma monòtona, és a dir, no es veu afectada per salts discontinus en el temps del sistema.
Per executar aquest experiment, compilem el codi amb la següent comanda:
gcc experiment.c -o experiment -o0
Afegim l'opció -o0
per desactivar l'optimització del compilador i obtenir resultats més precisos. El compilador de C, sovint optimitza el codi i això pot afectar als resultats. Per tant, desactivem l'optimització per obtenir resultats més fiables.
Un cop compilat, executem el programa amb la següent comanda:
./experiment
Temps mitjà de la funció: 5.298092 ns
Temps mitjà de la crida a sistema: 142.388794 ns
Com a resultat de l'experiment, es mostrarà el temps mitjà de la crida a sistema és molt més gran que el temps mitjà de la crida a procediment. Això és degut a la complexitat de les crides a sistema, que involucren moltes més operacions que una crida a procediment. Recordeu que les crides a sistema impliquen accedir al mode kernel, canviar de context, executar la crida a sistema i tornar al mode d'usuari, mentre que una crida a procediment és simplement una crida a una funció.
Exercicis opcionals
- Optimitza l'experiment per evitar repeticions de codi.
- Optimitza la mesura del temps per obtenir resultats més generals aplicant la fórmula recomanada.
- Modifica aquest experiment per comparar altres funcions i crides a sistema.
- Crea un makefile per compilar el codi de forma més eficient.