Z a aw a n so w a n e o p e ra cj e w e jś ci a / w y jś ci a
WitoldPaluszyński witold.paluszynski@pwr.edu.pl http://www.kcir.pwr.edu.pl/∼ witold/ Copyrightc 2002–2003WitoldPaluszyński Allrightsreserved. Niniejszydokumentzawieramateriałydowykładunatematzaawansowanychoperacji wejścia/wyjściawsystemieUnix.Jestonudostępnionypodwarunkiemwykorzystania wyłączniedowłasnych,prywatnychpotrzebimożebyćkopiowanywyłączniewcałości, razemzniniejsząstronątytułową.B lo k o w a n ie o p e ra cj i I/ O
Blokowanieoperacjiwejścia/wyjściaoznaczaoczekiwanienamożliwośćodczytania albozapisaniadanych.Przykładyblokowaniawejścia/wyjścia: •odczytzplikówurządzeń,gdyniemawnichjużdanych(potoki,terminale, gniazdka), •zapisdotychsamychurządzeń,jeślidaneniemogąbyćodrazuprzyjęte, •otwarcieplikublokujedomomentuspełnieniapewnychwarunków(np.otwarcie plikuterminaladochwilinawiązaniapołączeniaprzezmodem,otwarcieFIFOdo zapisugdyżadenprocesniemagootwartegodoodczytu,itp.), •operacjenaplikachzmandatoryrecordlocking, •iinne. Unix:zaawansowaneoperacjeI/O3N ie b lo k u ją ce w e jś ci e / w yj śc ie
BlokowanieoperacjiI/Omożnakontrolować,toznaczymożnatakskonfigurować operacjewejścia/wyjścia,bywsytuacjiktóranormalniespowodowałabyblokowanie, funkcjawróciłaodrazuzkodemwskazującymniemożnośćwykonaniaoperacji. •openzflagąONONBLOCK •fcntlnaotwartymdeskryptorzeztąsamąflagą Wtedypowrótzfunkcjiread/writenastępujezbłędemwskazującym,żeoperacjaby blokowała. UWAGA:możnazastosowaćstarąflagęONDELAY,wtedyfunkcjeread/write zwracają0,cojednakpokrywasięzsygnalizacjąEOF(SystemV). Unix:zaawansowaneoperacjeI/O4R ó w n o cz e sn e o p e ra cj e I/ O
Jakzorganizowaćoperacjewejścia/wyjścia,gdymamynp.jednocześniekilkaźródeł, zktórychnadchodządane? Naprzykład,emulatorterminala,komunikującysięzednejstronyzużytkownikiem, azdrugiejzezdalnymsystemem: Możliwesąnastępującerozwiązania: •polling:nieblokującepróbyodczytunaprzemianzkolejnychdeskryptorów; wniektórychsystemachmożnateżużyćnagniazdkach: ioctl(fd,FIONREAD,&nread); •przełączanie(multiplexing):funkcjeselectipoll •asynchroniczneI/O:powiadamianieprocesuprzezjądro(przypomocysygnału) ogotowoścideskryptoradooperacjiI/O Unix:zaawansowaneoperacjeI/O5 Unix:zaawansowaneoperacjeI/O6I/ O m u lt ip le xi n g
TenmechanizmniejestobjętynormąPOSIX,jednakfunkcjaselectistnieje wUnixachAT&TiBSDoddawnaijestdośćstandardowa.Funkcjapolljestnowa, iwniektórychsystemachjestzaimplementowanaprzezselect. structtimeval{ time_ttv_sec;/*seconds*/ suseconds_ttv_usec;/*andmicroseconds*/ }; intselect(intnfds,fd_set*readfds,fd_set*writefds, fd_set*errorfds,structtimeval*timeout); FunkcjaselectzwracaliczbędeskryptorówgotowychdooperacjiI/O.Popowrocie wynikającymzupłynięciazadanegoczasufunkcjazerujewszystkiezbiorydeskryptorów wsystemieV,leczpozostawiajeniezmienionewsystemieBSD.Tesystemyrównież inaczejlicządeskryptorygotowedoI/O(AT&Tliczyjewsensiemnogościowym, aBSDsumujearytmetycznielicznościwszystkichtrzechzbiorówdeskryptorów). Ponadto,niektóresystemywprzypadkugotowoścideskryptoraustawiająwstrukturze timevalczaspozostałydowyczerpania(Linux). Unix:zaawansowaneoperacjeI/O7 Dotworzeniaodpowiednichzbiorówdeskryptorów,atakżesprawdzaniaotrzymanych wyników,istniejąmakra: voidFD_SET(intfd,fd_set*fdset); voidFD_CLR(intfd,fd_set*fdset); intFD_ISSET(intfd,fd_set*fdset); voidFD_ZERO(fd_set*fdset); Funkcjapollzapewniapodobnedziałanieleczinnyinterfaceprogramisty: structpollfd{ intfd;/*filedescriptor*/ shortevents;/*requestedevents*/ shortrevents;/*returnedevents*/ } intpoll(structpollfdfds[],nfds_tnfds,inttimeout); Tablicastrukturpollfdokreślawszystkiedeskryptoryizdarzenia,naktórechcemy oczekiwać(czasokreślanyjesttuwmilisekundach).Wartośćzwracanazfunkcjidaje liczbęgotowychdeskryptorów. Unix:zaawansowaneoperacjeI/O8F u n k cj a s e l e c t — p ro st y p rz yk ła d
Poniżejminimalnyitrywialnyprzykładużyciafunkcjiselect.Wtymprzypadku obsługiwanyjesttylkojedendeskryptor,więctutajużycieselectniejestkonieczne. #include<sys/types.h> #include<sys/time.h> #include<stdio.h> #include<fcntl.h> #include<sys/ioctl.h> #include<unistd.h> intmain(){ charbuffer[128]; intresult,nread; fd_setinputs,testfds; structtimevaltimeout; FD_ZERO(&inputs); FD_SET(0,&inputs); while(1){ testfds=inputs; timeout.tv_sec=2; timeout.tv_usec=500000; result=select(FD_SETSIZE,&testfds, (fd_set*)0, (fd_set*)0, &timeout);switch(result){ case0: printf("timeout\n"); break; case-1: perror("select"); exit(1); default: if(FD_ISSET(0,&testfds)){ ioctl(0,FIONREAD,&nread); if(nread==0){ printf("keyboarddone\n"); exit(0); } nread=read(0,buffer,nread); buffer[nread]=0; printf("read%dfromkeyboard:%s", nread,buffer); } break; }//switch }//while }//main Unix:zaawansowaneoperacjeI/O9
F u n k cj a s e l e c t — b ar d zi e j re a li st yc zn y p rz yk ła d
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/time.h> #include<sys/ioctl.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> intmain(){ intserv_len,serv_sock,cli_sock,result; structsockaddr_inserv_addr; fd_setreadfds,testfds; serv_addr.sin_family=AF_INET; serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); serv_addr.sin_port=htons(9734); serv_len=sizeof(serv_addr); serv_sock=socket(AF_INET,SOCK_STREAM,0); bind(serv_sock,(structsockaddr*)&serv_addr,serv_len); listen(serv_sock,5); Unix:zaawansowaneoperacjeI/O10FD_ZERO(&readfds); FD_SET(serv_sock,&readfds); while(1){ charch; intfd,nread; testfds=readfds; printf("serverwaiting\n"); result=select(FD_SETSIZE,&testfds,(fd_set*)0, (fd_set*)0,(structtimeval*)0); if(result<1){ perror("select"); exit(1); } for(fd=0;fd<FD_SETSIZE;fd++){ if(FD_ISSET(fd,&testfds)){ if(fd==serv_sock){ printf("addingclientonfd%d\n",cli_sock); cli_sock=accept(serv_sock,0,0); FD_SET(cli_sock,&readfds); } Unix:zaawansowaneoperacjeI/O11 else{ nread=read(fd,buf,sizeof(buf)); if(nread==0){/*nodata,clientclosedconnection*/ printf("removingclientonfd%d\n",fd); close(fd); FD_CLR(fd,&readfds); } else{ buf[nread]=0;/*terminatestring*/ printf("clientonfd=%d,msg=%s\n",fd,buf); sprintf(buf,"serverreplytoclient%d\n",fd); write(fd,buf,strlen(buf)+1); }//else... }//else... }//if(FD_ISSET(fd... }//for(fd=0... }//while(1)... }//main... Zwróćmyuwagęnato,żeużyciefunkcjiselectwpowyższymprzykładzierozwiązuje problemjednoczesnejobsługiwielupołączeńnaistniejącychgniazdkachklientów,oraz przyjmowanianowychpołączeńnagniazdkuserwera.Wprzypadkuużyciakomunikacji bezpołączeniowejwykorzystywanebyłobytylkogniazdkoserwera,ifunkcjaselectnie byłabypotrzebna. Unix:zaawansowaneoperacjeI/O12