Post on 12-Oct-2015
LaGuaBeejdeComunicacinentreprocesosUnix
Versin0.9.3(16denoviembrede2004)[http://www.ecst.csuchico.edu/~beej/guide/ipc/]
IntroSabequeesfcil?Usarlafuncinfork()(forksignificabifurcacin).Ustedpuedebifurcarunprocesotodoeltiempoytratarunproblemaenformaparalelayfraccionadaenpedazoscortos.Obviamenteestoesmsfcilsilosprocesosnosecomunicanentresmientrasestncorriendoypuedenquedarsehaciendolopropiosipreocuparseporlosdemsprocesos.Sinembargo,cuandoempiezaabifurcarprocesoscomienzaapensarenlasaplicacionesmultiusuarioquepodrahacersilosprocesospudierandialogarconlosdemsprocesosenformasencilla.Tambinpuedeintentarhacerunarrayglobalyluegobifurcarseparaversiesteescompartido.(esdecir,versitantoelprocesohijocomoelprocesopadreusanelmismoarray.)Encontrarqueelprocesohijotienesupropiacopiadelarrayyelpadreesolvidadoparaqueelhijopuedahacercualquiercambio.Cadavezqueunprocesocreaaotro,letransfieresusrecursos,peronoseduplican.Elkerneldetectaelaccesoparamodificarunrecursoporpartedelhijoyenesemomentocreaunacopiapropia.Cmopuedeconseguirqueestostiposhablenconotros,compartanestructurasdedatosyseangeneralmenteamigables?Estedocumentodiscutediversosmtodosdecomunicacinentreprocesos(IPCs)quepuedenlograrestecometidoalgunodeloscualessonmejoresymssatisfactoriosparaciertastareasqueparaotras.
PblicoSiustedconoceCoC++yesbastantebuenousandoelentornoUnix(uotrosentornosPOSIXquesoportenestassystemcalls),estedocumentoesparausted.Sinoestfamiliarizado,bueno,Estenelhorno!.Estedocumentosupone,sinembargoqueustedcuentaconunabastaexperienciaenprogramacinenC.EstedocumentosignificauntrampolnparaelusuarioenelreinodelasIPCsentregandounaapreciacinglobalyconcisadevariastcnicasentalsentido.stenoeseljuegodefinitivodedocumentosquecubrenesteasunto,sinoquesoloesunpieparaintroducirseenelmundodelasIPCs.
PlataformaycompiladorSecompilaronlosejemplosenestedocumentobajoLinuxqueusagcc.EllostambinsehanpodidocompilarbajoHPUX10.10usandoccAe.
Documentosespecficos:.Unfork()adecuado.seales.Pipes.FIFOS(llamadaspipes).Archivosprotegidos
SysVIPCs.Colasdemensajes.Semforos.MemoriaCompartida.Mapadearchivosenmemoria.SocketsdeUnix
Msrecursos
1
Unfork()adecuadoLafuncinfork()puedepensarsecomounmediopoderoso.Poderqueavecespuedepensarsecomounboletoaladestruccin.Porconsiguiente,debetenercuidadocuandoutilizaelforkensusistema.Noesquenuncadebajugarconfork,solotienequesercauto.Unfork()eslaformacmoUnixempiezanuevosprocesos.Bsicamente,sucedeas:elprocesopadre(elqueyaexiste)sebifurcaaunprocesohijo(elnuevo).Elprocesohijoobtieneunacopiadelosdatosdelpadre.Voal!Tienedosprocesosdondehabaunosolo!Porsupuesto,sillenalamquinadeprocesoselsistemasetornartanlentoqueforzarareiniciarlasmquinas.Enprimerlugar,debeconoceralgoacercadelaconductadelprocesobajoUnix.Cuandounprocesosemuere,realmentenosemarchacompletamente.Estmuerto,porqueyanoestcorriendo,perounremanentepequeoestesperandoalrededorparaserrecogidoporelprocesopadre.Esteremanentecontieneelvalordelretornodelprocesodelhijoyalgoms.Asdespusdeunabifurcacindelprocesopadreaunprocesohijo,debeesperar(waitpid())alprocesohijoparaterminar.Esesteactodeesperaelquepermitequetodoslosremanentesdelhijopuedandesaparecer.Naturalmente,hayunaexcepcinalareglaanterior:elpadrepuedeignorarlasealSIGCLDyentoncesnotendrqueesperar.Estopuedehacerse(ensistemasquelosoportan)delsiguientemodo:
main(){signal(SIGCLD,SIG_IGN);/*ahoranotengoqueesperar!*/..fork();fork();fork();/*sereproducecomoconejos!*/Ahora,cuandounprocesohijosemuereynohasidoesperado,estesemostrarnormalmenteenunalistadopscomo(difunto).Esteseguirsiendodifuntomientraselpadreloespereoserepartecomosemencionadebajo.Hayotrareglaquedebeaprenderahora:cuandoelpadresemuereantesdequeculminelaesperadelhijo(asumiendoquenoestignorandoSIGCLD),elhijoesreenparentadoalprocesoinicialinit(conPID1).Estonoesunproblemasielniotodavaestviviendobienybajocontrol.Sinembargo,sielhijoyaestdifunto,estaremosporunmomentoenunlazo,Veaqueelpadreoriginalyanopuedeesperar,porqueestmuerto.Entoncescmosabenlosinitquedebenesperaraestosprocesoszombis?Larespuesta:esmgico!Bien,enalgunossistemas,elinitdestruyetodoslosprocesosdifuntosqueposeeperidicamente.Enotrossistemas,lmuydescaradoseniegaavolverseelpadredecualquierprocesodifuntoyencambiolosdestruyeinmediatamente.Siestusandounodelossistemasanteriores,podraescribirunalazoquellenealatabladeprocesosconprocesosdifuntosposedosporINIT.Nohabrahechofelizasuadministradordesistema?Sumisin:asegresequesuprocesopadreignoreSIGCLD,oespereatodoslosprocesoshijosbifurcados.Endefinitiva:loshijossevuelvendifuntosalaesperadelpadre,amenosqueelpadreestignorandoSIGCLD.Adems,loshijos(vivosodifuntos)cuyospadressemuerensinesperarlosaellos(asumiendoqueelpadredenuevonoestignorandoSIGCLD)sevuelvenhijosdelprocesoinitquelosrepartemanualmente.LafuncinforkledevuelvealpadreelPIDdelhijoo1sihuboalgnerrormientrasquealhijoledevuelveun0.Bien!Aquestaunejemplodecmousarfork():#include#include#include#include#include#include
main(){pid_tpid;intrv;
2
switch(pid=fork()){case1:perror("fork");/*algosalimal*/exit(1);/*elpadretermina*/
case0:printf("HIJO:esteeselprocesohijo!\n");printf("HIJO:MiPIDes%d\n",getpid());printf("HIJO:ElPIDdemipadrees%d\n",getppid());printf("HIJO:Ingresemiestadodesalida(hgalocorto):");scanf("%d",&rv);printf("HIJO:Aquestamiestadodesalida!\n");exit(rv);
default:printf("PADRE:Esteeselprocesopadre!\n");printf("PADRE:MiPIDes%d\n",getpid());printf("PADRE:ElPIDdemihijoes%d\n",pid);printf("PADRE:Ahoraesperoamihijoparasalir...\n");wait(&rv);printf("PADRE:Elestadodesalidademihijoes%d\n",WEXITSTATUS(rv));printf("PADRE:Aquestoyafuera!\n");}}
Elpid_tesuntipogenricodelproceso.BajoUnix,steesunshort.Paraquealllamaralfork()sesalveelvalorretornadoenlavariabledelpidfork()essencillayaquepuededevolverslotrescosas:0:Sidevuelve0,eselprocesohijo.PuedeconseguirlosPIDdelpadrellamandoalafuncingetppid().TambinpuedeconseguirsupropioPIDllamandoalafuncingetpid().
1:Sidevuelve1,algosalimal,yningnhijofuecreado.Useperror()paraverloquepas.Probablementellenlatabladeprocesos.Otrovalor:Cualquierotrovalordevueltoporfork()significaqueeselpadreyqueelvalordevueltoeselPIDdesuhijo.staeslanicamaneradeobtenerlosPIDdesushijos,
Cuandoelhijollamafinalmenteaexit(),elvalorretornadollegaralpadrecuandoesteespera.Comoustedpuedeverdelallamadaalainstruccinwait(),hayalgunasrarezasqueentranjuegocuandoimprimimoselvalorretornado.NosreferimosalusodeWEXITSTATUS().Estaesunamacroqueextraeelvalorrealretornadodelhijoalaesperadequesusvaloresseaningresados.S,haymsinformacinencerradaeneseenterolepermitirverlomsadelanteporsmismo.Lavariablervesunargumentoparalafuncinscanf(),unpunteroaentero.Scanfdevuelveenlavariabledememoria(tipoint,enestecaso)apuntadaporrv,elnmeroqueletipeeporteclado,scanftelodevuelveconvertidoaentero.Oseascanf=scanwithformat,comoprintf,soloquescanfteleelaentradayprintfteescribeenlasalida...Loquehaceelprogramaespedirteelcdigoderetorno.Seloingresaporteclado,yretornaconeseparmetroenlafuncinexit().Nadatil...soloungrupodeexcusasparaejercitarprogramacin....esoesloqueencontrarenlaguadebeej.
Sepreguntarcomohacewait()parasaberqueprocesoespera?Yaqueelpadrepuedetenervariosprocesoshijos.Larespuestaessimple,misamigos:esperaporcualquierhijoquellegueprimeroparaterminar.Siquiere,puedeespecificaraquhijoesperarllamandoalainstruccinwaitpid()conelPIDdesuhijocomoargumento.Otracosainteresanteparanotardelejemploanterioresqueelpadreyelhijousanlavariablerv.Significaestoqueescompartidoentrelosprocesos?NO!Sifuera,yonohabraescritotodoestematerialdeIPC.
3
Cadaprocesotienesupropiacopiadetodaslasvariables.Haymuchosotrosdatosquesecopiantambin,perotendrqueleerlaspginasdelMANparavereso.Unanotafinalsobreelprogramaanterior:Youtilicuncambiodedeclaracinparamanejarelfork(),yestonoeslomsusual.Amenudoverunadeclaracintancortacomolasiguiente:
if(!fork()){printf("Yosoyelhijo!\n");exit(0);}else{printf("Yosoyelpadre!\n");wait(NULL);}
Elejemploanteriortambindemuestraesperarconwait()sinoleimportaelvalorqueretornaelhijopuedesolollamarlaconNULLcomoargumento.
ConclusionesElfork()esunasystemcallparacrearprocesos.Losprocesosbifurcadoscorrespondenaunamismacopiafsicadelcdigoylosdatosapuntadapor2descriptoresdeprocesosdiferentes.(unacopia=2procesosesloquecaracterizaalLighweigtprocess)
PginasHPUXdelMANSinoejecutaHPUX,verifiquelassiguientespginaslocalesdelMAN:
exit() fork() ps signal() signallisting wait() waitpid()
SealesHayunmuyfcil,simple,yavecestilmtodoparaqueunprocesoavisealgoaotro:lasseales.Bsicamenteunprocesopuedeactivarunasealytenerunefectodeliberadosobreotro.Elhandlerdelaseal(justamenteunafuncin)esinvocadoyelprocesopuedemanejarlo.Losprocesospuedensealizarseentresopuedehacerloelkernel.Algunassealespuedenignorarse,otraspuedeninterceptarseycambiarelhandler.Porejemplo,unprocesopodraquererdeteneraotro,yestopuedeserhechoenviandolasealSIGSTOP.Paracontinuar,elprocesotienequerecibirlasealSIGCONT.Cmosabeelprocesoquedebehacercundorecibeunaciertaseal?Bien,muchassealessonpredefinidasyelprocesotieneunhandleryaestablecidoparamanejarla.
Unhandlerpredefinido?S.TomeSIGINTporejemplo.staeslasealdeinterrupcinqueunprocesorecibecuandoelusuariopulsa^C.ElhandlerpredefinidoparaSIGINThacequeelprocesotermine!Lesuenafamiliar?Bien,comopuedeimaginarse,puedeatropellarlasealSIGINTparahacercualquiercosaquequiera(onadaenabsoluto!)Ustedpodratenersuprocesodeprintf()Ahorasabequepuedetenersuprocesopararesponderacasicualquiersealdecasicualquiermaneraquequiera.Hayexcepcionesnaturalmente,comoporotraparteserademasiadofcilentender.TomelasealmspopularSIGKILL,seal#9.TecleandoKILL9mataunprocesoencurso.UstedestabaenvindoleSIGKILL.Ahoratambinpodrarecordarqueningnprocesopuedeescapardeun"kill9",.SIGKILLesunadelassealesalasqueustednopuedeagregarsupropiohandlerparalaseal.ElSIGSTOPmencionadotambinestenestacategora.ElcomandoKILLpermiteenviarsealesdesdeelprompt.
4
Adems:ustedusaamenudoelcomandoUnix"KILL"sinespecificarqueestenviando....peroquesealesesta?Respuesta:SIGTERM.UstedpuedeescribirsupropiohandlerparaSIGTERMparaquesuprocesonorespondaaunregular"KILL",yelusuariodeberentoncesusar"KILL9"paradestruirelproceso.)Todaslassealesestnpredefinidas?Quequiereenviarunasealquetengaimportanciayquesloustedentiendaelproceso?Haydossealesquenoestnreservadas:SIGUSR1ySIGUSER2.Ustedeslibredeusarlasparacualquiercosaquequieraydemanejarlasdelamaneraqueescoja.(Porejemplo,miprogramadereproductordeCDpodraresponderaSIGUSR1adelantandoalaprximapista.Deestamanera,yopodracontrolarlodelalneadecomandostipeandoKILLSIGUSR1nnnn".)
NopuedeenviarSIGKILLalPresidente!ComopuedesuponercomandoKILLdelUnixesunamaneradeenviarsealesalosprocesos.Porpuracoincidencia,hayunasystemcallllamadaKILL()quhacelamismacosa.TomaporargumentounnmerodefinidoyunprocessID(comosedefiniensignal.h).Haytambin,unarutinadelalibrerallamadaraise()qupuedeusarseparalevantarunasealdentrodelmismoproceso.Lapreguntaardientepermanece:cmotomaunasealSIGTERMrpidamente?Necesitausarlasealmencionadaypasarleunpunteroalafuncinquequierequeseasuhandler.Nuncauspunterosafuncin?Debecomprobarlarutinaqsort()algnda!).Nosepreocupe,sonsimples:si"elfoo("hola"!)";esunallamadaalafuncinfoo(),entonces"foo"esunpunteroaesafuncin.Ustednotienequeusarnisiquieraladireccindeloperador.Sinembargo,aquestlaseal()ruptura:
void(*signal(intsig,void(*func)(int)))(int);
Bien,lasituacinbsicaesesta:vamosapasarlasealasermanejadaascomoladireccindesuhandlercomoargumentosenlallamadaasignal().Lafuncindelhandlerdelasealqueusteddefinetomaunsolointcomoargumento,yretornaunvoid.Luego,signal()retornarunerror,ounpunteroalafuncindelhandlerprevio.Paraquetengamoscomollamarasignal()lacualaceptacomoargumentosunasealyunpunteroalhandler,yretornaunpunteroalhandlerprevio.Afortunadamenteusarloesmuchomsfcildeloqueparece.Todoloquenecesitaesunhandlerquetomeunenterocomoargumentoyretornevoid.Entoncesprepararelllamadoaunasealesfcil?HagamosunprogramasimplequemanejarSIGINTydetendrelusuarioatravsde^C,llamadosigint.doc:sigint.doc:#include#include#include#include
intmain(void){voidsigint_handler(intsig);/*prototipo*/chars[200];/*preparaelhandler*/if(signal(SIGINT,sigint_handler)==SIG_ERR){perror("signal");exit(1);}printf("Ingreseunstring:\n");if(gets(s)==NULL)perror("gets");elseprintf("ustedingres:\"%s\"\n",s);return0;}/*esteeselhandler*/
5
voidsigint_handler(intsig){printf("Ahorano!\n");}
Esteprogramatienedosfunciones:elmain()qupreparaelhandler(usandolallamadaalafuncinsignal()),ysigint_handler()queselhandlerdelaseal.
Qupasacundoloejecuta?Siestenmediodelingresodelstringyteclea^C,lallamadaagets()fallaysetealavariableglobalerrnodeEINTR.Ademssellamaasigint_handler()quehacesurutina,paraqueustedrealmentevea:Ingreseunstring:gets:systemcallinterrumpida
Aquhayunapartevitaldeinformacinqueomitmencionarantes:cuandoelhandlerdelasealesllamado,elhandlerparticulardeestasealpredefinidosereestablece.Elresultadoprcticodeestoesquenuestrosigint_handler()atraparael^Cquetecleamoslaprimeravez,peronolosposteriores.Lasolucinrpidaysuciaesrestableceralhandlerdelasealdentrodesmismocomosemuestraacontinuacin:
voidsigint_handler(intsig){signal(SIGINT,sigint_handler);/*reestableseestafuncin*/printf("ahorano!\n");}Elproblemaconestearregloesquesiocurreunainterrupcinyelhandleresllamado,peroocurreotraantesdequelaprimerapuedarestablecerelhandlerdeinterrupcin,elhandlerpredefinidoserelinvocado.Seaconscientequesiestesperandomuchasseales,podraocasionarproblemas.
TodoloqueustedsabaestabamalLallamadaalasystemcallsignal()eselmtodohistricoparaprepararseales.ElestndarPOSIXhadefinidounmontndenuevasfuncionesparaenmascararlassealesquequierarecibir,verificaqusealesestnpendientes,ypreparaloshandlersdelasseales.Dadoquemuchasdeestasllamadasoperanengrupos,ojuegos,deseales,hayvariasfuncionesquetratandemanipularlasseales.
Enconclusin,elnuevomtododemanejodesealessuperaampliamentealviejo.Yoincluirunadescripcindelmismoenunaprximaversindeestedocumento,sieltiempolopermite.
AlgunassealesparahacerlopopularAquhayunalistadesealesque(probablemente)tieneasudisposicin:
Seal DescriptionSIGABRT Abortaelprocesodelaseal.SIGALRM Alarmadelreloj.SIGFPE Operacinaritmticaerrnea.SIGHUP Hangup.SIGILL Instruccinnovlida.SIGINT Sealdeinterrupcindeterminal.SIGKILL Matar(nopuedeatraparseniignorarse).SIGPIPE Escribirenunpipequenofueledo.SIGQUIT Dejarlasealdeterminal.SIGSEGV Referenciaamemorianovlida.
6
SIGTERM Sealdeterminacin.SIGUSR1 Seal1definidaporelusuario.SIGUSR2 Seal2definidaporelusuario.SIGCHLD Procesohijofinalizadoodetenido.SIGCONT Continuarlaejecucinsiestabastopped.SIGSTOP Detenerlaejecucin(nopuedeatraparseniignorarse).SIGTSTP Sealdedetencindeterminal.SIGTTIN Intentodelecturadeunprocesobackground.SIGTTOU Intentodeescrituradeunprocesobackground.SIGBUS Errordebus.SIGPOLL EventodetectableporencuestaSIGPROF Perfildetiempoexpirado,SIGSYS SystemcallerroneaSIGTRAP Trace/breakpointtrap.SIGURG DatodealtaprioridaddisponibleenunsocketSIGVTALRM Timervirtualexpirado.SIGXCPU TiempodeCPUlmiteexcedido.SIGXFSZ Tamaolmitedearchivoexcedido.
Tabla1.SealesmscomunesCadasealtienesupropiohandlerpredefinido,cuyaconductasedefineenlaspginaslocalesdelM.A.N..
PginasHPUXdelMANSinoejecutaHPUX,verifiquelassiguientespginaslocalesdelMAN!
kill kill() raise() signal() signals
AquestnlaspginasdelMANparaalgunosdelosnuevoshandlersdeseales:
sigaction() sigprocmask() sigpending() sigsuspend() sigsetops
PipesNohayningunaformadeIPCqueseamssimplequelospipes.LlevadosacaboencadasaborqueproveeUnix,pipes()yfork()constituyenlafuncionalidaddetrsdel"|"en"ls|yms".Ellossonraramenteusadosparahacercosasbuenas,perosonunbuenmtodoparaaprenderacercadelosmtodosbsicosdeIPCs.Comosonmuyfciles,novamosaemplearmuchotiempoenellos.Tendremosapenasalgunosejemplosyalgnmaterial.
"Estospipesestnvacos!"Espere!Notanrpido.Aestaalturayopodranecesitardefinirunos"descriptoresdelarchivo".Permtameponerloestamanera:qutantosabeustedsobreel"FILE*"destdio.h?Sabequetienetodasesas
7
funcionesbuenascomoelfopen(),fclose(),fwrite(),yas?Bien,sassonahorafuncionesdealtonivelqueseimplementanusandodescriptoresdearchivosofiledescriptors,losqueusansystemcallstalescomoopen(),creat(),close(),ywrite().LosdescriptoresdearchivosimplementesonenterosquesonanlogosalosFILE*senstdio.h.FILE*esunaestructuradefinidaenPOSIXparafuncionesdemanejodestreams(flujosdeinformacin)demasaltonivelcomolaantesmencionadasfopen,fread,fwrite,etc.Estasfuncionespermitenalgunasoperacionesmselaboradasconlosdescriptoresdelosarchivos.Lasqueusamosnosotrossonlasprimitivas,porllamarlasdealgnmodo(alviejoestiloUNIX).Porejemplo,stdineseldescriptordearchivo"0",elstdoutesel"1",yelstderresel"2".Delmismomodo,puedeabrircualquierarchivousandofopen()conlocualobtienesuspropiosdescriptoresdelarchivo,aunqueestedetalleestocultodeparausted.(EstedescriptordelarchivopuedeserrecuperadodelFILE*usandolamacrofileno()destdio.h.)
Figura1.Cmoseorganizaunpipe.
Bsicamente,unallamadaalafuncinpipe()retornaunpardefiledescriptors.Unodeestosdescriptoresseconectaalextremodeescriturayelotroaldelectura.Algopuedeescribirseenunextremodelpipe,yserledoenelotroextremoenelordenenelquevino.Enmuchossistemas,lospipessellenarndespusdequeustedescribaaproximadamente10Kenellossinleer.Comounejemplointil,elprogramasiguientecrea,escribeyleeunospipes.
#include#include#include#includeintmain(){intpfds[2];charbuf[30];if(pipe(pfds)==1){perror("pipe");exit(1);}printf("filedescriptorparaescritura#%d\n",pfds[1]);write(pfds[1],"test",5);printf("filedescriptorparalectura#%d\n",pfds[0]);read(pfds[0],buf,5);printf("leo\"%s\"\n",buf);}
Comoustedpuedever,pipe()tomaunarraydedosenteroscomoargumento.Sinoaconteceningnerror,conectalosdosfiledescriptorsylosdevuelveenelarray.Elprimerelementodelarrayeselfiledescriptordelextremodelecturadelpipe,yelsegundoesdelextremodeescritura.
fork()ypipe()tieneselpoder!Enelejemploanterior,resultadifcilverquetantilesson.Bien,yaquesteesundocumentodeIPC,pongamosunfork()enlamezclayaverquepasa.Supongaqueustedesunagentefederalqueseasignparaconseguirqueunprocesohijoenvelapalabra"test"alpadre.Noesmuyfascinante,peronienlainformticanienlavidaseraMulderdelosexpedientesX..Primero,tendremosquehacerleunpipealpadre.Luego,haremosunfork().Ahora,lapginadelMANreferidaalfork()nosdicequeelhijorecibirunacopiadelosdescriptoresdearchivodelpadre,yesto
8
incluyelosdelpipe.Ahoraelhijopodrescribirenunextremodelpipeyelpadrepodrleerlosporelotroextremo.Estosehaceas:#include#include#include#include
intmain(){intpfds[2];charbuf[30];
pipe(pfds);
if(!fork()){printf("HIJO:escribiendoenelpipe\n");write(pfds[1],"test",5);printf("HIJO:terminando\n");exit(0);}else{printf("PADRE:leyendoelpipe\n");read(pfds[0],buf,5);printf("PADRE:le\"%s\"\n",buf);wait(NULL);/*esperaquetermineunhijocualquiera*/}}
Tengaencuentaquesusprogramasdebentenermuchosmschequeosdeerroresquelosmos.Yolosomitoparaayudaraquelascosasquedenclarasynocomplicarlasintilmente.Detodosmodos,esteejemploesjustocomoelanterior,exceptoqueahorahacemosfork()deunnuevoprocesoqueescribeenelpipemientrasqueelpadrelolee.Loqueseobservarseralgocomoesto:PADRE:leyendoelpipe.HIJO:escribiendoenelpipeHIJO:terminandoPADRE:leo"test"Enestecaso,elpadreintentaleerdelpipeantequeelhijoloescriba.Cuandoestopasa,sedicequeelpadresebloquea,oduerme,hastaquelosdatoslleguenparaserledos.Parecequeelpadreintentleer,comonohabadatossefueadormir,cuandoelhijotermindeescribirlosdatoselpadresedespertylosley.
Yoapuestoaquetodavaestpensandoquenohaymuchosusosparapipe()y,bien,tienerazn.LasotrasformasdeIPCssongeneralmentemstilesyamenudomsexticas.
LabsquedadelpipecomonosotrossabemosEnunesfuerzoparahacerlepensarquelospipessonbestiasrealmenterazonables,yoledarunejemplodecmousarpipe()enunasituacinmsfamiliar.Eldesafo:implementar"ls|wcl"enC.Estorequieredelusodeunpardefuncionesdelasquenuncahabrodohablar:exec()ydup().Lafamiliadefuncionesexec()reemplazaelprocesoqueestcorriendoporcualquieraquesepaseconexec().staeslafuncinqueusaremosparaejecutarlsywcl.Sonfuncionesquepermitenreemplazarunprocesoenmemoriaporotroquesepasaenlalistadeargumentos.Loquesereemplazaeselcdigoylosdatosperoseheredaelprocessdescriptor(esdecirlamismaestructuratask_struc)quetenaelprocesoreemplazado.Obviamente,alefectuarelreemplazo,seactualizanciertoscamposdetask_struc,comoporejemplo,lospunterosalasestructurasmm_strucquedescribencadabloquedememoriafsicaasignadoalprocesoporpartedelsistemaoperativo.Seheredanlosfiledescripotrsabiertos(peroOJOnoseheredanlasvariablesqueloscontienenyaquelossegmentosdelprocesosonreemplazadosporlosdelnuevoproceso!!!!),seheredaelPID,elPPID(PIDdelpadre),etc.
dup()tomaundescriptordeunarchivoabiertoqueselepasacomoargumentoyhaceunclon(unduplicado)del.Elduplicadoselohaceenelprimerdescriptorqueencuentradisponible.Asescmo
9
conectaremoslasalidaestndardellsalaentradanormaldewc.Vea,stdoutdelflujodellsdentrodelpipe,yelflujodestdindelwcdentrodelpipe.Elpipeencajajustoenelmedio!
Siantessehaceclose(0)oclose(1)segncorrespondaalpadreoalhijoyluegodup(),estaltimabuscaelprimerfiledescriptorlibreapartirde0ycopiaallelfiledescriptorquerecibicomoargumento.Enesesencilloacto,redirigielarchivoaldescriptorqueencontr.Enuncasoredirigeelfiledescriptordeescrituradelpipealasalidaestndar(pantalla)yenelotroredirigelaentradaestndar(teclado)alfiledescriptordelecturadelpipe.Entonceselefectologradoesquecuandoescribaenelpipe,saleporlapantallaycuandoleaelpipeleerloqueentraportecladoDetodosmodos,aquestcdigo:#include#include#includeintmain(){intpfds[2];
pipe(pfds);
if(!fork()){close(1);/*cerrarlastdoutnormal*/dup(pfds[1]);/*hacelomismoquestdoutconpfds[1]*/close(pfds[0]);/*nonecesitamosesto*/execlp("ls","ls",NULL);}else{close(0);/*cierralastdinnormal*/dup(pfds[0]);/*hacelomismoquestdinconpfds[0]*/close(pfds[1]);/*nonecesitamosesto*/execlp("wc","wc","l",NULL);}}Yovoyahacerotranotaacercadelacombinacinclose()/dup()yaqueesbastanterara.close(1)liberaalfiledescriptor1(salidaestndar).dup(pfds[1])haceunacopiadeloescritoenelfinaldepipeenelprimerfiledescriptordisponible,queesel1yaqueacabamosdecerrarlo.Deestamanera,algoquelsescribealasalidaestndar(filedescriptor1)cambiaralpdfs[1](elqueescribealfinaldelpipe).Deigualmodoseprocedeconlaseccinwcdecdigodetrabajo,exceptoqueesalrevs.
ConclusionesProbablementeelmejorusoparalospipesesunoalqueustedestacostumbrado:enviaralasalidaestndardeuncomandoalaentradaestndardeotro.Paraotrosusos,esmuylimitadoyamenudoexistenotrastcnicasdeIPCsquetrabajanmejor.
PginasHPUXdelMANSinoejecutaHPUX,verifiquelassiguientespginaslocalesdelMAN!
dup() exec() fileno() fork() pipe() read() write()
10
FIFOsUnFIFOesconocidaavecescomounpipenombrado.Esdecir,escomounpipe,sloquetieneunnombre!Enestecaso,elnombreeseldeunarchivoquelosprocesosmltiplespuedenabrirconopen(),leeryescribir.EsteltimoaspectodelasFIFOssediseaparapermitirlesirmasalldeunadelaslimitacionesdelospipesnormales:ustednopuedeagarrarunextremodeunpipenormalquefuecreadoporunprocesonorelacionado.Vea,siyoejecutodoscopiasindividualesdeunprograma,ambospuedenllamaralafuncinpipe(),yaunqueellosquierancomunicarseentres,todavanopueden.(Estoesporquedebeejecutarpipe()yluegofork()paraobtenerunprocesohijoquepuedacomunicarseconelpadrepormediodepipe.)ConlasFIFOs,sinembargo,cadaprocesonorelacionadopuedeabrirsimplementeunpipe()ytransferirdatosatravsdel.
UnanuevaFIFOnacePuestoquelaFIFOrealmenteesunarchivoendisco,ustedtienequehaceralgunosmanejoscreativosparacrearla.Noesdifcil.Apenastienequellamaralafuncinmknod()conlosargumentosapropiados.Aquhayunallamadaamknod()quecreaunaFIFO:
mknod("myfifo",S_IFIFO|0644,0);
Enelejemploanterior,elarchivodelaFIFOsellamar"myfifo".Elsegundoargumentoeselmododecreacinqueseusaparadecirleamknod()quehagaunaFIFO(S_IFIFOformaparteenlaoperacinOR)ylospermisosdeaccesoparaleeraesearchivo(octal644,orwrr)qutambinpuedeponerseusandolasmacrosdesys/stat.h.Estepermisoesjustamentedelaformaquelopondrausandoelcomandochmod.Finalmente,sepasaunnmerodedispositivo.EstoseignoracuandosecreaunaFIFO,paraponerloquesequiera.(Aparte:unaFIFOtambinpuedecrearsedelalneadecomandosusandoelUnixmknodcommand.)Lospermisosdecadaficherosepuedenverconelcomandolsl.Paracambiarlospermisosdeunficheroseempleaelcomandochmod,quetieneelformatosiguiente:
chmod[quien]operpermisofiles
quienIndicaaquienafectaelpermisoquesedeseacambiar.Esunacombinacincualquieradelasletrasuparaelusuario,gparaelgrupodelusuario,oparalosotrosusuarios,yaparatodoslosanteriores.Sinosedaelquien,elsistemasuponea.operIndicalaoperacinquesedeseahacerconelpermiso.Paradarunpermisosepondrun+,yparaquitarlosepondrun.permisoIndicaelpermisoquesequieredaroquitar.Serunacombinacincualquieradelasletrasanteriores:r,w,x,s.filesNombresdelosficheroscuyosmodosdeaccesosequierencambiar.Porejemplo,paraquitarelpermisodelecturaalosusuariosdeunficheroelcomandoes:chmodarfichero.txtLospermisosdelectura,escriturayejecucintienenunsignificadodiferentecuandoseaplicanadirectoriosynoaficherosnormales.Enelcasodelosdirectorioselpermisorsignificalaposibilidaddeverelcontenidodeldirectorioconelcomandols;elpermisowdalaposibilidaddecrearyborrarficherosenesedirectorio,yelpermisoxautorizaabuscaryutilizarunficheroconcreto.
ProductoresyConsumidoresUnavezquelaFIFOsehacreado,elprocesopuedecomenzarypuedeabrirlaparaleerlaoescribirlausandolasystemcallestndaropen().Puestoqueesmsfcilentenderelprocesounavezquesetienealgncdigoconcebido,presentaraqudosprogramasqueenviarndatosatravsdeunFIFO.Unoesspeak.cqueenvadatosatravsdelFIFO,yelotrosellamatick.c,quesacadatosdelaFIFO.Aquestspeak.c:#include
11
#include#include#include#include#include#include#include
#defineFIFO_NAME"american_maid"
main(){chars[300];intnum,fd;
/*noolvidechequearerrores!!*/mknod(FIFO_NAME,S_IFIFO|0666,0);
printf("esperandolectores...\n");fd=open(FIFO_NAME,O_WRONLY);printf("tengounlectortipeealgo\n");
while(gets(s),!feof(stdin)){if((num=write(fd,s,strlen(s)))==1)perror("escribir");elseprintf("speak:escribi%dbytes\n",num);}}
Lacondicinwhile(gets(s),!feof(stdin))quieredecirmasomenosquemientrasporstdinnollegueunEOF......haceloqueestentrellaves.Funcionaenbaseadosfunciones:gets(s)queleestdin(teclado)ydevuelveenselpunteroalarraydecaracteresledos,yfeof(filedescriptor),queevalalalecturarealizadadesdeunstreamdedatosydevuelve1cuandoseleeelcarcterFindeArchivo.stdindevuelveEOFcuandosepulsaENTER.DemodoqueelconjuntohacequemientrasnosepulseENTERsealmacenenloscaracteresledosenlastrings.CuandopulseENTERdevuelveelpunteroalastring.....
LoquespeakhaceescrearlaFIFO,entoncesintentaabrirlaconopen().Ahora,loquepasaresqueelllamadoaopen()bloquearhastaquealgnotroprocesoabraelotroextremodelpipeparaleer.(HayunamanerareferidaaestoverdebajoO_NDELAY,)Eseprocesoestick.c,mostradoaqu,:#include#include#include#include#include#include#include#include
#defineFIFO_NAME"american_maid"main(){chars[300];intnum,fd;
/*noolvidechequearesto!!*/mknod(FIFO_NAME,S_IFIFO|0666,0);
printf("esperandoaquienescriba...\n");fd=open(FIFO_NAME,O_RDONLY);printf("conseguunescritor:\n");
do{
12
if((num=read(fd,s,300))==1)perror("read");else{s[num]='\0';printf("tick:le%dbytes:\"%s\"\n",num,s);}}while(num>0);}
Talcomosucedaconspeak.c,tick,aqusebloquearlaejecucinconopen()sinohayniunaescrituraenlaFIFO.EncuantoalguienabralaFIFOparaescribir,tickvolveralavida.Prubelo!Ejecutespeakysebloquearhastaqueustedarranquetickenotraventana.(Recprocamente,siustedempiezatick,bloquearhastaqueustedejecutespeakenotraventana.)Tecleeenlaventanadespeakyticklotomar.
Ahora,salgadespeak.Avisoloquepasa:losread()entickretornan0,loquesignificafindearchivo.Deestamanera,ellectorpuededecircuandotodoslosescritoreshancerradosuconexinalaFIFO.Qu?Preguntasipuedehaberescritoresmltiplesalmismopipe?Efectivamente!Esopuedesermuytil.Quizsmuestredespuseneldocumentocmopuedeexplotarseestaventaja.Peroporahora,permtameterminarestetemaviendoloquepasacuandosaledetickmientrasspeakestcorriendo."Piperoto"!Quinhizoesto?Bien,loquehapasadoesquecuandotodosloslectoresdeunaFIFOcierranyelescritoresttodavaabierto,elescritorrecibirlasealSIGPIPElaprximavezqueintenteescribir.Elhandlerpredefinidodelasealescribe"PipeRoto"ytermina.Porsupuesto,ustedpuedemanejarestomsairosamentetomandoSIGPIPEatravsdelallamadaalafuncinsignal().Finalmentequpasasitienelectoresmltiples?Bien,lascosasextraaspasan.Avecesunodeloslectoresconsiguetodo.Avecesalternaentreloslectores.Sinembargo,Paraququieretenerlectoresmltiples?
O_NDELAY!SoyIMPARABLE!Antes,mencionquepodrabloquearllamandoalafuncinopen()sinohabaningnlectoroescritorcorrespondiendo.Lamaneradehacerestoesllamaraopen()conelflagO_NDELAYpuestaenelargumentodelsiguientemodo:fd=open(FIFO_NAME,O_RDONLY|O_NDELAY);
Estoprovocarqueopen()devuelva1sinohayningnprocesoquetengaelarchivoabiertoparaleerlo.Igualmente,puedeabrirelprocesodellectorusandoelflagO_NDELAY,peroestotieneunefectodiferente:todoslosintentosdeleerelpiperetornarn0bytesledossinohayningndatoenelpipe.(Esdecir,losread()yanobloquearnhastaquehallaalgndatoenelpipe.)Notequeyanopuededecirsiread()estdevolviendo0porquenohayningndatoenelpipe,oporqueelescritorhaterminado.steeselpreciodepoder,peromisugerenciaesintentarbloquearsiemprequeseaposible.
EscritoresmltiplesCmohagoparamultiplicarlos?Permtamedecirlequetieneunpipeconunlectoryunescritorconectadosal.Nohayningnproblemaparaellector,yaquehayslounlugardedondesusdatospodranprovenir(asaber,delunescritor.)Derepenteotroescritorbrincagruendodelassombras!Sinprevioavisoempiezaavomitardatosenelpipe!Cmovaaordenarlosdatosdelos2escritoreselpobrelector?Bien;haymuchasmaneras,ytodasellasdependendequtipodedatosestpasandodeunladoaotro.Unadelasmanerasmssimplestienelugarcuandotodoslosescritoresenvanlamismacantidaddedatosporvez(supongamos1024bytes).Entoncesellectorpodraleer1024bytesenunmomentoyseaseguraqueestobteniendounsolopaquete(oencasocontrario512bytesdeunescritory512deotro.)Sinembargo,todava,nohayningunamaneradedecirqueescritorenvicadapaquete.Unadelassolucionesmsbuenasaestoesusar,paracadaescritor,losprimerosbytesdecadapaqueteparaalgntipodeidentificadornico.Ellectorpuederecogeresteidentificadorydeterminarquescritorenvielpaquete.Este"id"puedepensarsecomounpequeoencabezadodepaquete.
13
Permitiendounencabezadodelpaquetetendremosmuchamsflexibilidadenloquepodemosenviaratravsdeunpipe.Porejemplo,podraagregaruncampodelongitudquelediceallectorcuntosbytesdedatosacompaanelencabezado.Unamuestradelaestructuradedatosquepodratenerestepaqueteseralasiguiente:
typedefstruct{shortid;shortlength;chardata[1024]}PACKET;
Transmitiendounpaqueteconunaestructurasimilaralaanterior,podratenerunnmeroarbitrariodeescritoresqueenvanpaquetesdelongitudesvariables.Ellectorpodrordenarlosatodosyaqueobtieneel"id"delafuentequeloescribiylalongituddelpaquete.
NotasconcluyentesLosprocesosnorelacionadossepuedencomunicarvapipes!(staesunacapacidaddeseadaparaelusodelospipesnormales.)Sinembargo,todavalafuncionalidaddelospipespodranoserrealmentelaqueustednecesitaparasusaplicaciones.Lascolasdelmensajepodransermsveloces,sisusistemalassoporta.
pginasHPUXdelMANSinoejecutaHPUX,verifiquelassiguientespginaslocalesdelMAN!
mknod() mknod open() read() signal() write()
Filelocking(archivocerradobajollave)Elfilelockingproporcionaunmecanismomuysimpleeincreblementetilparacoordinarlosaccesosaarchivos.Antesdequeempiececonlosdetallespermtanmecontarlesunossecretos:Haydostiposdemecanismosdecerradura:elmandatory(obligatorio)yeladvisory(asesor).Lossistemasmandatoryprotegernalosarchivosdelaescrituraylalectura(write()yread()).VariossistemasdeUnixlossoportan.Noobstante,yovoyignorarlosalolargodeestedocumento,prefiriendohablarsolamentesobrelosadvisorylocks.Conunadvisorylock,losprocesospuedenanleeryescribirenunarchivomientrasestnlockeados.Intil?Realmenteno,yaquehayunamaneradeverificarlaexistenciadeunlockantesdequeunprocesololeaoescriba.Vea,esunaespeciedesistemadecierrecooperativo.Estoesmsquesuficienteparacasitodoscasosdondeelfilelockingesnecesario.
Deahoraenadelantesiemprequemerefieraaunlockenestedocumento,meestarrefiriendoalosadvisorylocks.
Ahora,permtameestropearunpocomselconceptodeunlock.Haydostiposdeadvisorylocks:readlocksywritelocks(tambinllamadascerradurascompartidasycerradurasexclusivas,respectivamente.)Ladiferenciadetrabajarconreadlocksesqueellasnointerfierenconotrasreadlocks.Porejemplo,variosprocesospuedentenerunmismofilelockingparaleerlo.Sinembargo,cuandounprocesotieneunwritelockenunarchivo,ningnotroprocesopuedeactivarunreadlockniunwritelockhastaqueseabandone.Unamanerafcildepensarenestoesdecirquepuedehaberlectoresmltiplessimultneamente,peroenunmomentodadoslopuedehaberunescritor.
14
Unaltimacosaantesdeempezar:haymuchasmanerasdehacerunfilelockingensistemasUnix.Lossistemasqueusanlockf(),personalmente,piensoquesonunaporquera.Losmejoressistemassoportanflock()quofrecemejorcontrolsobreellock,perotodava,deciertaforma,lefalta.Paralaportabilidadeintegridad,hablarsobrecmohacerlockfilesqueusanfcntl().Parausarunafuncindealtoniveldelestilodeflock()quesisatisfacesusnecesidades,quierodemostrarelpoderqueustedtieneasusmanos.(SisuSistemaUnixnosoportafcntl()dePOSIXy,tendrquerecurriralainformacindelockf()enlaspginasdelMAN.)
PoniendounacerraduraLafuncinfcntl()hacecasitodoenelplaneta,peronosotroslausaremosapenasparahacerfileslocking.Ponerlacerraduraconsisteenllenarunastructflock(declaradaenfcntl.h)quedescribeeltipodecerradurarequerido,abrirconopen()elarchivoconelmodoemparejadoyllamarafcntl()conlosargumentosapropiados,comoestos:
structflockfl;intfd;
fl.l_type=F_WRLCK;/*F_RDLCK,F_WRLCK,F_UNLCK*/fl.l_whence=SEEK_SET;/*SEEK_SET,SEEK_CUR,SEEK_END*/fl.l_start=0;/*Offsetfroml_whence*/fl.l_len=0;/*length,0=toEOF*/fl.l_pid=getpid();/*ourPID*/
fd=open("filename",O_WRONLY);
fcntl(fd,F_SETLKW,&fl);/*F_GETLK,F_SETLK,F_SETLKW*/Empecemosconlaestructurastructflockyaqueloscamposenellaseusanparadescribirlaaccindelockingquetienelugar.Aquestnalgunasdefinicionesdeloscampos:
l_typelindicaeltipodecerraduraconelcontenidoqueustedlepone.DichocontenidopuedeserF_RDLCK,F_WRLCK,oF_UNLCKsiquiereponerunacerraduradelectura,escrituraoliberarlacerradura,respectivamente.
l_whenceEstecampodeterminadondecomienzaelcampol_start(escomounoffsetparaeloffset).PuedeserSEEK_SET,SEEK_CUR,oSEEK_END,paraelposicionamientoenelcomienzodelarchivo,enlaposicinactual,oenelfinal.l_startsteeseloffsetinicialenbytesdelacerradura,respectodelcampol_whence.
l_lenstaeslalongituddelaregindelacerraduraenbytes(laquecomienzaenl_startqueesrelativaal_whence).l_pidElprocessIDdelprocesoquetrataconlacerradura.Usegetpid()paraobtenerlo.Ennuestroejemplo,hicimosunacerraduradetipoF_WRLCK(unacerraduradeescritura),empezandorespectodeaSEEK_SET(elprincipiodelarchivo),usandooffset0,longitud0(uncerosignificalockalfindearchivo)conelPIDseteadoporgetpid().
Elprximopasoesabrirelarchivo,yaqueflock()necesitaunfiledescriptordelarchivoqueestsiendolockeado.Notequecuandoabreelarchivo,necesitaabrirloenelmismomodoqueuscuandoespecificlacerradura,comosemuestraenlatabla1.Siustedabreelarchivoenelmodoequivocadoparauntipodelacerraduradado,open()devolverEBADF.
15
l_type modoF_RDLCK O_RDONLYoO_RDWRF_WRLCK O_WRONLYoO_RDWR
Tabla1.Tiposdecerradurasysuscorrespondientesmodosdeaperturaconopen().Finalmente,lallamadaafcntl()realmentesetea,libera,uobtienelacerradura.Vea,elsegundoargumentodefcntl()(elcmd)dicequhacerconlosdatospasadosenlaestructuradeltipostructflock.Lasiguientelistaresumequehacecadacmdconfcntl():
F_SETLKWEsteargumentolediceafcntl()queintentaobtenerlacerradurarequeridaenlaestructuraflock.Silacerraduranosepuedeobtener(porquealgnotrolatiene),fcntl()esperar(sebloquear)hastaquelacerraduraselibereentonceslpodrobtenerla.steesuncomandomuytil.Yolousotodoeltiempo.F_SETLKEstafuncinescasiidnticaaF_SETLKW.Lanicadiferenciaesqueenestaunonoesperarsinopuedeobtenerunacerradura.Sinoqueretornarinmediatamenteun1.EstafuncinpuedeusarseparaliberarunacerraduraponiendounF_UNLCKenelcampol_typedelastructflock.F_GETLKSisloquiereverificarsihayunacerradura,peronoquiereponeruna,puedeusarestecomando.Estecomandomiratodoslosfilelockshastaqueencuentreunoqueestenconflictoconeltipodecerraduraqueespecificenlastructflock.Entoncescopialainformacindelacerraduraenconflictoenlaestructurayseladevuelve.Sinopuedeencontrarunacerraduraenconflicto,fcntl()retornalaestructuracomoustedlapas,exceptoqueleponealcampol_typeunF_UNLCK.
Ennuestroejemploanterior,llamamosafcntl()conF_SETLKWcomoargumento,paraquebloqueehastaquepuedaponerlacerradura,entonceslaseteaycontina.
LiberandounacerraduraDespusdetodoloquelockeamosllegelmomentodealgofcil:deshacerellocking.Realmenteestoesunaporcindelpastelcomparadoconloanterior.Yosolamentevolverausarelprimerejemployagregarelcdigoparadeshacerellockingalfinal:
structflockfl;intfd;
fl.l_type=F_WRLCK;/*F_RDLCK,F_WRLCK,F_UNLCK*/fl.l_whence=SEEK_SET;/*SEEK_SET,SEEK_CUR,SEEK_END*/fl.l_start=0;/*Offsetfroml_whence*/fl.l_len=0;/*length,0=toEOF*/fl.l_pid=getpid();/*ourPID*/
fd=open("filename",O_WRONLY);/*obtengoelfiledescriptor*/fcntl(fd,F_SETLKW,&fl);/*seteoellock,esperandosiesnecesario/...fl.l_type=F_UNLCK;/*ledigoquedeshagaellockenlaregin*/fcntl(fd,F_SETLK,&fl);/*seteodichaaccin*/
Ahora,dejelviejocdigodellockingparaquecompareynoteladiferencia,peroustedpuededecirqueapenascambielcampodel_typeaF_UNLCK(dejandolosotroscompletamenteinalterados!)yllamafcntl()conF_SETLKcomocomando.Esasdefcil!
16
UnprogramadedemostracinAqu,yoincluirunprogramadedemostracin,lockdemo.cqueesperaparaqueelusuarioprovoqueelretornoentonceslockeasupropiafuente,esperaporotroretorno,luegodeshaceellocking.Ejecutandoesteprogramaendos(oms)ventanas,ustedpuedevercmolosprogramasinteractanmientrasesperanporlascerraduras.Bsicamente,elusoeseste:siejecutalockdemosinlosargumentosdelneadecomando,intentaratraerunwritelock(F_WRLCK)ensufuente(lockdemo.c).Siloempiezaatodosconalgnargumentoenlalneadecomando,intentaratraerunreadlock(F_RDLCK)enl.Aquestaelfuente:
#include#include#include#include#includeintmain(intargc,char*argv[]){ /*l_typel_whencel_startl_lenl_pid*/ structflockfl={F_WRLCK,SEEK_SET,0,0,0}; intfd;fl.l_pid=getpid(); if(argc>1) fl.l_type=F_RDLCK; if((fd=open("lockdemo.c",O_RDWR))==1){ perror("open"); exit(1); } printf("Presioneparaintentarobtenerellock:"); getchar(); printf("Intentandoobtenerellock..."); if(fcntl(fd,F_SETLKW,&fl)==1){ perror("fcntl"); exit(1); } printf("gotlock\n"); printf("Presioneparaliberarellock:"); getchar(); fl.l_type=F_UNLCK;/*liberaellockenlamismaregin*/ if(fcntl(fd,F_SETLK,&fl)==1){ perror("fcntl"); exit(1); } printf("lockliberado.\n"); close(fd);}
Compileestecachorroycomienceeldesastreconunpardeventanas.Notequecuandounlockdemotieneunareadlock,otrasinstanciasdelprogramapuedenobtenersuspropiasreadlockssinproblema.Solocuandoseobtieneunawritelockesqueotrosprocesosnopuedenconseguirunacerraduradeningunaclase.Otracosaparanotaresquenosepuedeobtenerunwritelocksihayunareadlockenlamismaregindelarchivo.Elprocesoqueintenteconseguirlawritelockesperaraquelasreadlocksseanliberadas.Comoresultadodeestoesquepuedeobtenerunmontndereadlocks(porqueunareadlocknodetieneaotros
17
procesosqueobtienenreadlocks)ycualquierprocesoqueesperaporunawritelocksesentarallysemorirdehambre.Nohayningunareglaqueimpidaagregarmsreadlockssihayunprocesoqueesperaporunawritelock.Debetenercuidado.Sinembargo,enlaprctica,probablementeuseprincipalmentewritelocksparagarantizarelaccesoexclusivoaunarchivoporunacantidadcortadetiempomientrasestactualizndose;seeselusomscomndecerradurashastadondeyohevisto.
ConclusionesLascerradurasgobiernan.Aveces,sinembargo,podranecesitarmscontrolsobresusprocesosenunasituacindeproductorconsumidor.Porestarazn,debevereldocumentosobresistemasdesemforossisusistemasoportaasemejantebestia.Elloscumplenunafuncinequivalenteperomscompletaaladelosfilelocks.
PginasHPUXdelMANSinoejecutaHPUX,verifiquelassiguientespginaslocalesdelMAN!
fcntl() lockf() lseek() paraelcampol_whencedelastructflock open()
ColasdelmensajeEsaspersonasquenostrajeronSysVsehanvistoatacadasparaincluiralgunosdetallesdeIPCquesehanimplementadoenvariasplataformas(inclusoLinux,porsupuesto.)EstedocumentodescribeelusoyfuncionalidaddelosfabulososSysVydelasColasdemensaje!Comodecostumbre,yoquieroentregarlealgunaapreciacinglobalantesdeentrarenelterrenomspantanoso.UnacolademensajetrabajaenformasimilaraunaFIFO,perosoportaalgunafuncionalidadadicional.Tambintienelecturadestructivaperoselepuedeenviarcualquiercosa(nosolostrings).Nosonnodosdelfilesystemsinomemoria.Generalmentesesacanmensajesdelacolaenelordenenelquefueroncolocados.Sinembargo,haymanerasespecficasdearrancarciertosmensajesdelacolaantesdequeellosalcancenelfrentedelamisma.Estoescomocortarlalnea.
Entrminosdeuso,unprocesopuedecrearunanuevacoladelmensaje,opuedeconectarseaunaqueexista.Esporestoltimoquedosprocesospuedenintercambiarinformacinatravsdelamismacoladelmensaje.
UnacosamssobrelosSysVdeIPC:cuandoustedcreaunacoladelmensaje,estanosemarchahastaqueustedladestruya.Todoslosprocesosquelahanusadoalgunavezpuedendejarla,perolacolatodavaexistir.UnabuenaprcticaesusarloscomandosparaIPCsparaverificarsiquedancolasdelmensajesinusoquesimplementeestnmerodeando.Espreferiblequelasdestruyaconelcomandoipcrmaquequedenimproductivastodaslascolasdelsistema.
Dndeestmicola?Continuemos!Enprimerlugar,quiereconectarseaunacola,ocrearlasinoexiste.Lafuncinalaquedebellamarparalograrestoesmsgget()ydebehacerlodelsiguientemodo:
intmsgget(key_tkey,intmsgflg);
msgget()retornaelIDdelacolademensajesituvoxito,o1sifracas(yporsupuesto,seteaerrno)
Losargumentossonunpocoraros,peropuedenentenderseconunosgolpesenlafrente.Elprimero,alquellamamoskey,esunidentificadornicodelacolaconlaquesequiereconectaroquequierecrear.Cualquierotroprocesoquequieraconectarseaestacolatendrqueusarelmismoidentificador.
18
Elotroargumento,lediceamsgget()quhacerconlacolaencuestin.EstecamposecompletaconelresultadodehacerlaoperacinORentreIPC_CREATylospermisosparalacola.(Lospermisosdelacolasonigualesquelospermisosdearchivosnormaleslascolasasumenelidentificadordeusuarioydegrupodelprogramaquelacrea.)Unamuestradelallamadasedaenlasiguienteseccin.
"EsustedelKeyMaster?"Quesestacosasinsentidodelaclave?Cmocreamosuna?Bien,yaqueeltipokey_tesenrealidadunlong,ustedpuedeusarelnmeroquequiera.Perosiustedhardcodeaestenmeroyalgunosotrosprogramasnorelacionadoshardcodeanelmismonmeroperonecesitanotracola?.Lasolucinesusarlafuncinftok()quegeneraunaclaveenbaseadosargumentos.Estafuncinsirveparaidentificarunvocamentecolasdemensaje,semforosysharedmemoriesyseusadelsiguientemodo:
key_tftok(constchar*path,intid);Ok,estoestponindoseraro.Bsicamente,elpathtienequesereldeunarchivoqueesteprocesopuedeleer.Elotroargumento,elid,normalmenteseponesimplementeunchararbitrariocomoporejemplo'A'.Lafuncinftok()usainformacinsobreelarchivonombrado(comoelinodenumber,etc.)yelidparagenerarunaclaveprobablementenicaparamsgget().losprogramasquequierenusarlamismacoladebengenerarlamismaclave,paraloquedebenpasarlosmismosparmetrosaftok().Finalmente,eshoradehacerlallamada:#includekey=ftok("/home/beej/somefile",'b');msqid=msgget(key,0666|IPC_CREAT);
Enelejemploanterior,yopuselospermisosparalacolaen666(orwrwrw).Yahoratenemoselmsqidqueseusarparenviaryrecibirmensajesdelacola.
EnviandoalacolaUnavezquesehaconectadoalacolademensajeusandomsgget(),estlistoparaenviaryrecibirmensajes.Primero,elenvo:Cadamensajesehaceendospartesquesedefinenenlaestructurastructmsgbufcomosedefiniensys/msg.h:structmsgbuf{longmtype;charmtext[1];
};Elcampomtypeseusadespusalrecuperarmensajesdelacola,ypuedeponerseacualquiernmeropositivo.Elcampomtexteseldatoqueseagregaralacola.que?!Puedeponerslounaarraydebytesenunacoladelmensaje?!Bien,noexactamente.Ustedpuedeusarcualquierestructuraquequieraparaponermensajesenlacola,contaldequeelprimerelementoseaunlong.Porejemplo,podramosalmacenartodaclasedeestructuras:structpirata_msgbuf{longmtype;/*mustbepositive*/charname[30];chartipo_de_nave;intnotoriedad;intcrueldad;intvalor_del_botin;};Ok,peroquecmopasamosestainformacinaunacoladelmensaje?Larespuestaessimple,misamigos,:slousandomsgsnd():
intmsgsnd(intmsqid,constvoid*msgp,size_tmsgsz,intmsgflg);
19
msqideselidentificadordelacolademensajedevueltopormsgget().msgpesunpunteroaldatoquequiereponerenlacola.msgszeseltamaoenbytesdelosdatosaagregaralacola.
Finalmente,msgflglepermiteponeralgunosflagscomoparmetrosoptativosqueignoraremosporahoraparaponindoloa0.Yaquestelcdigoquemuestraunadenuestrasestructuraspirataagregndosealacoladelmensaje:#include
key_tkey;intmsqid;structpirata_msgbufpmb={2,"L'Olonais",'S',80,10,12035};
key=ftok("/home/beej/somefile",'b');msqid=msgget(key,0666|IPC_CREAT);
msgsnd(msqid,&pmb,sizeof(pmb),0);/*pongoeldatoenlacola*/
Recordarlaverificacindeerroresconelvalorretornadodetodasestasfunciones.Oh,s:notaqueyoarbitrariamentehepuestoelcampodelmtypea2.Esoserimportanteenlaprximaseccin.
RecepcindesdelacolaAhoraquetenemosaltemidopirataFrancisL'Olonaispegadoennuestracoladelmensaje,cmolosacamos?Comopuedeimaginar,hayuncolegademsgsnd():esmsgrcv().Queimaginativo.Unallamadaamsgrcv()seraalgoas:
#include
key_tkey;intmsqid;structpirata_msgbufpmb;/*dondesealojarL'Olonais*/key=ftok("/home/beej/somefile",'b');msqid=msgget(key,0666|IPC_CREAT);
msgrcv(msqid,&pmb,sizeof(pmb),2,0);/*losacadelacola!*/
Hayalgonuevoquenotarenlallamadamsgrcv()lel2!Qusignifica?Aquestlasintaxsisdelallamada:
intmsgrcv(intmsqid,void*msgp,size_tmsgsz,longmsgtyp,intmsgflg);
El2queespecificamosenlallamadaeselmsgtyppedido.Recuerdequepusimoselmtypearbitrariamentea2enmsgsnd()enlaseccindeestedocumento,paraquefueraelqueserecuperedelacola.Laconductademsgrcv()puedesermodificadadrsticamenteescogiendoelmsgtyppositivo,negativoocero:msgtyp Efectossobremsgrcv()cero Recuperanelprximomensajeenlacola,sintenerencuentasumtype.Positivo Obtieneelprximomensajeconunmtypeigualalmsgtypespecificado
Negativo Recuperaelprimermensajeenlacolacuyocampomtypeesmenoroigualalvalorabsolutodelargumentomsgtyp.Tabla1.Efectodelargumentomsgtypsobremsgrcv().
Enlamayoradeloscasossimplementequerrelprximoenlacola,sinimportarquvalortienemtype.Entalcaso,pondrelparmetromsgtypa0.
DestruccindeunacoladelmensajeLlegaelmomentodedestruirunacoladelmensaje.Comodijeantes,lascolasquenoseeliminendeambularnhastaqueexplcitamentelasquite;esimportantequehagaestoparaquenomalgastelos
20
recursosdelsistema.Ok,ustedusestacoladelmensajetodoelda,yestenvejeciendo.Poresoquiereborrarla.Haydosmaneras:
1.UsarloscomandosIPCdeUnixparaobtenerunalistadelascolasdemensajedefinidasyluegousarelcomandoipcrmparaanularlacola.
2.escribirunprogramaparahacerloustedmismo.
Amenudo,laltimaopcineselmsapropiada,yapodraquerersuprogramaparaliberarlacolaenalgnmomentouotro.Parahacerestorequierelaintroduccindeotrafuncin:msgctl().Lasintaxismsgctl()es:intmsgctl(intmsqid,intcmd,structmsqid_ds*buf);
Porsupuesto,msqideselidentificadordelacolaobtenidomsgget().Elargumentoimportanteescmdquelediceamsgctl()cmocomportarse.Puedeserunavariedaddecosas,peroslovamosahablarsobreIPC_RMIDqueseusapararemoverlacoladelmensaje.ElargumentobufpuedeponerseaNULLparalospropsitosdeIPC_RMID.Digaquetenemoslacolaquecreamosanteriormentepararegistraralospiratas.Ustedpuededestruiresacolaemitiendolasiguientellamada:#include..msgctl(msqid,IPC_RMID,NULL);
Ylacoladelmensajenoestms.
ProgramasdemuestraIncluirunmanojodeprogramasquesecomunicarnusandocolasdelmensaje.Elprimero,kirk.cagregamensajesalacola,yspock.closrecupera.Aquestaelcdigofuenteparakirk.c:
#include#include#include#include#include#include
structmy_msgbuf{longmtype;charmtext[200];};
intmain(void){structmy_msgbufbuf;intmsqid;key_tkey;
if((key=ftok("kirk.c",'B'))==1){perror("ftok");exit(1);}
if((msqid=msgget(key,0644|IPC_CREAT))==1){perror("msgget");exit(1);}printf("Ingreselineasdetexto,^Dparasalir:\n");
21
buf.mtype=1;/*realmentenonoscuidamosenentecaso*/while(gets(buf.mtext),!feof(stdin)){if(msgsnd(msqid,(structmsgbuf*)&buf,sizeof(buf),0)==1)perror("msgsnd");}if(msgctl(msqid,IPC_RMID,NULL)==1){perror("msgctl");exit(1);}
return0;}
Eltrabajodekirkeseldepermitirleingresarlneasdetexto.Cadalneaestatadadentrodeunmensajeyseagregaalacolademensajequeserluegoledaporspock.
Lacondicinwhile(gets(s),!feof(stdin))quieredecirmasomenosquemientrasporstdinnollegueunEOF......haceloqueestentrellaves.Funcionaenbaseadosfunciones:gets(s)queleestdin(teclado)ydevuelveenselpunteroalarraydecaracteresledos,yfeof(filedescriptor),queevalalalecturarealizadadesdeunstreamdedatosydevuelve1cuandoseleeelcarcterFindeArchivo.stdindevuelveEOFcuandosepulsaENTER.DemodoqueelconjuntohacequemientrasnosepulseENTERsealmacenenloscaracteresledosenlastrings.CuandopulseENTERdevuelveelpunteroalastring.....
Aquestelcdigoparaspock.c:
#include#include#include#include#include#include
structmy_msgbuf{longmtype;charmtext[200];};
intmain(void){structmy_msgbufbuf;intmsqid;key_tkey;
if((key=ftok("kirk.c",'B'))==1){/*mismaclavequekirk.c*/perror("ftok");exit(1);}
if((msqid=msgget(key,0644))==1){/*seconectaalacola*/perror("msgget");exit(1);}printf("spock:listopararecibirmensajes,capitn.\n");
for(;;){/*Spocknuncatermina!*/if(msgrcv(msqid,(structmsgbuf*)&buf,sizeof(buf),0,0)==1){perror("msgrcv");exit(1);}printf("spock:\"%s\"\n",buf.mtext);
22
}
return0;}
Notequeenspock,lallamadaamsgget(),noincluyelaopcindeIPC_CREAT.Lopusimosenkirkparacrearlacoladelmensajeyaque,delocontrario,spockdevolverunerrorsikirknohubieracreadolacolaantes.
Avisoloquepasacuandoestejecutandolasdosenventanasseparadasymataunouotroprograma.Luegointentecorrerdoscopiasdekirkodoscopiasdespockparadarseunaideadeloquepasacuandotienedoslectoresodosescritores.Otrademostracininteresanteesejecutarkirk,entreenunmanojodemensajes,luegoejecutarspockyversiesterecuperatodoslosmensajes.Simplementeenviarmensajesentreestosdosprogramitasleayudaraadquirirunacomprensindequesucederealmente.
ConclusionesHaymuchomsacercadelascolasdelmensajequeloqueestaguadidcticapuedepresentar.AsegresedeverlaspginasdelMANparaconocerelrestodelascosasquepuedehacersobretodoconlafuncinmsgctl().Tambinencontrarallmsopcionesconlasquepuedecontrolarelmanejodelascolasmediantemsgsnd()ymsgrcv()silacolaestllenaovaca,respectivamente.
PginasHPUXdelMANSinoejecutaHPUX,verifiquesuspginaslocalesdelMAN!
ftok() ipcs ipcrm msgctl() msgget() msgsnd()
SemforosRecuerdalosfileslocking?Bien,lossemforospuedenpensarsecomomecanismosfilelockingdetipoadvisorymuygenricos.Ustedpuedeusarlosparacontrolarelaccesoalosarchivos,memoriacompartida,y,bueno,casicualquiercosaquequiera.Lafuncionalidadbsicadeunsemforoesqueustedpuedaponerlo,verificarlo,oesperarlohastaqueselibereparaluegotomarlo("testnset").Sinimportarcuancomplejoseaelmaterialquesiguepermiterecordaresostresfuncionamientos.Estedocumentoproporcionarunaapreciacinglobaldefuncionalidaddelsemforo,yacabarconunprogramaqueusasemforosparacontrolarelaccesoaunarchivo.(Estatarea,reconocida,podramanejarsefcilmenteconfilelocking,peroesunbuenejemployaqueesmsfcilparadarleunaidearespectoaloqueesmemoriacompartidaosharedmemory).
AgarrandoalgunossemforosConlosSysVdeIPCs,ustednoagarrasolosemforos;sinojuegosdesemforos.Puede,porsupuesto,agarrarunjuegodesemforosqueslotengaunsemforoenl,peroelpuntoesquepuedetenerunmontndesemforosmuertosconsolocrearunjuegodeellos.Cmocreaeljuegodesemforos?Sehaceconunallamadaalafuncinsemget()queretornaelidentificadordelsemforo(deahoraenadelantellamadoelsemid):
#include
intsemget(key_tkey,intnsems,intsemflg);
23
Culeslaclave?Esunnicoidentificadorqueesusadopordiferentesprocesosparaidentificarestejuegodesemforos.(Estaclavelageneraftok()delmismomodoquesedescribieneldocumentodeColasdeMensaje.)
Elprximoargumento,nsems,es(comolosupuso)elnmerodesemforosenestejuego.Elnmeroexactodependedelsistema,peroprobablementeestentre500y2000.Sinecesitams(!desgraciadoinsaciable!),simplementeconsigaotrojuegodesemforos.
Finalmente,elargumentosemflg.Estodiceasemget()quepermisosdebendarseenelnuevojuegodesemforos,siustedestcreandounnuevojuegoosimplementequiereconectarseaunoqueexisteyotrascosasqueleparezcan.Paracrearunnuevojuego,puedehacerlaoperacinORalospermisosdeaccesoconIPC_CREAT.
Aquhayunejemplodecmollamaraftok()paragenerarunaclaveycrearunsetde10semforosconpermisosen666(orwrwrw):#include#include
key_tkey;intsemid;
key=ftok("/home/beej/somefile",'E');semid=semget(key,10,0666|IPC_CREAT);Felicitaciones!Hacreadounnuevojuegodesemforos!DespusdeejecutarelprogramapuedecomprobarloconloscomandosdeIPC.(Noseolvidedequitarlosconipcrm!)
semop():elpoderAtmico!Todaslasoperacionesquecreansemforos,losseteanohacenconellostestnsetusanlasystemcallsemop().Estasystemcallesdepropsitogeneralysufuncinesdictadaporunaestructurastructsembufqueselepasa:structsembuf{ushortsem_num;shortsem_op;shortsem_flg;};
Porsupuesto,sem_numeselnmerodesemforoseneljuegoqueustedquieremanipular.Luego,sem_opesloqueustedquierehacerconesesemforo.Estoasumesignificadosdiferentesquedependiendodequesem_opseapositivo,negativo,ocero,comosemuestraenlasiguientetabla:sem_op Quesucede
Positivo Elvalordesem_opsesumaalvalordelsemforo.Asescmounprogramausaunsemforoparamarcarunrecursocomotomado..
Negativo
Sielvalorabsolutodesem_opesmayorqueelvalordelsemforo,lallamadaalprocesosebloquearhastaqueelvalordelsemforoalcancealvalorabsolutodesem_op.Finalmente,elvalorabsolutodesem_opserestaralvalordelsemforo.Asescmounprocesoliberaunrecursoguardadoporelsemforo.
Cero Esteprocesoesperarhastaqueelsemforoencuestinalcanceel0.Tabla1.Elvalordesem_opysusefectos.
Bsicamente,loqueesthaciendoescargaraunaestructurastructsembufconalgunosvaloresquequiereyluegollamaasemop()as:
intsemop(intsemid,structsembuf*sops,unsignedintnsops);
24
Elargumentodesemideselnmeroobtenidodelallamadaasemget().Elsiguienteargumentoessops,queesunpunteroparalaestructurasembufqueustedllenconsuscomandosalsemforo.
Sinembargo,siquiere,puedehacerunarraydeestructurassembufsparahacerunmanojodeoperacionesdesemforoalmismotiempo.Lamaneraenquesemop()sabequequierehaceresmedianteelargumentonsopquedicecuntasestructurassembufleestenviando.Sislotieneunapongaun1comoargumento.Uncampodelaestructurasembufquenohemencionadoeselcamposem_flgquelepermitealprogramaespecificarflagsquemodificanmucholosefectosdelallamadaasemop().UnodeestosflagseselIPC_NOWAITque,comoelnombresugiere,hacequelallamadaasemop()retorneelerrorEAGAINsiencuentraunasituacinenlaquenormalmentesebloqueara.Estoesbuenoparalassituacionesenlaquenecesitahacerunpollingparaversipuededisponerdeunrecurso.
OtroflagmuytilesSEM_UNDO.Estehacequesemop()grabe,enciertomodo,elcambioquelehizoalsemforo.Cuandoelprogramatermina,elkerneldesharautomticamentetodosloscambiosquefueronmarcadosconelflagSEM_UNDO.Porsupuesto,suprogramadeberhaceralgomejorparadesasignarrecursosmarcadosusandoelsemforo,peroavecesestonoesposiblecuandosuprogramaobtieneunSIGKILLolesucedealgnotropercance.
DestruyendounsemforoHaydosmanerasdelibrarsedeunsemforo:unoesusarelcomandoipcrmdeUnix.Elotroesatravsdeunallamadaasemctl()conlosargumentosapropiados.
AhoraintentocompilarestecdigotantobajoLinuxcomobajoHPUX,peroencuentroquelassystemcallsdifieren.Linuxpasaununinsemunasemctl(),peroHPUXusaensulugarunalistadeargumentosvariables.Intentarproporcionaruncdigoclaroparaambos,perodarprioridadalestiloLinux,yaqueassedescribeenellibrodeStevenllamadoUnixNetworkProgramming.
AqulauninsemunalestiloLinuxestilo,juntoconlallamadaasemctl()quedestruirelsemforo:
unionsemun{intval;/*usadasolamenteparaSETVAL*/structsemid_ds*buf;/*paraIPC_STATeIPC_SET*/ushort*array;/*usadaparaGETALLySETALL*/};intsemctl(intsemid,intsemnum,intcmd,unionsemunarg);
Simplementenotequelauninsemunproporcionaunamaneradepasartantounint,comounaestructurasemid_dsounpunteroaushort.EstaeslaflexibilidadquelaversindeHPUXdesemctl()lograconunalistadelargumentosvariables:
intsemctl(intsemid,intsemnum,intcmd,.../*arg*/);
EnHPUX,enlugardepasarunauninsemun,pasasimplementecualquiervalor(enteroodeotraclase).ParaobtenermsinformacinsobresusistemaespecficodebechequearlaspginasdelMAN.Noobstante,elcdigodeaquenadelanteseralestiloLinux.Dndeestbamos...?Ohsdestruyendounsemforo.Bsicamente,debeobtenerelIDquellamamossemidparaelsemforoquequiere.cmddebecargarseconIPC_RMIDquelediceasemctl()quequiteestejuegodesemforos.LosdosparmetrossemnumyargnotienenningnsignificadoenelcontextodeIPC_RMIDypuedeponersecualquiercosaensulugar.Aqusedaunejemploparaunjuegodesemforos:unionsemundummy;intsemid;..
25
semid=semget(...);..semctl(semid,0,IPC_RMID,dummy);
AdvertenciaCuandocreaalgunossemforostodosellosestninicializadosencero.Esunapenayaquesignificaqueestntodosmarcadoscomoasignados;entoncesrequieredeotrallamada(asemop()oasemctl()paramarcarloscomolibres).Qusignificaesto?Bien,significaquelacreacindeunsemforonoesatmica(enotraspalabras,noesunprocesodelunsolopaso).Sidosprocesosestnintentandocrear,inicializarousarunsemforoalmismotiempopodradarseunacondicindecompetenciaentreprocesos.
Voyareferirmeaesteproblemaenelcdigodelprogramademuestrateniendounsoloprocesoquecreaeinicializaelsemforo.Elprocesoprincipalapenasloaccede,peronuncalocreaolodestruye.Simplementeestprocesoeselguardia.Stevensserefiereaestocomola"fallafatal"delsemforo.
ProgramasdemuestraHaytresdeellosytodoscompilarnbajoLinux(yHPUXconmodificaciones).Elprimero,seminit.c,creaeinicializaelsemforo.Elsegundo,semdemo.c,pretenderealizaralgunosfileslockingusandoelsemforo,enunademostracinmuybuenacomoeneldocumentodefilelocking.Finalmente,semrm.cseusaparadestruirelsemforo(denuevo,podrausarseipcrmparalograresto.)Laideaesejecutarseminit.cparacrearelsemforo.Intenteusarlosipcsdelalneadecomandosparaverificarqueexistan.Luegoejecutarsemdemo.cenunpardeventanasyvercmoactanrecprocamente.Finalmente,usesemrm.cparaquitarelsemforo.Ustedpodraprobartambinquitandoelsemforomientrassemdemo.cestcorriendosloparaverqueclasedeerroressegeneran.
Aquestseminit.c(ejecutesteprimero!):
#include#include#include#include#include#includeintmain(void){key_tkey;intsemid;unionsemunarg;
if((key=ftok("semdemo.c",'J'))==1){perror("ftok");exit(1);}
/*creaunsetdesemforoscon1semforo:*/if((semid=semget(key,1,0666|IPC_CREAT))==1){perror("semget");exit(1);}
/*inicializaelsemforo#0a1:*/arg.val=1;if(semctl(semid,0,SETVAL,arg)==1){perror("semctl");exit(1);}
26
return0;}
Aquestsemdemo.c:
#include#include#include#include#include#include
intmain(void){key_tkey;intsemid;structsembufsb={0,1,0};/*seteadosparaasignarrecursos*/
if((key=ftok("semdemo.c",'J'))==1){perror("ftok");exit(1);}
/*tomaeljuegodesemforoscreadosporseminit.c:*/if((semid=semget(key,1,0))==1){perror("semget");exit(1);}
printf("Presionereturnparalockear:");getchar();printf("Intentandolockear...\n");
if(semop(semid,&sb,1)==1){perror("semop");exit(1);}
printf("Lockeado.\n");printf("Presionereturnparadeslockear:");getchar();
sb.sem_op=1;/*paraliberarrecurso*/if(semop(semid,&sb,1)==1){perror("semop");exit(1);}
printf("deslockeado\n");
return0;}Aquestsemrm.c:#include#include#include#include#include#include
intmain(void){key_tkey;
27
intsemid;unionsemunarg;
if((key=ftok("semdemo.c",'J'))==1){perror("ftok");exit(1);}/*tomaelsetdesemforoscreadoporseminit.c:*/if((semid=semget(key,1,0))==1){perror("semget");exit(1);}
/*loremueve:*/if(semctl(semid,0,IPC_RMID,arg)==1){perror("semctl");exit(1);}
return0;}
Noesdivertido!estoyseguroquequedartemblandodespusdejugarcontodoeldaconestematerialdesemforos.
ConclusionesHmmm.Piensoquehesubestimadolautilidaddesemforos.Leaseguroquesonmuymuymuytilesenunasituacindelconcurrencia.Amenudosonmsrpidosquelosfilelockingregulares,tambin.Tambinpuedeusarlosenotrascosasquenosonarchivos,comoensegmentosdeMemoriaCompartida!Dehecho,adecirverdad,avecesesdifcilvivirsinellos.Siemprequetengacorriendomltiplesprocesosatravsdeunaseccincrticadelcdigonecesitasemforos.
PginasHPUXdelMANSinoejecutaHPUX,verifiquelassiguientespginaslocalesdelMAN!
ipcrm ipcs semctl() semget() semop()
SegmentosdeMemoriacompartidaLobuenodelossegmentosdememoriacompartidaesquesonloqueparecen:unsegmentodememoriaqueescompartidoentrelosprocesos.Quierodecir,pienseenelpotencialdeesto!Ustedpodraasignarunbloquedeinformacindeljugadorparaunjuegodemltiplesjugadoreselcualpodraseraccedidoporcadaprocesoavoluntad!Diversin,diversin,diversin.Hay,comodecostumbre,msdetallesmolestosqueconsiderar,peroalalargasontodosbastantefcilesdesuperar.Vea,soloseconectaalsegmentodememoriacompartida,yobtieneunpunteroalamemoria.Puedeleeryescribiradondeapuntaytodosloscambiosqueustedhacesernvisiblesparatodoslosdemsqueestnconectadosalsegmento.Nohaynadamssimple.
28
CreandoelsegmentoyconectandoDeformasimilaraotrosdelos5sistemasdeIPC,unsegmentodememoriacompartidasecreayseconectaporvadeunallamadaaunafuncin,enestecasoashmget():
intshagged(key_tkey,size_tsize,intshmflg);Silafuncinshmget()resultexitosadevuelveunidentificadorparaelsegmentodememoriacompartida.ElargumentokeydebecrearsedelmismomodoquesemostrcuandoeneldocumentodeColasdeMensajeyusandoftok().Elprximoargumento,sizeeseltamaoenbytesdelsegmentodememoriacompartida.Finalmente,ashmflgdebendarseelresultadodelaoperacinORentrelospermisosdelsegmentoeIPC_CREATsiquierecrearelsegmento,peropuedeserporotraparte0.(NohacemellaespecificarIPC_CREATcadavezqueseconectesielsegmentoyaexiste.)Aquhayunallamadadeejemploquecreaunsegmentode1Kcon644comopermisos(rwrr):key_tkey;intshmid;
key=ftok("/home/beej/somefile3",'R');shmid=shmget(key,1024,0644|IPC_CREAT);Perocmorecibeustedunpunteroparamanejarlosdatosidentificadosporshmid?Larespuestaestenlallamadaashmat(),enlasiguienteseccin.
tchameconsiguiendounpunteroalsegmentoAntesdequepuedausarunsegmentodememoriacompartidatienequeatacharsealpormediodeunallamadaalafuncinshmat():
void*shmat(intshmid,void*shmaddr,intshmflg);
Qusignificatodoesto?Bien,shmideselidentificadordelamemoriacompartidaquerecibidelallamadaashmget().Elsiguienteparmetroesshmaddrquepuedeutilizarparadecirleashmat()qudireccinespecficaquiereusarsiesquequiereescogerunadireccinydebeponerlosimplementeen0parapermitirlealSOqueescojaladireccinporusted.Finalmente,shmflgpuedenponerseaSHM_RDONLYsisloquiereleerdel,o0enotrocaso.Aquestunejemplomscompletodecmoobtenerunpunteroaunsegmentodememoriacompartida:
key_tkey;intshmid;char*data;
key=ftok("/home/beej/somefile3",'R');shmid=shmget(key,1024,0644|IPC_CREAT);data=shmat(shmid,(void*)0,0);
Ustedtieneelpunteroalsegmentodememoriacompartida!Notequeshmat()devuelveunpunteroavoid,ynosotrosestamostratndolo,enestecaso,comounpunteroachar.Ustedpuedetratarlocomoloquequiera,dependiendodequeclasededatostieneall.Lospunterosaarraysdeestructurassoloaceptanqueselostrateasnadams.Tambin,esinteresantenotarqueshmat()retorna1encasodefracaso.Peroobtieneun1deunpunteroavoid?Simplementehagauncastingdurantelacomparacinenelchequeodeerrores:data=shmat(shmid,(void*)0,0);if(data==(char*)(1))perror("shmat");
Todoloquetienequehacerahoraescambiarlosdatosalestilodeunpunteronormal.Hayalgunosejemplosdemuestrasenlaprximaseccin.
29
LecturayEscrituraPermtamedecirlequetieneelpunteroalosdatosdelejemploanterior.Esunpunteroachar,porqueestaremosleyendoyescribiremoscharsconl.Adems,porsimplicidad,permtamesuponerqueelsegmentodememoriacompartidade1Kcontieneunstringnulocomoterminacin.Nopodrasermssencillo.Puestoquehayjustounstringall,podemosimprimiralgocomoesto:
printf("lamemoriacompartidacontiene:%s\n",data);
Ypodramosguardaralgoendichamemoriatanfcilmentecomohaceresto:printf("Ingreseunstring:");gets(data);
Porsupuesto,comodijeantes,puedetenerotrosdatosallademsdeslochars.Yoestoyusndolossimplementecomounejemplo.YoasumirqueustedestbastantefamiliarizadoconlospunterosenCyquepodrtratarconcualquiertipodedatosquequieraponerall.
DesatachandoyborrandosegmentosCuandoustedsehaceconunsegmentodememoriacompartida,suprogramadebedesatacharsedelusandounallamadaalafuncinshmdt():
intshmdt(void*shmaddr);
Elnicoargumento,shmaddr,esladireccinquerecibidelshmat().Lafuncindevuelve1encasodeerrory0encasodexito.Cuandosedesatachadelsegmento,stenosedestruye.Nisequitacuandotodossedesatachandel.Tienequedestruirloutilizandounallamadaespecficaashmctl(),demanerasimilaralasllamadasdecontrolparacualquieradelosotrosSysVdeIPC:shmctl(shmid,IPC_RMID,NULL);
Lallamadaanterioranulaelsegmentodememoriacompartidayasumequenadiemsseatachaal.Lafuncinshmctl()hacemuchomsqueesto.
Comosiempre,puededestruirelsegmentodememoriacompartidadesdelalneadecomandosusandoelcomandoipcrmdeUnix.Asegresetambindenodejarningnsegmentodememoriacompartidasinusaryaquepermanecerderrochandorecursosdelsistema.CadaunodelosSysVdeIPCpuedeserobjetodeloscomandosipcqueustedposee.
ProblemasdeconcurrenciaCulessonproblemasdeconcurrencia?Bien,desdequetieneprocesosmltiplesquemodificanelsegmentodememoriacompartida,esposiblequeciertoserrorespudieransurgircuandoocurrequeseactualizaalsegmentoenformasimultnea.Esteaccesocoexistentecasisiempreesunproblemacuandotienemltiplesescritoresaunobjetocompartido.
Lamaneradetratarestoesusarsemforosparalockearelsegmentodememoriacompartidamientrasunprocesoestescribindolo.(Aveceslacerraduraabarcatantolaescrituracomolalecturasobrelamemoriacompartida,dependiendodeloqueseesthaciendo.)
Unaverdaderadiscusindeconcurrenciaestmsalldelalcancedeestedocumento,yustedpodraquererconsultarunodelosmuchoslibrosqueataenaesteasunto.Yoledirapenasesto:siempiezaatenerinconsistenciasrarasensusdatoscompartidoscuandoconectadosomsprocesosal,bienpuedetenerunproblemadeconcurrencia.
30
CdigodemuestraAhoraqueloheiniciadoentodoslospeligrosdelaccesocoexistenteaunsegmentodememoriacompartidasinusarsemforos,ledarunademostracindecmoeseso.Yaquestanoesunaaplicacincuyamisinseacrtica,yesimprobablequeustedaccedaalosdatoscompartidosenformasimultaneaconcualquierotroproceso,porsimplicidad,omitirlossemforos.
Esteprogramahaceunadedoscosas:siustedloejecutasinlosparmetrosdelneadecomandos,imprimeelcontenidodelsegmentodememoriacompartida.Siledaunparmetrodelneadecomandosguardaelparmetroenelsegmentodememoriacompartida.Aquestelcdigoparashmdemo.c:
#include#include#include#include#include#include
#defineSHM_SIZE1024/*segmentodememoriacompartidade1K*/
intmain(intargc,char*argv[]){key_tkey;intshmid;char*data;intmode;
if(argc>2){fprintf(stderr,"uso:shmdemo[dato_a_escribir]\n");exit(1);}
/*hacelaclave:*/if((key=ftok("shmdemo.c",'R'))==1){perror("ftok");exit(1);}
/*seconectaalsegmento(yposiblementelocrea):*/if((shmid=shmget(key,SHM_SIZE,0644|IPC_CREAT))==1){perror("shmget");exit(1);}
/*seatachaalsegmentoparaconseguirunpunteroaeste:*/data=shmat(shmid,(void*)0,0);if(data==(char*)(1)){perror("shmat");exit(1);}
/*leeomodificaelsegmento,enbasealalneadecomandos:*/if(argc==2){printf("escribirparaelsegmento:\"%s\"\n",argv[1]);strncpy(data,argv[1],SHM_SIZE);}elseprintf("elsegmentocontiene:\"%s\"\n",data);
/*sedesatachadelsegmento:*/if(shmdt(data)==1){perror("shmdt");exit(1);
31
}
return0;}
Normalmente,unprocesoseatacharalsegmentoycorrerduranteunratomientrasotrosprogramasestncambiandoyleyendoelsegmentocompartido.Estosecuidaparaqueelprocesoveaunaactualizacindelsegmentoyloscambiosseanvistostambinporlosotrosprocesos.Nuevamente,porsimplicidad,elcdigodemuestranolohace,peroustedpuedevercmolosdatossoncompartidosentrelosprocesosindependientes.Tampocohayaquningncdigopararemoverelsegmento,debeasegurarsedehacerlousted.
PginasHPUXdelMANSinoejecutaHPUX,verifiquelassiguientespginaslocalesdelMAN!
ftok() ipcrm ipcs shmat() shmctl() shmdt() shmget()
ArchivosmapeadosenmemoriaLlegaelmomentoenelqueustedquiereleeryescribirlosarchivosparaquelainformacinseacompartidaentrelosprocesos.Pienseenellosdeestamanera:dosprocesosqueabrenelmismoarchivo,losdosloleenyloescribenycompartenaslainformacin.Elproblemaesqueavecesesunsufrimientohacertodosesosfseek()s.Noseramsfcilsipudierasimplementemapearunaseccindelarchivoenmemoriayconseguirleunpunteroparaello?Entoncespodrasimplementeusarunpunteroaritmticoparaaccederalosdatosdelarchivo(ymodificarlos).
Bien,estoesexactamenteloqueunarchivomapeadoenmemoria.Ytambinesmuyfcildeusar.Unaspocasysimplesllamadas,mezcladasconunaspocasreglassimples,yustedestarencondicionesdemapearmemoriacomoloco.
TrazadodelmapadememoriaAntesdemapearunarchivoenlamemoria,necesitaobtenerunfiledescriptorparaserusadoporlasystemcallopen():
intfd;
fd=open("mapdemofile",O_RDWR);Enesteejemplo,hemosabiertoelarchivoparaaccesodellectura/escritura.Ustedpuedeabrirloencualquiermodoquequiera,perotienequecoincidirconelmodoespecificadoenelparmetroprotparalallamadaalafuncinmmap()quesehardebajo.Paramapearunarchivoenmemoriaustedusalasystemcallmmap()queestdefinidadelsiguientemodo:
void*mmap(void*addr,size_tlen,intprot,intflags,intfildes,off_toff);
Quemontndeparmetros!Aqusedetallantodosellos:
32
addr
staesladireccinenlaquequeremosmapearelarchivo.Lamejormaneradeusarloesponerloa0(caddr_t)ypermitirlealSOescogerloporusted.SiustedledicequeuseunadireccinquealSOnolegusta(porejemplo,sinoesunmltiplodeltamaodeunapginadememoriavirtual),ledarunerror.lenEsteparmetroeslalongituddelosdatosquequeremosmapearenlamemoria.stapuedesercualquierlongitudqueustedquiera.(Aparte:siellennounmltiplodeltamaodeunapginadememoriavirtual,obtendruntamaodebloquequedependedelredondeadodeesetamao.Losbytesextrasern0,ycualquiercambioquelehagaaellosnomodificarelarchivo.)
prot
Elargumentoproteccinlepermiteespecificarqutipodeaccesotieneesteprocesoparalaregindememoriamapeada.stapuedesurgirdelresultadodelaoperacinORentrelossiguientesvalores:PROT_READ,PROT_WRITE,yPROT_EXEC,parapermisosdelectura,escritura,yejecucin,respectivamente.Elvalorespecificadoaqudebeserequivalentealmodoespecificadoenlasystemcallopen()queesusadaparaobtenerelfiledescriptor.
flags
Aqusimplementehaybanderasmiscelneasquepuedenponerseparalasystemcall.UstedquerrponerleelvalorMAP_SHAREDsiplaneacompartirsuscambiosalarchivoconotrosprocesos,odelocontrarioMAP_PRIVATE.Siustedpusieraesteltimovalor,suprocesoconseguirunacopiadelareginmapeada,paraquenosereflejarcualquiercambioqueustedhaceenelarchivooriginalas,otrosprocesosnopodrnverlos.AqunohablaremosenabsolutosobreMAP_PRIVATEyaquenotienemuchoqueverconIPC.
fildes
Aquesquedondeponeesefiledescriptorqueantesabri.offsteeseloffsetdentrodelarchivoporelqueustedquiereempezaramapear.Unarestriccin:stedebeserunmltiplodeltamaodeunapginadememoriavirtual.Estetamaodelapginapuedeobtenerseconunallamadaagetpagesize().
Encasodeerrormmap(),comolohabrsupuesto,devolver1ysetearlavariableerrno.Deotromododevuelveunpunteroalcomienzodelosdatosmapeados.
Haremosunademostracincortaquemapealasegundapginadeunarchivoenlamemoria.Primeroloabriremosconopen()paraobtenerlosfiledescriptors,luegousaremosgetpagesize()paraobtenereltamaodeunapginadememoriavirtualyusarestevalortantoparalenycomoparaoff.Deestamanera,empezaremosmapeandoalasegundapginaparaunalongituddeunapgina.(EnmiLinuxbox,eltamaodelapginaesde4K.)
#include#include#include
intfd,pagesize;char*data;
fd=fopen("foo",O_RDONLY);pagesize=getpagesize();
33
data=mmap((caddr_t)0,pagesize,PROT_READ,MAP_SHARED,fd,pagesize);
Unavezqueestecdigohayacorridopuedeaccederalprimerbytedelaseccindelarchivomapeadaqueusadata[0].Notequeaquhaymuchaconversindetipos.Porejemplo,mmap()devuelveacaddr_t,perolotratamoscomounchar*.Bien,elhechoesquenormalmenteaesecaddr_tselodefineparaqueseaunchar*,queparalamayoradeloscasosestbien.TambindebenotarquehemosmapeadoelarchivoPROT_READparateneraccesosoloparalectura.Cualquieresfuerzoporescribiralosdatos(data[0]='B',porejemplo)causarunaviolacindelasegmentacin.SiustedquiereaccesoalosdatosparalecturayescrituraabraelarchivocomoO_RDWRconprotpuestoaPROT_READ|PROT_WRITE.
DesmapeandoelarchivoPorsupuestoquehayunafuncinmunmap()paraarchivosmapeadosenmemoria:
intmunmap(caddr_taddr,size_tlen);
Estafuncinsimplementedesmapealareginapuntadaporaddr(devueltapormmap())conunalongitudlen(lamismapasadaammap()).Lafuncinmunmap()devuelve1encasodeerrorysetealavariableerrno.
Unavezquetienedesmapeadoalarchivo,cualquieresfuerzoporaccederalosdatosatravsdelviejopunteroproducirunafaltadesegmentacin.Ustedhasidoadvertido!
Unanotafinal:elarchivoserdesmapeadoautomticamentesisuprogramatermina,porsupuesto.
Problemasdeconcurrencia,otravez?!Sitieneprocesosmltiplesquemanejanlosdatosconcurrentementeenelmismoarchivo,podraestarenproblemas.Podratenerquelockearelarchivoousarsemforospararegularelaccesoalmismomientrasunprocesoaccedeal.MireeldocumentodeMemoriaCompartidaparaobtenermsinformacinacercadelosproblemasdeconcurrencia.
UnamuestrasimpleBien,denuevoestiempodelcdigo.Yotengoaquunprogramadedemostracinquemapeasupropiafuenteenlamemoriaeimprimeelbytequeseencuentraaunoffsetespecificadoporlineadecomandos.
Elprogramarestringelosdesplazamientosqueustedpuedeespecificaralrangode0lalongituddelarchivo.Lalongituddelarchivoseobtieneatravsdeunallamadaalstat()quseguramentejamslahabrvistoantes.Estafuncindevuelveunaestructurallenadeinformacindelarchivo,entrelaquehayuncampoquecontieneeltamaoenbytes.Bastantefcil.Aquestelcdigofuenteparammapdemo.c:
#include#include#include#include#include#include#include#include
intmain(intargc,char*argv[]){intfd,offset;char*data;structstatsbuf;
if(argc!=2){
34
fprintf(stderr,"uso:eloffsetmmapdemo\n");exit(1);}
if((fd=open("mmapdemo.c",O_RDONLY))==1){perror("open");exit(1);}
if(stat("mmapdemo.c",&sbuf)==1){perror("stat");exit(1);}
offset=atoi(argv[1]);if(offsetsbuf.st_size1){fprintf(stderr,"mmapdemo:eloffsetdebeestarentre0y%d\n",\sbuf.st_size1);exit(1);}if((data=mmap((caddr_t)0,sbuf.st_size,PROT_READ,MAP_SHARED,\fd,0))==(caddr_t)(1)){perror("mmap");exit(1);}
printf("elbytealoffset%des'%c'\n",offset,data[offset]);
return0;}
Estoestodo.Compileestoyejecteloconalgunalneadelcomandocomo:$mmapdemo30elbytealoffset30es'e'
Yolodejaraustedescribirprogramasnuevosqueusenalgunasdeestassystemcalls.
ConclusionesLosarchivosmapeadosenmemoriapuedensermuytiles,sobretodoensistemasquenosoportanlossegmentosdememoriacompartida.Dehecho,losdossonmuysimilares.(Losarchivosmapeadosenmemoriasontambinenviadosaldisco,loquepuedeserunaventaja)Conlosfileslocking,conlossemforosoconlosarchivosmapeadosenmemorialosmltiplesprocesospuedencompartirfcilmentelosdatos.
PginasHPUXdelMANSinoejecutaHPUX,verifiquelassiguientepginaslocalesdelMAN!
getpagesize() mmap() munmap() open() stat()
UnixSocketsRecuerdaalasFIFOs?Recuerdaquepodaenviardatosenunasoladireccin,comounpipe?Noseragrandiosopoderenviardatosenambasdireccionescomoconunsocket?
35
Bien,noesperems,porquelarespuestaestaqu:UnixSockets!Encasodequeustedtodavaestpreguntndosequeesunsocketledigoqueesqueunpipedecomunicacinbidireccionalquepuedeusarseparacomunicarenunavariedaddedominios.UnodelosmscomunesdominioscomunicadosporsocketsesInternet,peronosotrosnodiscutiremosestoaqu.NosotrostrataremoslosdominiosdeUnix,esdecir,lossocketsquepuedenusarseentrelosprocesosdeunmismosistemaUnix.
LossocketsdeUnixusan,enmuchoscasos,lasmismasfuncionesllamadasporlossocketsdeInternetperoyonovoyadescribirtodaslasfuncionesllamadasendetallesinoquesolotratarlasqueusedentrodeestedocumento.Siladescripcindeunaciertallamadaesdemasiadovaga(osiustedquiereaprendermssobrelossocketsdeInternet),porfavorvealaBeej'sGuidetoNetworkProgrammingUsingInternetSocketsparatenerunainformacinmsdetallada.
ReseaComodijeantes,lossocketsdeUnixsoncomoFIFOsbidireccionales.Sinembargo,todoslosdatosdelacomunicacinserntomadosdelainterfasedelossocketsenlugardelainterfasedelarchivo.AunquelossocketsdeUnixsonarchivosespecialesenfilesystems(comoFIFOs),ustednopodrutilizaropen()yread()usarsocket(),bind(),recv(),etc.Alprogramarconsocket,normalmentecrearprogramasservidoresyprogramasclientes.Elservidorpermanecerescuchandolasconexionesentrantesdelosclientesylosmanejar.EstoesmuysimilaralasituacinqueexisteentrelossocketsdeInternet,peroconalgunasdiferencias.Porejemplo,cuandodicequsocketdeUnixquiereusar(esdecir,elpathdelarchivoespecialqueeselsocket),recurriraunaestructurasockaddr_unlacualtienelossiguientescampos
structsockaddr_un{unsignedshortsun_family;/*AF_UNIX*/charsun_path[108];
}
staeslaestructuraquepasaralafuncinbind()queasociaunsocketdescriptor(unfiledescriptor)conunciertoarchivo(cuyonombreestelcamposun_path).
QuehacerparaserunServidorSinentrarendemasiadosdetalles,yoperfilarlospasosquenormalmentedaunprogramaparaserunservidor.Mientrasestoyenl,intentarllevaracaboun"ecodeservidor"(ecoserver)elquecualjustamenteretornaunecodecualquiercosaqueobtengadeunsockets.
Aquestnlospasosdelservidor:
1. Llamaalafuncinsocket():unallamadaasocket()conlosargumentosapropiadoscreaunUnixsocket
unsignedints,s2;structsockaddr_unlocal,remote;intlen;
s=socket(AF_UNIX,SOCK_STREAM,0);Elsegundoargumento,SOCK_STREAM,lediceasocket()quedebecrearunstreamsocket.Losdatagramsockets(SOCK_DGRAM)tambinsonsoportadoseneldominiodeUnix,peroaqusolovoyacubrirlosstreamsockets.
Paraloscuriososquequierantenerunabuenadescripcindelosdatagramsocketsdesconectados,verlaBeej'sGuidetoNetworkProgrammingqueseaplicanabsolutamentebienalossocketsdeUnix.Lonicoquecambiaesqueenlugardeusarunaestructurasockaddr_undebeusarunaestructurasockaddr_in
36
Unanotams:todasestasllamadasdevuelven1encasodeerroryseteanlavariableglobalerrnoparareflejarquealgosalimal.Asegresedehacerunchequeodeerrores.
2. Llamaalafuncinbind():Ustedrecibiunsocketdescriptordelallamadaasocket(),ahoradebeligarloaunadireccineneldominiodeUnix.(Esadireccin,comodijeantes,esunarchivoespecialeneldisco.)
local.sun_family=AF_UNIX;/*localesdeclaradaantesdesocket()^*/
local.sun_path="/home/beej/mysocket";unlink(local.sun_path);len=strlen(local.sun_path)+sizeof(local.sun_family);bind(s,(structsockaddr*)&local,len);
EstoasocialossocketsdescriptorsconladireccindelaUnix"/home/beej/mysocket".Notequellamamosalafuncinunlink()antesdebind()paraquitarelsocketsiyaexiste.UstedobtendrunerrorEINVALsielarchivoyaexista.
3. Llamaalafuncinlisten():Estainstruyealsocketparaqueescuchealasconexionesentrantesdelosprogramasclientes:
listen(s,5);Elsegundoargumento,5,eselnmerodeconexinentrantequepuedenestarenlacolaantesdequeustedejecutelallamadaalafuncinaccept().Sihaymuchasconexionesqueesperanseraceptadas,losclientesadicionalesgenerarnelerrorECONNREFUSED.
4. Llamaalafuncinaccept():Estoaceptarunaconexindeuncliente.Estafuncindevuelveotrosocketdescriptor!Eldescriptorviejotodavaestescuchandoalasnuevasconexiones,peroelnuevoseconectaalcliente:len=sizeof(structsockaddr_un);
s2=accept(s,&remote,&len);****************************Cuandoretornaaccept(),laestructuraremotesercargadaconelcontenidodelaestructurasockaddr_undelladoremoto,ylensercargadaconsulargo.Eldescriptors2seconectaalcliente,yestlistoparaenviarconlafuncinsend()yrecibirconlafuncinrecv(),comosedescribeenlaNetworkProgrammingGuide.
5. Manejalaconexinyretornahaciaatrs,alallamadaalafuncinaccept():Normalmenteustedquerrcomunicaralcliente(precisamenteharemosretornodelecodetodaslascosasquesenosenva),cierralaconexin,luegoejecutaunanuevallamadaalafuncinaccept().while(len=recv(s2,&buf,100,0),len>0)send(s2,&buf,len,0);
/*desdeaquvuelveatrsaaccept()*/
6. Cierralaconexin:Ustedpuedecerrarlaconexinllamandoaclose(),opormediodelallamadashutdown().
Contodoloquesedijo,aquestunprogramafuente,llamadoechos.c,parahacerunservidor.TodoloquehaceesesperarporunaconexinenunsocketdeUnix(llamado,enestecaso,"echo_socket").
#include#include#include#include#include#include#include
37
#defineSOCK_PATH"echo_socket"
intmain(void){ints,s2,t,len;structsockaddr_unlocal,remote;charstr[100];
if((s=socket(AF_UNIX,SOCK_STREAM,0))==1){perror("socket");exit(1);}
local.sun_family=AF_UNIX;strcpy(local.sun_path,SOCK_PATH);unlink(local.sun_path);len=strlen(local.sun_path)+sizeof(local.sun_family);if(bind(s,(structsockaddr*)&local,len)==1){perror("bind");exit(1);}
if(listen(s,5)==1){perror("listen");exit(1);}
for(;;){intdone,n;printf("esperandounaconexin...\n");t=sizeof(remote);if((s2=accept(s,(structsockaddr*)&remote,&t))==1){perror("accept");exit(1);}
printf("Conectado.\n");
done=0;do{n=recv(s2,str,100,0);if(n
QuehacerparaserunclienteAhoranecesitahacerunprogramaparapoderhablarconelservidoranterior.Ladiferenciadelcliente,esqueesmuchomsfcilporquenotienequehacerningunadelasmolestasllamadasalasfuncioneslisten(),oaccept().Estossonlospasosquerequiere:
1. Llamaralafuncinsocket()paraobtenerunUnixdomainsocketparacomunicarse.2. Prepararunaestructurasockaddr_unconladireccinremota(dondeelservidorestescuchando)y
llamaraconnect()conestacomounargumento.3. Asumiendoquenohuboerrores,ustedestarconectadoalladoremotoUsesend()yrecv()
paraalegrarsucorazn!
Culeselcdigoparahablarconelechoserveranterior?Nosepreocupenamigos,aquestechoc.c:
#include#include#include#include#include#include#include#defineSOCK_PATH"echo_socket"
intmain(void){ints,t,len;structsockaddr_unremote;charstr[100];
if((s=socket(AF_UNIX,SOCK_STREAM,0))==1){perror("socket");exit(1);}
printf("Intentandoconectarse...\n");
remote.sun_family=AF_UNIX;strcpy(remote.sun_path,SOCK_PATH);len=strlen(remote.sun_path)+sizeof(remote.sun_family);if(connect(s,(structsockaddr*)&remote,len)==1){perror("connect");exit(1);}
printf("Conectado.\n");
while(printf(">"),fgets(str,100,stdin),!feof(stdin)){if(send(s,str,strlen(str),0)==1){perror("send");exit(1);}
if((t=recv(s,str,100,0))>0){str[t]='\0';printf("echo>%s",str);}else{if(t
return0;}
Porsupuestoqueenelcdigodelcliente,notarquesolohayunaspocassystemcallsusadasparasetearalgunascosas:socket()yconnect().Dadoqueelclientenovaaejecutarlallamadaaccept()aceptandocualquierconexinentrante,nohaynecesidaddeescucharconlafuncinlisten().Porsupuestoqueelclientetambinusalasfuncionessend()yrecv()paratransferirdatos.
socketpair()pipesbidireccionalessuperrpidosQupasarasiustedquisieraunpipe(),peroquiereusarunosoloparaenviaryrecibirdatosemambossentidos?Yaquelospipessonunidireccionales(conexcepcionesenSYSV),nopuedehacerlo!Perosinembargohayunasolucin:useunUnixdomainsocket,yaqueellospuedenmanejardatosbidireccionales.PeroestoesmuydifcilPonertodoesecdigoconlisten()yconnect()ytodoesosloparapasardatosporambasvasPeroporqusuponequenolotiene
Hayunabellezadesystemcallconocidacomosocketpair()queesbastantebuenaparadevolverleunpardesocketsyaconectados!Nosenecesitaningntrabajoextraordinariodesuparte;yustedpuedeusarinmediatamenteestossocketsdescriptorsparalacomunicacinentreprocesos.
Porejemplo,hagamosdosprocesos.Elprimeroenvauncharalsegundo,yelsegundocambiaelcarcteramaysculayretorna.Aquhayuncdigosimpleparahacerjustamenteeso,llamadospair.c(sinchequeodeerroesparamayorclaridad):
#include#include#include#include#include#include
intmain(void){intsv[2];/*elpardesocketdescriptors*/charbuf;/*paraintercambiardatosentreprocesos*/
socketpair(AF_UNIX,SOCK_STREAM,0,sv);
if(!fork()){/*hijo*/read(sv[1],&buf,1);printf("hijo:lee'%c'\n",buf);buf=toupper(buf);/*loconvierteenmayscula*/write(sv[1],&buf,1);printf("hijo:envi'%c'\n",buf);
}else{/*padre*/write(sv[0],"b",1);printf("padre:envi'b'\n");read(sv[0],&buf,1);printf("padre:lee'%c'\n",buf);}return0;}
Efectivamente,estaunamaneracaradecambiaruncarcteramayscula,peroelhechoesquetieneunacomunicacinsimplequeutilizaloquerealmentenosimporta.
Unacosamsparanotaresquesocketpair()tomatantoaltipodomain(AF_UNIX)comoalsocket(SOCK_STREAM).stospuedentenercualquiervalorpermitidoparatodos,dependiendodequrutinasquieremanejarsucdigoenelkernelysiquierestreamsocketsodatagramsockets.
40
YoescogsocketsdeAF_UNIXporqueesteesundocumentodesocketsUnixysonmsrpidosquelosAF_INETsockets.
Finalmente,siquiereconocermsacercadeporquusowrite()yread()enlugardesend()yrecv().Bien,paraabreviar,porperezoso.Vea,usandostassystemcallsnotengoquedarlosargumentosdelosflagsparausarsend()yrecv()ysiempreporelcontrarioseteotodoestoacero.Porsupuesto,lossocketsdescriptorssonfiledescriptorscomocualquierotro,paraquerespondanbienalmanejodelassystemcalls
PginasHPUXdelMANSinoejecutaHPUX,verifiquelassiguientespginaslocalesdelMAN!
accept() bind() connect() listen() socket() socketpair() send() recv() read() write()
MsRecursosdeIPC
LibrosAqusedaunalistadelibrosquedescribenalgunosdelosprocedimientosqueheplanteadoenestagua,comotambindetallesespecficosdeUnix.
Bach,MauriceJ.TheDesignoftheUNIXOperatingSystem.NewJersey:PrenticeHall,1986.ISBN0132017997.
Stevens,RichardW.UNIXNetworkProgramming.NewJersey:PrenticeHall,1990.ISBN0139498761.
.AdvancedProgrammingintheUNIXEnvironment.AddisonWesley,1992.ISBN0201563177.
PginasHPUXdelMANEstassonlaspginasdelmanualHPUXdesusistemalocal.SiejecutaotrotipodeUnix,porfavormiresuspropiaspginasdelMAN,yaqueestasfuncionespodrannofuncionarensusistema.
accept() bind() connect() dup() exec() exit() fcntl() fileno() fork()
41
ftok() getpagesize() ipcrm ipcs kill kill() listen() lockf() lseek() mknod mknod() mmap() msgctl() msgget() msgsnd() munmap() open() pipe() ps raise() read() recv() semctl() semget() semop() send() shmat() shmctl() shmdt() shmget() sigaction() signal() signals sigpending() sigprocmask() sigsetops sigsuspend() socket() socketpair() stat() wait() waitpid() write()
42