OVH Cloud OVH Cloud

Utilité DLL C++

102 réponses
Avatar
Korchkidu
Bonjour,

A partir de quand estimez vous que faire une DLL plutot qu'un LIB soit
int=E9ressant. En effet, utiliser des DLL fait des programmes plus
petits (entre autres) mais induit d'enormes contraintes au niveau de la
programmation. Donc d'apres moi, faire une DLL pour une bibliotheque de
petite taille n'est pas forcement une bonne idee...

Merci pour vos commentaires.
K=2E

10 réponses

Avatar
Arnold McDonald \(AMcD\)
Alain Gaillard wrote:

Tiens au fait toi qui parle volontiers "d'engueulo, de franche
camaraderie, de docte". Que dirais tu toi même de ta propre
intervention là ? LOL


Je connais la plupart des intervenants ici depuis des années. Eux aussi...

Faites ce que je dis mais ne faites pas ce que je fais, le principe
d'Arnold ?


Commence par lire les archives de ce NG avant de me chercher.

--
Arnold McDonald (AMcD)

http://arnold.mcdonald.free.fr/

Avatar
kanze
Alain Gaillard wrote:

g++ -fpei sous Linux, par exemple). Cet offset, c'est par
rapport à DS.


Hum, l'adressage par rapport à DS c'est du temps du 16 bits ça
non ?


C'était encore actuel avec le 80386. Sous Linux, gdb le montre
toujours ; je suppose donc qu'il existe encore.

Alors, je compile cette fonction, et je le lie dans une DLL.
Je charge la DLL dans deux processus différents, dont les
données, etc., se trouvent à des adresses différentes.
Comment fait Windows pour que ce bout de code fonctionne ?


Poussés par la curiosité et ton insistance, j'ai fait une dll
autour de ta fonction et désassemblé, ça donne:

__declspec( dllexport ) int ma_fonction()
{
10011300 push ebp
10011301 mov ebp,esp
10011303 sub esp,0C4h
10011309 push ebx
1001130A push esi
1001130B push edi
1001130C lea edi,[ebp-0C4h]
10011312 mov ecx,31h
10011317 mov eax,0CCCCCCCCh
1001131C rep stos dword ptr es:[edi]
static int i = 0 ;
return i ++ ;
1001131E mov eax,dword ptr [i (10017168h)]
10011323 mov dword ptr [ebp-0C4h],eax
10011329 mov ecx,dword ptr [i (10017168h)]
1001132F add ecx,1
10011332 mov dword ptr [i (10017168h)],ecx
10011338 mov eax,dword ptr [ebp-0C4h]
}
1001133E pop edi
1001133F pop esi
10011340 pop ebx
10011341 mov esp,ebp
10011343 pop ebp
10011344 ret


C'est marrant, je n'ai pas tout ça (mais je régarde l'assembleur
généré par l'option /Fa du compilateur). Mais l'essentiel ne
change pas : on charge eax avec une adresse constante. Ce qui
veut dire que si on va partager l'image (du code), il faut que
cette adresse soit la même chez tous les utilisateurs. Avec
d'avantages d'essais chez moi, je constate que ce n'est pas le
cas. Ce qui suggère fortement qu'en fait, l'image n'est pas
partagé, au moins pas tout le temps.

(En passant, comment fait-on d'ailleurs pour invoquer le
débogueur de VC++ ? Sur un programme existant, qui n'est pas
dans un projet.)

Il me semble qu'il est clair que ta variable static est dans
la zone des 2 go supérieure.


Tu crois ? Si l'adresse, c'est bien 0x10017168, elle n'est pas
dans les 2Go supérieur, mais dans le deuxième banc de 1Go, entre
1Go et 2Go.

J'ai modifié la fonction pour renvoyer l'adresse de i, que
j'affiche. Parfois, c'est 0x00231000, parfois 0x100217C0. Jamais
dans les 2Go supérieur, mais même pas toujours à la même
adresse, dans deux processus qui tournent au même temps.

Et je suppose qu'au démarrage de l'appli le système qui lit le
fichier PE y trouve une section "données" contenant au moins
ta variable static et que lors du chargement il crée une zone
en mémoire (une par process si plusieurs utilisent la dll)
contenant cette variable.


Jusque là, ça me semble fort probable ; je ne vois pas ce qu'il
pourrait faire d'autre. Le problème qui me tracassait, c'est
bien que si l'adresse de la variable est constante (dans l'image
du code -- le système pourrait bien l'ajuster lors du
chargement), il faudrait qu'elle soit la même dans tous les
processus utilisateurs.

