c ++ - Hvad er den anbefalede måde for DLL'er at dele data?

Indlæg af Hanne Mølgaard Plasc

Problem



Flow



Jeg har en læser DLL skrevet i C++.

Jeg har også skribent DLL skrevet på nogle sprog (ikke i C++).

DLL'er kører i samme proces synkront.



  1. Læser DLL opkald forfatterens DLL API, GetData

  2. Forfatter DLL forbereder data enten ved at downloade det, uddrage det osv.

  3. Læser DLL læser og bruger dataene






Spørgsmål



Hvad er den anbefalede måde, hvorpå DLL'er kan dele data?





Tilgang 1



Reader DLL passere filvej argument til Writer DLL og læser data fra fil.


Ulemper



Jeg vil gerne undgå at skrive data til disk. Selvom det er den mest robuste løsning, vil jeg gerne udforske forskellige muligheder, da det ikke virker meget elegant for mig at skrive data til disk, når du ikke har brug for det disk.





Approach 2



Writer DLL vil allokere buffer på bunken og returnere en adresse og størrelse til læseren DLL.


Ulemper



Reader DLL skal frigøre hukommelsen. Er det muligt? slet hukommelsen efter adresse og størrelse?


Det er også en stor NO-NO tildeling og frigivelse af buffer over moduler/sprog





Tilnærmelse 3



Adskil GetData () til to opkald.



  1. Læser DLL-opkald GetDataSize ()

  2. Læser DLL allokere buffer og send adressen til Writer DLL

  3. Writer DLL fyldes buffer

  4. Reader DLL bruger buffer

  5. Læser DLL frigør buffer



Dette er den acceptable WINAPI tilgang.


Ulemper



Jeg antager, at Writer DLL er i stand til at kende størrelsen af ​​dataene før skrivning, men det er ikke altid tilfældet.





Tilnærmelse 4



Brug Windows-filkortlægning


Ulemper



Lignende ulemper ved tilnærmelse 2 & 3.



  • Hvem vil oprette filmapping?

  • Hvem vil afkorte?

  • Filkortlægning har ingen dynamisk størrelse. Du skal definere størrelsen, når du opretter den.


Bedste reference


DLL'erne løber alle i samme proces og adresserum. Så de kan dele alle data direkte i hukommelsen. Udfordringen er kun, hvordan man giver adgang til dataene, især hvis du bruger forskellige sprog.



  • Mulighed 1 er let, fordi du bare skal sende det fælles filnavn til læseren. Men hvorfor denne overhead? Der er en lettere strengvariant : Hvis du formår at sende et filnavn som en streng, kan du også lade forfatteren serialisere dataene i en streng og sende den til læseren

  • Mulighed 2 er mere sart. Det er ok, hvis hukommelsen er allokeret/afsat på samme side af DLL'en, eller hvis du allokerer din buffer ved hjælp af Windows API. Ellers kan det være vanskeligt, fordi hukommelsesallokering passerer DLL-barriererne med vanskeligheder (ikke på grund af adresserummet, men på grund af risikoen for at bruge forskellige dynger og forskellige tildelings-/frigivelsesrutiner). Desuden kan du ikke være sikker på, at opkaldsprogrammet styrer C ++-objektets livscyklus korrekt (hvis du bruger noget RAII-design på C ++-siden). Så dette er kun en mulighed, hvis læseren forvalter bufferens livscyklus:



    • caller beder læseren om at tildele, så ringer giver forfatteren adressen til bufferen, så ringer opkaldslæseren igen for at behandle dataene og frigive bufferen.

    • Fast størrelse af buffer er acceptabel, dvs. størrelsen af ​​daa er kendt.


  • Mulighed 3 er valgmulighed 2 gjort godt

  • Mulighed 4 har stadig disk overhead, hvis du bruger kortfil I/O, med et yderligere spørgsmål: kan du kortlægge to gange samme fil i samme proces? Hvis du bliver fristet af denne mulighed, skal du se et andet udsnit af den stringsbaserede variant, som jeg foreslog til valgmulighed 1 ovenfor: Den delte streng spiller rollen som hukommelseskortlægning uden ulejligheden af ​​filen. [17]



