guia beej ipcs es

Upload: lp-lupus

Post on 12-Oct-2015

71 views

Category:

Documents


10 download

TRANSCRIPT

  • 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