En fait, j'ai l'impression qu'il ne partage l'image pas
toujours. La première fois qu'il charge la DLL, il alloue
l'espace d'adressage, en fixant alors la variable à une adresse
donnée. Ensuite, s'il y a une démande pour la même DLL, il
utilise l'image déjà présent SI (et seulement si) il peut
allouer la variable (enfin, l'espace de données propre à la DLL)
à la même adresse. Si le nouveau processus utilisateur a des
contraints qui l'empèche, il charge la DLL une deuxième fois,
avec un ajustement différent pour correspondre à l'adresse
différente des données.

Dans l'ensemble, ça me semble un bon compromis. Avec un peu de
bol, il arrivera bien à réutiliser l'image la plupart du temps,
sans pour autant payer le coût (important) en temps d'exécution
qu'a la solution Linux, où les données sont « position
independant » aussi. (Dans le cas où la fonction a un
traitement important, évidemment, le temps supplémentaire
d'exécution n'est pas mesurable. Mais avec une petite fonction
comme ceci, le fait qu'elle soit dans un .so pourrait bien
doubler son temps d'exécution.)

Je dis je suppose parce que pour être certain à 100% il
faudrait examiner le fichier binaire PE, mais je ne pense pas
me tromper toutefois. Quand l'appli va lire la valeur elle le
fait avec un pointeur qui vaut 10017198h dans l'exemple. Mais
cette adresse est toute virtuelle.


Tout à fait. Toujours est-il qu'il faut qu'il n'y a rien d'autre
à cette adresse dans l'espace d'adressage du processus. Chose
qui est difficile à garantir quand tu as des processus
différents qui se servent de la même DLL.

Quand elle est lue le système fait ce qu'il faut pour que la
variable appartenant au process soit lue effectivement en
mémoire physique. Et chaque process à sa propre copie de la
variable static i bien entendu.

En fait je ne comprends pas bien ce qui te défrise tant. On
dirait que tu raisonnes comme si l'adressage était à
l'ancienne en 16 bits (registre DS...) à moins que je ne
comprenne pas ta question.


J'avoue que j'ai assez de mal à l'expliquer clairement. Le
problème, si tu veux, c'est ce qui se passe si je démarre
processus A, qui utilise cette DLL, et qu'on mappe bien la
variable à l'adresse 0x100217C0. Qu'est-ce qui se passe ensuite
si un processus B démande la même DLL, et qu'il a déjà quelque
chose mappée à cette adresse (toujours, dans son espace
d'adressage logique à lui).

Ce que je peux constater, c'est que si je modifie la fonction
pour renvoyer l'adresse, ce n'est pas toujours la même adresse
qu'elle renvoie. Mais que pour avoir des adresses différentes,
il faut bien bricoler un peu pour s'arranger que l'adresse est
bien prise dans le deuxième processus ; par défaut, on a bien
la même adresse partout.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Alain Gaillard

Commence par lire les archives de ce NG avant de me chercher.


Mais non je ne te cherche pas. C'était juste une boutade.
En fait je suis navré que tu prennes tout ça comme ça.



--
Alain

Avatar
Vincent Burel
"kanze" wrote in message
news:
Alain Gaillard wrote:


Il me semble qu'il est clair que ta variable static est dans
la zone des 2 go supérieure.

Tu crois ? Si l'adresse, c'est bien 0x10017168, elle n'est pas
dans les 2Go supérieur, mais dans le deuxième banc de 1Go, entre
1Go et 2Go.



la base adress d'une DLL est part défaut 0x10000000 (alors que pour une
appli c'est 0x00400000 je crois).


J'ai modifié la fonction pour renvoyer l'adresse de i, que
j'affiche. Parfois, c'est 0x00231000, parfois 0x100217C0. Jamais
dans les 2Go supérieur, mais même pas toujours à la même
adresse, dans deux processus qui tournent au même temps.


Alors pour les DLL, le système n'a pas le choix, il doit pouvoir les reloger
dans la mémoire logique, puisque chaque DLL est mappé dans la mémoire du
processus, et que toutes les DLL ne peuvent pas etre au même endroit.

VB


Avatar
kanze
Vincent Burel wrote:
"kanze" wrote in message
news:
Vincent Burel wrote:
"kanze" wrote in message
news:
Vincent Burel wrote:
"James Kanze" wrote in message
news:
Alain Gaillard wrote:
ben c'est à dire que chaque "task" s'exécute dans un

environnement mémoire propre, et en fonction d'une table de
mappage mem-phy <-> mem logique spécique. Quand le programme
fait appel à une adresse 32 bit, c'est toujours dans un
contexte mémoire donné...

Certes, mais qu'est-ce que ça change ? Je parle bien ici de
l'adresse dans l'espace d'adressage d'un processus. Le
compilateur génère une adresse constante (par rapport à DS,
en tout cas).


ben les différentes hinstance de la DLL travaille avec les
mêmes adresses 32 bit (logiques), mais chaque appli à ses 6
segments sauvé dans sont swith context, et chaque segements
décrivent des zones différentes...


Oui, mais à l'intérieur d'un processus, on ne touche pas aux
régistres de segmentation.

Après, vous etes sur que le compilo est capable d'user
d'adresse vraiment constante ?


Je me suis peut-être mal expliqué. L'adresse est bien constante,
dans le sens qu'elle fasse partie de l'instruction, sans prendre
en compte d'autres choses qui peuvent varier lors de l'exécution
(le contenu des régistres, par exemple). Mais c'est sûr que le
compilateur ne le connaît pas, lors de la compilation ; il ne
génère que des informations pour que l'éditeur de liens (lors du
chargement, probablement) insère la bonne constante. N'empèche
que si on veut utiliser le même image exécutable dans plusieurs
processus, il faut que l'adresse ici soit la même dans chacun
des processus. (L'adresse logique, évidemment. Si l'adresse
physique était la meme, ça se saurait:-).)

genre un nombre codé en dur dans le code ASM ? ca me parait
bizarre !


C'est cependant le résultat final. L'instruction qui est générée
est un "MOV EAX,constante". C'est bien à l'éditeur de liens
d'insérer la constante, mais elle fait finalement partie de
l'image exécutable.

Tout ça c'est bien beau, même si j'imagine mal quelqu'un qui
ne le connais pas.

ha oui c'est vrai que tout le monde connais ca sur le bout des
doigt !


Il faut dire que la technologie de la mémoire virtuelle n'est
pas particulièrement récente. Je vois mal un ingenieur en
informatique qui ne sait pas à peu près comment ça fonctionne.

d'ailleurs ce thread en ai la preuve :-)


Ce thread est surtout la preuve que quand on a plusieurs niveaux
d'adressage, c'est difficile à rendre clair lequel dont on
parle. Il ne m'était jamais venu à l'esprit qu'on puisse
imaginer des réponses sur la base de la mémoire virtuelle, pour
la simple raison qu'il me semblait tellement évident comment ça
marchait, et que le problème se situait dans l'espace
d'adressage du processus. Mais en effet, je me suis pas fait
comprendre.

Et je constate également qu'à l'intérieur d'un processus, on
ne touche normalement pas à DS -- si la fonction renvoie
l'adresse, par exemple, il
renvoie bien l'offset, sans renseigner sur le segment


une appli user, ne gère par d'adresse seg:offset, il n'utilise
que l'offset.


En effet. C'est ce qu'il me semblait. (Pour une fois, Linux et
Windows sont d'accord. Il faut brider les applications, et ne
pas leur laisser accès à ce que peut faire le processeur.)

Seulement, c'est impossible que l'adresse logique soit
la même dans tous les processus.


pourquoi ? justement le mappage permet ca !


Si l'éditeur de liens a l'information des besoins de tous les
processus, il se peut qu'il trouve une solution, mais ce n'est
pas sûr. Mais de toute façon, il ne l'est pas. Si l'éditeur de
liens (ou le système) met la variable à l'adresse 0x1000CCA0,
par exemple, comment s'en sort-il quand le prochain processus
veut charger la DLL, et qu'il a déjà quelque chose à cette
adresse ? (On parle ici bien des adresses logiques à
l'interieur du processus. Et que de ces adresses.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
Alain Gaillard


Tu crois ? Si l'adresse, c'est bien 0x10017168, elle n'est pas
dans les 2Go supérieur, mais dans le deuxième banc de 1Go, entre
1Go et 2Go.


Heu.. hem je me suis trompé d'un zéro.


J'avoue que j'ai assez de mal à l'expliquer clairement. Le
problème, si tu veux, c'est ce qui se passe si je démarre
processus A, qui utilise cette DLL, et qu'on mappe bien la
variable à l'adresse 0x100217C0. Qu'est-ce qui se passe ensuite
si un processus B démande la même DLL, et qu'il a déjà quelque
chose mappée à cette adresse (toujours, dans son espace
d'adressage logique à lui).


Ce 0x100217C0 étant une adresse virtuelle, pourquoi l'adresse physique à
laquelle elle correspond devrait elle être la me^me pour chaque proc.

Elle peut l'être ou pas. Si l'adresse pointe sur du code alors ce sera
la même adresse physique et alors le code est partagé. Si l'adresse
pointe sur des données, alors l'adresse physique correspondante est
différente pour chaque processus qui a sa copie en mémoire physique des
données.

--
Alain

Avatar
Vincent Burel
"kanze" wrote in message
news:
Vincent Burel wrote:
"kanze" wrote in message
news:
Vincent Burel wrote:
"kanze" wrote in message
news:
Vincent Burel wrote:
"James Kanze" wrote in message
news:
Alain Gaillard wrote:



Si l'éditeur de liens a l'information des besoins de tous les
processus, il se peut qu'il trouve une solution, mais ce n'est
pas sûr. Mais de toute façon, il ne l'est pas. Si l'éditeur de
liens (ou le système) met la variable à l'adresse 0x1000CCA0,
par exemple, comment s'en sort-il quand le prochain processus
veut charger la DLL, et qu'il a déjà quelque chose à cette
adresse ? (On parle ici bien des adresses logiques à
l'interieur du processus. Et que de ces adresses.)


ha mais les DLL sont completement relogés dynamiquement au chargement...
faut demander à AMCD, mais le système est obligé de de le faire...

VB



Avatar
kanze
Jean-Marc Bourguet wrote:
Je préviens (surtout qu'il y a un crosspost où peu de gens
doivent me connaîre) que je ne connais rien à Windows.

Alain Gaillard writes:


g++ -fpei sous Linux, par exemple). Cet offset, c'est par
rapport à DS.



Je suppose qu'il s'agit de -fpic et pas -fpei.


C'est ce que j'avais crû aussi, mais c'est bien -fpie (et non
-fpei, qui est effectivement une faute de frappe). C'est -pic
avec Sun CC.

[...]
En fait je ne comprends pas bien ce qui te défrise
tant.


Je crains que tu ne comprennes pas la question.

Cela fait longtemps que j'ai fait de l'assembleur avec la
syntaxe MS -- mais il me semble que

mov eax,dword ptr [i (10017168h)]

c'est de l'adressage absolu: donc le code sait qu'i est à
l'adresse 10017168h et pas ailleurs. Il faut donc que i ait
la même adresse dans tous les process si le code est
réellement partagé. Comment Windows fait pour s'assurer que
toutes les DLL sont bien mappées pour où elles ont été
compilées?


Enfin, quelqu'un qui comprend ma question ? Je commençais à
devenir fou, ne sachant pas comment me rendre clair.

Soit le code n'est pas partagé et Windows fait de la
relocation au chargment (ce que fait Solaris quand un .so
n'est pas compilé en PIC -- on pourrait faire de la relocation
"intelligente" et essayer de charger la lib généralement au
même endroit, je doute que ce soit fait sous Solaris car le
chargeur est un exécutable sans droits supplémentaires et donc
sans influence sur les autres processus), soit il s'arrange
pour que 10017168h désigne des adresses différentes suivant
les endroits où il est appelé, et la seule façon que je vois,
c'est de modifier la base indiquée pas DS.


En fait, d'après quelques petits essais (pas si facile que ça,
quand on ne connaît pas la plate-forme, mais vu qu'il va falloir
que je l'apprenne un peu quand même...), Windows essayer
réelement de charger la lib toujours au même endroit.
Évidemment, il n'y réussit pas toujours : dans des cas des
programmes ultra-simples, comme les tests que j'ai fait, il y
réussit au moins qu'on fasse quelque chose exprès pour
l'empêcher. Dans les cas des applications réeles, je n'ai aucune
idée de son taux de réussite.

On dirait que tu raisonnes comme si l'adressage était à
l'ancienne en 16 bits (registre DS...) à moins que je ne
comprenne pas ta question.


Les segments sont toujours sur un x86 (enfin, plus en mode 64
bits si j'ai bien suivi), simplement on ne les utilise pas
beaucoup dans les OS répandus. Mais il y a des utilisations,
par exemple je crois savoir que Windows les utilise aussi pour
"pointer" vers les variables locales à des threads et les
utiliser pour les DLL, ça ne me semble pas stupide étant donné
que les x86 sont quand même mal foutus quand on veut écrire du
code PIC.


Tout est rélatif. J'ai déjà vu pas mal de processeurs pires. Où
les sauts étaient aux adresses absolues, et non rélatifs au IP.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
Vincent Burel
"Alain Gaillard" wrote in message
news:4505813b$0$25921$


Tu crois ? Si l'adresse, c'est bien 0x10017168, elle n'est pas
dans les 2Go supérieur, mais dans le deuxième banc de 1Go, entre
1Go et 2Go.


Heu.. hem je me suis trompé d'un zéro.


J'avoue que j'ai assez de mal à l'expliquer clairement. Le
problème, si tu veux, c'est ce qui se passe si je démarre
processus A, qui utilise cette DLL, et qu'on mappe bien la
variable à l'adresse 0x100217C0. Qu'est-ce qui se passe ensuite
si un processus B démande la même DLL, et qu'il a déjà quelque
chose mappée à cette adresse (toujours, dans son espace
d'adressage logique à lui).


Ce 0x100217C0 étant une adresse virtuelle, pourquoi l'adresse physique à
laquelle elle correspond devrait elle être la me^me pour chaque proc.

Elle peut l'être ou pas. Si l'adresse pointe sur du code alors ce sera
la même adresse physique et alors le code est partagé. Si l'adresse
pointe sur des données, alors l'adresse physique correspondante est
différente pour chaque processus qui a sa copie en mémoire physique des
données.


Vous allez voir qu'on va revenir à ma réflexion de départ : "Pour deux
process utilisant la meme DLL, je ne
suis pas certain que cette DLL ne soit pas physiquement chargé 2 fois..."
:-)

VB


Avatar
Alain Gaillard


Vous allez voir qu'on va revenir à ma réflexion de départ : "Pour deux
process utilisant la meme DLL, je ne
suis pas certain que cette DLL ne soit pas physiquement chargé 2 fois..."
:-)


Ben on l'a dit dès le départ non ? Elle n'est pas chargée physiquemenbt
deux fois en ce qui concerne le code.

--
Alain