/* bench_connector.c — proc connector (netlink) y taskstats contra sondear /proc. * * Cierra el experimento 1 de SDD 25 §9, que quedo sin medir por falta de CAP_NET_ADMIN. * Requiere root. Tres fases: * A. LATENCIA fork+_exit+waitpid vs fork+_exit+esperar el evento del connector. * B. CEGUERA procesos cortos vistos por el connector vs vistos sondeando /proc cada 100 ms. * C. TASKSTATS una consulta binaria por genetlink vs parsear /proc//stat. * * Se compila con: cc -O2 -o bench_connector bench_connector.c */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static volatile long sink; static inline double now_ns(void){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec*1e9+t.tv_nsec; } static int cmpd(const void*a,const void*b){ double x=*(const double*)a,y=*(const double*)b; return (x>y)-(xdata; } /* Vacia lo que haya en el socket sin bloquear. */ static void cn_drain(int s){ int fl=fcntl(s,F_GETFL,0); fcntl(s,F_SETFL,fl|O_NONBLOCK); char b[4096]; while(recv(s,b,sizeof b,0)>0){} fcntl(s,F_SETFL,fl); } static void fase_a(int s){ printf("\n### A. Latencia de notificacion: waitpid contra evento del connector\n"); printf("%-44s %10s %10s\n","BENCH (ns por proceso)","min","mediana"); const long N=200; double r[ROUNDS]; for(int q=0;qlim){ perdidos++; break; } struct pollfd pf={.fd=s,.events=POLLIN}; if(poll(&pf,1,50)<=0) continue; struct proc_event *e=cn_read(s,buf,sizeof buf); if(!e) continue; if(e->what==PROC_EVENT_EXIT && e->event_data.exit.process_pid==p) break; } int st; waitpid(p,&st,0); } r[q]=(now_ns()-t0)/(double)N; } report("fork+_exit+esperar EXIT del connector",r,ROUNDS,N); printf("eventos EXIT que no llegaron en 200 ms: %ld de %ld\n",perdidos,(long)N*ROUNDS); } /* ---------------------------------------------------------------- ceguera del sondeo */ #define BURST 200 /* procesos cortos */ #define SPACING_US 10000 /* uno cada 10 ms => 2 s de rafaga */ #define POLL_MS 100 /* periodo del sondeo, generoso: un supervisor real no baja de aca */ /* Lanza BURST hijos que viven lo minimo, y escribe sus pids por el pipe. */ static void spawner(int wfd){ for(int i=0;id_name[0]<'0'||e->d_name[0]>'9') continue; pid_t p=(pid_t)atol(e->d_name); if(nvwhat==PROC_EVENT_EXIT){ pid_t p=e->event_data.exit.process_pid; if(nv su CPU es cota superior. Lo que cuenta: COBERTURA.\n"); } /* ---------------------------------------------------------------- taskstats */ static int genl_family(int s,const char *name){ struct { struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } req; memset(&req,0,sizeof req); req.n.nlmsg_type=GENL_ID_CTRL; req.n.nlmsg_flags=NLM_F_REQUEST; req.n.nlmsg_seq=1; req.n.nlmsg_pid=getpid(); req.g.cmd=CTRL_CMD_GETFAMILY; req.g.version=1; struct nlattr *a=(struct nlattr*)req.buf; a->nla_type=CTRL_ATTR_FAMILY_NAME; a->nla_len=NLA_HDRLEN+strlen(name)+1; strcpy((char*)a+NLA_HDRLEN,name); req.n.nlmsg_len=NLMSG_LENGTH(GENL_HDRLEN)+NLA_ALIGN(a->nla_len); if(send(s,&req,req.n.nlmsg_len,0)<0) return -1; char rb[4096]; ssize_t r=recv(s,rb,sizeof rb,0); if(r<0) return -1; struct nlmsghdr *nh=(struct nlmsghdr*)rb; if(nh->nlmsg_type==NLMSG_ERROR) return -1; struct nlattr *na=(struct nlattr*)((char*)NLMSG_DATA(nh)+GENL_HDRLEN); int len=NLMSG_PAYLOAD(nh,0)-GENL_HDRLEN; while(len>0){ if(na->nla_type==CTRL_ATTR_FAMILY_ID) return *(__u16*)((char*)na+NLA_HDRLEN); len-=NLA_ALIGN(na->nla_len); na=(struct nlattr*)((char*)na+NLA_ALIGN(na->nla_len)); } return -1; } static int ts_query(int s,int fam,pid_t pid,unsigned long long *cpu_ns){ struct { struct nlmsghdr n; struct genlmsghdr g; char buf[128]; } req; memset(&req,0,sizeof req); req.n.nlmsg_type=fam; req.n.nlmsg_flags=NLM_F_REQUEST; req.n.nlmsg_seq=2; req.n.nlmsg_pid=getpid(); req.g.cmd=TASKSTATS_CMD_GET; req.g.version=TASKSTATS_VERSION; struct nlattr *a=(struct nlattr*)req.buf; a->nla_type=TASKSTATS_CMD_ATTR_PID; a->nla_len=NLA_HDRLEN+sizeof(__u32); *(__u32*)((char*)a+NLA_HDRLEN)=pid; req.n.nlmsg_len=NLMSG_LENGTH(GENL_HDRLEN)+NLA_ALIGN(a->nla_len); if(send(s,&req,req.n.nlmsg_len,0)<0) return -1; char rb[8192]; ssize_t r=recv(s,rb,sizeof rb,0); if(r<0) return -1; struct nlmsghdr *nh=(struct nlmsghdr*)rb; if(nh->nlmsg_type==NLMSG_ERROR) return -1; struct nlattr *na=(struct nlattr*)((char*)NLMSG_DATA(nh)+GENL_HDRLEN); int len=NLMSG_PAYLOAD(nh,0)-GENL_HDRLEN; while(len>0){ if(na->nla_type==TASKSTATS_TYPE_AGGR_PID || na->nla_type==TASKSTATS_TYPE_AGGR_TGID){ struct nlattr *in=(struct nlattr*)((char*)na+NLA_HDRLEN); int il=na->nla_len-NLA_HDRLEN; while(il>0){ if(in->nla_type==TASKSTATS_TYPE_STATS){ struct taskstats *ts=(struct taskstats*)((char*)in+NLA_HDRLEN); *cpu_ns=ts->ac_utime*1000ULL+ts->ac_stime*1000ULL; return 0; } il-=NLA_ALIGN(in->nla_len); in=(struct nlattr*)((char*)in+NLA_ALIGN(in->nla_len)); } } len-=NLA_ALIGN(na->nla_len); na=(struct nlattr*)((char*)na+NLA_ALIGN(na->nla_len)); } return -1; } static void fase_c(void){ printf("\n### C. Estadisticas de UNA tarea: taskstats binario contra /proc de texto\n"); int s=socket(PF_NETLINK,SOCK_DGRAM|SOCK_CLOEXEC,NETLINK_GENERIC); if(s<0){ printf("taskstats: sin socket genetlink (%s)\n",strerror(errno)); return; } struct sockaddr_nl a; memset(&a,0,sizeof a); a.nl_family=AF_NETLINK; a.nl_pid=getpid(); if(bind(s,(struct sockaddr*)&a,sizeof a)<0){ printf("taskstats: bind (%s)\n",strerror(errno)); close(s); return; } int fam=genl_family(s,TASKSTATS_GENL_NAME); if(fam<0){ printf("taskstats: la familia %s no esta (kernel sin TASKSTATS)\n",TASKSTATS_GENL_NAME); close(s); return; } unsigned long long ns=0; if(ts_query(s,fam,getpid(),&ns)<0){ printf("taskstats: la consulta fallo (%s)\n",strerror(errno)); close(s); return; } printf("familia TASKSTATS = %d, cpu de este proceso = %llu ns\n",fam,ns); printf("%-44s %10s %10s\n","BENCH (ns por consulta)","min","mediana"); const long N=2000; double r[ROUNDS]; for(int q=0;q0){ b[n]=0; char *p=strrchr(b,')'); for(int f=0;p&&f<12;f++) p=strchr(p+1,' '); sink=p?atol(p+1):0; } } r[q]=(now_ns()-t0)/(double)N; } report("open+read+parse /proc/self/stat",r,ROUNDS,N); close(s); } int main(void){ if(geteuid()!=0) fprintf(stderr,"aviso: sin root el connector no se suscribe (CAP_NET_ADMIN)\n"); printf("# bench_connector — SDD 25 seccion 9, experimento 1\n"); int s=cn_open(); if(s<0){ printf("proc connector: no disponible (%s)\n",strerror(errno)); } else { fase_a(s); fase_b(s); close(s); } fase_c(); return 0; }