Strengevarianten synes et nemt alternativ til at hoppe over sprogbarrierer med komplekse datastrukturer. Næsten alle sprog har strenge. Producenten kan bygge sin streng uden at vide størrelsen af ​​data på forhånd. Endelig, selvom strings styres forskelligt på tværs af sprog, er der altid en måde at få adgang til dem i skrivebeskyttet. [18]


Men min foretrukne variant ville være at organisere hele sagen på en måde, at forfatteren (eller hovedprogrammet som mediator) kalder aflæsningens behandlingsfunktioner efter behov (når dele af dataene er tilgængelige), at levere data som argumenter for en veldefinerede typer til at fungere opkald.

Andre referencer 1


Bemærk: Når vi taler om at videregive data mellem to forskellige sprog, vil jeg antage, at vi taler om 'rå' data (primitive typer, POD'er og co.), der ikke 'behøver ingen særlig behandling ved destruktion; Hvis dette ikke er tilfældet, bedes du fortælle mig i kommentarerne.



  1. Selvfølgelig muligt, men jeg ville ikke overveje det, medmindre desperat. De to dll'er lever i samme virtuelle adresserum, så de kan dele data direkte i hukommelsen uden at skulle gå gennem disk.

  2. Gennemføreligt og rutinemæssigt udført det problem, du generelt skal arbejde rundt, er at ofte 'standard' bunke i et givet modul er privat 1 så allokering fra en og frigivelse fra den anden er en stor no-no. Der er to typiske måder at implementere dette på:



    • Gå gennem en bunke, der sikkert er tilgængelig for begge moduler; I Win32 finder du ofte LocalAlloc/LocalFree (eller andre Win32 API-tilvejebragte heap primitives) anvendt til dette, da de logisk er 'under' alle bruger-tilstandskoder og giver en delt Håb er tilgængelig for alle modulerne i den nuværende proces, så den ene side ved, at den skal tildele ved hjælp af LocalAlloc, den anden side ved, at disse data skal fordeles ved hjælp af LocalFree; alt fungerer fint;

    • tildelingsmodulet giver også en deallokeringsfunktion til den hukommelse, den tildeler; klientkoden ved, at det, hvad den modtog tildelt efter modul A, skal frigøres ved hjælp af funktionen A\_free(). Dette vil igen nok kun indpakke din sprogfordelingsfunktion, der skal bruges som modstykke til de tildelinger du gør i eksporterede funktioner 'Business Logic'. Af den måde kan det være nyttigt at have en A\_malloc() for at markere de tildelinger, der forventes frigivet af A\_free() - selv om de kan være almindelige malloc/free i dag kan du være interesseret i at ændre dette senere.


  3. Rutinemæssigt gjort også; ofte i Win32 API'er er der en speciel indkaldelsesformular, der gør det muligt at hente den nødvendige størrelse til at tildele; det kan være besværligt at bruge eller implementere, hvis en sådan størrelse ikke kan beregnes nemt uden faktisk at forsøge at gøre, hvad funktionen skal gøre, eller hvis sådan størrelsen svinger (Win32 API'erne til at hente processerdata kommer til at tænke på, hvor du måske skal løkke med stigende tildelinger, hvis dataene, der hentes, rent faktisk stiger mellem et opkald og det andet).

  4. Kan gøres, selvom jeg aldrig har set det gjort for procesdata, og overhead på tildeling bliver større end nogen 'normal' heapfunktion, men intet som at skrive til fil, generelt er det mere besværligt end LocalAlloc/LocalFree løsningen uden nogen særlig gevinst, så jeg ville ikke genere det.



Personligt går jeg med option 2 - det er trivielt at implementere og kræver ikke store ændringer i, hvordan du normalt skriver disse ting i C - den eneste forskel er, at du skal bruge et bestemt antal tildelinger/deallokering fungerer, når du arbejder med disse data.


En ekstra mulighed, der kommer i betragtning, er at få din funktion til at tage tildelingsfunktionen som en tilbagekoblingsparameter (og muligvis også en dellocation-funktion parameter, hvis det er nødvendigt for din algoritme - dynamisk voksende arrays kommer til at tænke) vær den, der ringer til at levere den, så den kaldte DLL vil allokere med uanset bunken, som den, der ringer op, mest kan lide.





Noter




  1. Selv om det kan deles, f.eks. hvis de to moduler forbinder dynamisk mod samme C-runtime, er det sikkert det; OTOH, hvis de to moduler er skrevet på forskellige sprog, er det meget usandsynligt.