Wikilivres frwikibooks https://fr.wikibooks.org/wiki/Accueil MediaWiki 1.45.0-wmf.5 first-letter Média Spécial Discussion Utilisateur Discussion utilisateur Wikilivres Discussion Wikilivres Fichier Discussion fichier MediaWiki Discussion MediaWiki Modèle Discussion modèle Aide Discussion aide Catégorie Discussion catégorie Transwiki Discussion Transwiki Wikijunior Discussion Wikijunior TimedText TimedText talk Module Discussion module Bambara/Relations familiales 0 2709 744670 717147 2025-06-13T15:31:52Z 2A01:CB18:1150:4600:C5FF:3372:4F9:EE19 Correction de "si ils" en "s'ils" 744670 wikitext text/x-wiki {{bambara}} '''si''' : famille, lignée '''Beau papa''' '''masa si''': lignée royale '''fa''' : père '''i fa ka kéné?''': ton père va bien? '''ba''' : mère '''i ba ka kéné?''' : ta mère va bien? '''mo-kè''' : grand-père '''mo-muso''' : grand-mère ;'''den''' : enfant (par rapport aux parents); ;denw :enfants (prononciation: ''de-un'') ;'''démisen''' : (par rapport à un adulte) '''denkè''' : fils '''denmuso''' : fille '''dogomuso''': jeune sœur ; jeune cousine '''dogokè''': jeune frère ; jeune cousin '''koromuso''': sœur aînée ; cousine aînée '''korokè''': frère aîné ; cousin aîné Il n'existe pas de mots précis pour nommer des relations de sang (à vérifier). On appellera le fils aîné d'un oncle ou d'un ami proche de la famille : korokè. La famille a ici un sens large et ne se limite pas aux liens de l'hérédité. Les oncles et les tantes sont considérés comme pères et mères. '''fatkè fitini''' : petit papa, oncle '''bamuso fitini''' : petite maman, tante Alors, au Mali, lorsque deux frères deviennent pères, alors leurs enfants deviennent frère et sœur ; pour ces enfants ils ont deux pères, mais s'ils ont une sœur ce sera leur tante ; les enfants de cette sœur sont leurs neveux et nièces pour ces frères et leurs cousins pour leur enfant. exemple alors Mamadou, Ibrahim, Fatou et Aminata sont frères et sœurs. Mamadou Ibrahim Fatou Aminata Bakari frère Mariam cousin Ousmane frère Coumba Oumar sœur Safi cousine Aly sœur Kaya [[Catégorie:Bambara (livre)|Relations familiales]] 0p5ec8cg8enkgz85m4pctbvwa0a8gqu Japonais/Vocabulaire/Nombres 0 5021 744724 742530 2025-06-14T06:24:06Z 2A01:CB05:8BC9:BF00:B0BD:AC6D:8808:A312 /* Les classificateurs */ 744724 wikitext text/x-wiki [[Image:Books-aj.svg aj ashton 01.svg|right|70px]] {|class="wikitable" ! Français ! [[Japonais/Kanji|Kanji]] ! [[Japonais/Kana|Kana]] ! [[Japonais/Romaji|Rōmaji]] |- | Zéro | [[wikt:零|零]]/[[wikt:〇|〇]] | れい/ゼロ | rei/zero |- | Un | [[wikt:一|一]] | いち | ichi |- | Deux | [[wikt:二|二]] | に | ni |- | Trois | [[wikt:三|三]] | さん | san |- | Quatre | [[wikt:四|四]] | し/よん | shi/yon |- | Cinq | [[wikt:五|五]] | ご | go |- | Six | [[wikt:六|六]] | ろく | roku |- | Sept | [[wikt:七|七]] | しち/なな | shichi/nana |- | Huit | [[wikt:八|八]] | はち | hachi |- | Neuf | [[wikt:九|九]] | く/きゅう | ku/kyū |- | Dix | [[wikt:十|十]] | じゅう | jū |- | Onze | [[wikt:十一|十一]] | じゅういち | jūichi |- | Douze | [[wikt:十二|十二]] | じゅうに | jūni |- | Treize | [[wikt:十三|十三]] | じゅうさん | jūsan |- | Vingt | [[wikt:二十|二十]] | にじゅう | nijū |- | Vingt-et-un | [[wikt:二十一|二十一]] | にじゅういち | nijūichi |- | Trente | [[wikt:三十|三十]] | さんじゅう | sanjū |- | Trente-cinq | [[wikt:三十五|三十五]] | さんじゅうご | sanjūgo |- | Quarante | [[wikt:四十|四十]] | よんじゅう | yonjū |- | Cinquante | [[wikt:五十|五十]] | ごじゅう | gojū |- | Soixante | [[wikt:六十|六十]] | ろくじゅう | rokujū |- | Soixante-dix | [[wikt:七十|七十]] | ななじゅう | nanajū |- | Quatre-vingts | [[wikt:八十|八十]] | はちじゅう | hachijū |- | Quatre-vingt-dix | [[wikt:九十|九十]] | きゅうじゅう | kyūjū |- | Cent | [[wikt:百|百]] | ひゃく | hyaku |- | Mille | [[wikt:千|千]] | せん | sen |- | Dix-mille | [[wikt:万|万]] | まん | man |- | Cent-mille | [[wikt:十万|十万]] | じゅうまん | jūman |- | Un million | [[wikt:百万|百万]] | ひゃくまん | hyakuman |- | Un milliard | [[wikt:十億|十億]] | じゅうおく | jūoku |- | Un billion | [[wikt:一兆|一兆]] | ひっちょう | itchō |- | Un billiard | [[wikt:千兆|千兆]] | せんちょう | senchō |- | Un trillion | [[wikt:百京|百京]] | ひゃっけい | hyakkei |- | Un trilliard | [[wikt:十垓|十垓]] | じゅうがい | jūgai |- | Un quadrillion | [[wikt:一𥝱|一𥝱]] | いちじょ | ichijo |- | Un quadrilliard | [[wikt:千𥝱|千𥝱]] | せんじょ | senjo |- | Un quintillion | [[wikt:百穣|百穣]] | ひゃくっじょう | hyakujō |- | Un quintilliard | [[wikt:十溝|十溝]] | じゅうっこう | jūkkō |- | Un sextillion | [[wikt:一澗|一澗]] | いっかん | ikkan |- | Un sextilliard | [[wikt:千澗|千澗]] | せんっかん | senkan |- | Un septillion | [[wikt:百正|百正]] | ひゃくせい | hyakusei |- | Un septilliard | [[wikt:十載|十載]] | じゅうっさい | jūssai |- | Un octillion | [[wikt:一極|一極]] | いちごく | ichigoku |- | Un octilliard | [[wikt:千極|千極]] | せんごく | sengoku |- | Un nonillion | [[wikt:百恒河沙|百恒河沙]] | ひゃくごうがしゃ | hyakugōgasha |- | Un nonilliard | [[wikt:十阿僧祇|十阿僧祇]] | じゅうあそうぎ | jūasōgi |- | Un décillion | [[wikt:一那由他|一那由他]] | いちなゆた | ichinayuta |- | Un décilliard | [[wikt:千那由他|千那由他]] | せんなゆた | sennayuta |- | Un undécillion | [[wikt:百不可思議|百不可思議]] | ひゃくふかしぎ | hyakufukashigi |- | Un undécilliard | [[wikt:十無量大数|十無量大数]] | じゅうむりょうたいすう | jūmuryōtaisū |} Certains nombres comportent des modifications euphoniques. {|class="wikitable" ! Français ! [[Japonais/Kanji|Kanji]] ! [[Japonais/Kana|Kana]] ! [[Japonais/Romaji|Rōmaji]] |- | Trois cents | [[wikt:三百|三百]] | さんびゃく | sanbyaku |- | Six cents | [[wikt:六百|六百]] | ろっぴゃく | roppyaku |- |Huit cents | [[wikt:八百|八百]] | はっぴゃく | happyaku |- | Trois mille | [[wikt:三千|三千]] | さんぜん | sanzen |- |Huit mille | [[wikt:八千|八千]] | はっせん | hassen |- |} {|class="wikitable" ! Français ! [[Japonais/Kanji|Kanji]] ! [[Japonais/Kana|Kana]] ! [[Japonais/Romaji|Rōmaji]] |- | neuf-cent-quarante-deux milliards quatre-cent-soixante-et-un millions cent-douze mille soixante-deux (942 461 112 062) | 九千四百二十四億六千百十一万二千六十二 | きゅうせんよんひゃくにじゅうよん おくろくせんひゃくじゅういちまんにせんろくじゅうに | kyūsen yon hyaku nijū yon oku rokusen hyakujū ichiman nisen roku jūni |} (Pour le 4, 「し」 est moins utilisé parce qu'il se prononce de la même façon que « mort » ([[wikt:fr:死|死]]). Il y a également un souci avec le 7, 「しち」 pouvant signifier « terre de mort » ([[wikt:fr:死地|死地]]). Idem pour le 9, 「く」 pouvant correspondre au mot « souffrance » ([[wikt:fr:苦|苦]]).) ==Chiffres japonais== Contrairement aux chiffres d'origine chinoise utilisés habituellement, le japonais possède ses propres chiffres qui ne sont utilisés que dans certains cas particuliers. (Avec le classificateur '''つ''' par exemple.) {|class="wikitable" ! [[Japonais/Kanji|Kanji]] ! [[Japonais/Kana|Kana]] ! [[Japonais/Romaji|Rōmaji]] |- | 一 | ひと | hito |- | 二 | ふた | futa |- | 三 | み | mi |- | 四 | よ | yo |- | 五 | いつ | itsu |- | 六 | む | mu |- | 七 | なな | nana |- | 八 | や | ya |- | 九 | ここの | kokono |- | 十 | とお | tō |- |卄、廾、廿 |はた |hata |- |卅、丗 |みそ |miso |- |卌 |よそ |yoso |- | |いそ |iso |- | |むそ |muso |- | |ななそ |nanaso |- | |やそ |yaso |- | |ここのそ |kokonoso |- |百 |もも |momo |- |千 |ち |chi |- |万 |よろず | yorozu |- |兆 |きざ |kiza |} ==Les classificateurs== En japonais, quand on compte, on doit impérativement utiliser un classificateur indiquant le type d'objet compté. Par exemple : 「二車」 (« ni kuruma ») est faux ; il faut dire 「二台車」 (« nidai kuruma ») (« deux voitures ») Voici des tableaux contenant quelques classificateurs : {|class="wikitable" !Chiffre !人 ''nin'' (personne) !匹 ''hiki'' (petits animaux) !頭 ''tō'' (gros animaux, papillons) !羽 ''wa'' (oiseaux, lapins) !枚 ''mai'' (objets plats) !本 ''hon'' (objets cylindriques) !冊 ''satsu'' (livres) !台 ''dai'' (machines) !杯 ''hai'' (contenu d'un récipient) !輪 ''rin'' (objets circulaires) !固 ''ko'' (objets petits ou moyens) !つ ''tsu'' (choses concrètes ou abstraites) |- |一 |一人 (hitori) |一匹 (ippiki) |一頭 (ittō) |一羽 (ichiwa) |一枚 (ichimai) |一本 (ippon) |一冊 (issatsu) |一台 (ichidai) |一杯 (ippai) |一輪 (ichirin) |一個 (ikko) |一つ (hitotsu) |- |二 |二人 (futari) |二匹 (nihiki) |二頭 (nitō) |二羽 (niwa) |二枚 (nimai) |二本 (nihon) |二冊 (nisatsu) |荷台 (nidai) |二杯 (nihai) |二輪 (nirin) |二個 (niko) |二つ (futatsu) |- |三 |三人 (sannin) |三匹 (sanbiki) |三頭 (santō) |三羽 (sanba) |三枚 (sanmai) |三本 (sanbon) |三冊 (sansatsu) |三台 (sandai) |三杯 (sanbai) |三輪 (sanrin) |三個 (sanko) |三つ (mittsu) |- |四 |四人 (yonin) |四匹 (yonhiki) |四頭 (yontō) |四羽 (yonwa) |四枚 (yonmai) |四本 (yonhon) |四冊 (yonsatsu) |四台 (yondai) |四杯 (yonhai) |四輪 (yonrin) |四個 (yonko) |四つ (yottsu) |- |五 |五人 (gonin) |五匹 (gohiki) |五頭 (gotō) |五羽 (gowa) |五枚 (gomai) |五本 (gohon) |五冊 (gosatsu) |五台 (godai) |五杯 (gohai) |五輪 (gorin) |五個(goko) |五つ (itsutsu) |- |六 |六人 (rokunin) |六匹 (roppiki) |六頭 (rokutō) |五羽 (roppa) |六枚 (rokumai) |六本 (rokuhon) |六冊 (rokusatsu) |六台 (rokudai) |六杯 (roppai) |六輪 (rokurin) |六個 (rokko) |六つ (muttsu) |- |七 |七人 (nananin) |七匹 (nanahiki) |七頭 (nanatō) |七羽 (nanawa) |七枚 (nanamai) |七本 (nanahon) |七冊 (nanasatsu) |七台 (nanadai) |七杯 (nanahai) |七輪 (nanarin) |七個 (nanako) |七つ (nanatsu) |- |八 |八人 (hachinin) |八匹 (happiki) |八頭 (hattō) |八羽 (happa) |八枚 (hachimai) |八本 (happon) |八冊 (hassatsu) |八台 (hachidai) |八杯 (happai) |八輪 (hachirin) |八個 (hakko) |八つ (yattsu) |- |九 |九人 (kyūnin) |九匹 (kyūhiki) |九頭 (kyūtō) |九羽 (kyūwa) |九枚 (kyūmai) |九本 (kyūhon) |九冊 (kyūsatsu) |九台 (kyūdai) |九杯 (kyuhai) |九輪 (kyūrin) |九個 (kyūko) |九つ (kokonotsu) |- |十 |十人 (jūnin) |十匹 (jippiki) |十頭 (juttō) |十羽 (juppa) |十枚 (jūmai) |十本 (juppon) |十冊 (jūssatsu) |十台 (jūdai) |十杯 (juppai) |十輪 (jūrin) |十個 (jukko) |十 (tō) |} {|class="wikitable" !Chiffre !年 ''nen'' (année) !ヶ月 ''kagetsu'' (mois) !日 ''nichi''/''ka'' (jour) !週間 ''shūkan'' (semaine) !錠 ''jō'' (médicament rond) !時 ''ji'' (heure) !分 ''fun'' (minute) !秒 ''byō'' (seconde) !着 ''chaku'' (vêtement) !速 ''soku'' (chaussure) !切れ ''kire'' (tranche) |- |一     |一年 (ichinen) |一ヶ月 (ikkagetsu) |一日 (ichinichi) |一週間 (isshūkan) |一錠 (ichijō) |一時 (ichiji) |一分 (ippun) |一秒 (ichibyō) |一着 (itchaku) |一足 (issoku) |一切れ (hitokire) |- |二 |二年 (ninen) |二ヶ月 (nikagetsu) |二日間 (futsukakan) |二週間 (nishūkan) |二錠 (nijō) |二時 (niji) |二分 (nifun) |二秒 (nibyō) |二着 (nichaku) |二足 (nisoku) |二切れ (futakire) |- |三 |三年 (sannen) |三ヶ月 (sankagetsu) |三日間 (mikkakan) |三週間 (sanshūkan) |三錠 (sanjō) |三時 (sanji) |三分 (sanpun) |三秒 (sanbyō) |三着 (sanchaku) |三足 (sanzoku) |三切れ (mikire) |- |四 |四年 (yonen) |四ヶ月(yonkagetsu) |四日間 (yokkakan) |四週間 (yonshūkan) |四錠 (yonjō) |四時 (yoji) |四分 (yonpun) |四秒 (yonbyō) |四着 (yonchaku) |四足 (yonsoku) |四切れ (yonkire) |- |五 |五年 (gonen) |五ヶ月 (gokagetsu) |五日間 (itsukakan) |五週間 (goshūkan) |五錠 (gojō) |五時 (goji) |五分 (gofun) |五秒 (gobyō) |五着 (gochaku) |五足 (gosoku) |五切れ (gokire) |- |六 |六年 (rokunen) |六ヶ月 (rokkagetsu)  |六日間 (muikakan) |六週間 (rokushūkan) |六錠 (rokujō) |六時 (rokuji) |六分 (roppun) |六秒 (rokubyō) |六着 (rokuchaku) |六足 (rokusoku) |六切れ (rokkire) |- |七 |七年 (nananen) |七ヶ月 (nanakagetsu) |七日間 (nanokakan) |七週間 (nanashūkan) |七錠 (nanajō) |七時 (shichiji) |七分 (nanafun) |七秒 (shichibyō) |七着 (nanachaku) |七足 (nanasoku) |七切れ (nanakire) |- |八 |八年 (hachinen) |八ヶ月(hachikagetsu) |八日間 (yōkakan) |八週間 (hasshūkan) |八錠 (hachijō) |八時 (hachiji) |八分 (happun) |八秒 (hachibyō) |八着 (hatchaku) |八足 (hassoku) |八切れ (hakkire) |- |九 |九年 (kyūnen) |九ヶ月 (kyūkagetsu) |九日間 (kokonokakan) |九週間 (kyūshūkan) |九錠 (kyūjō) |九時 (kuji) |九分 (kyūfun) |九秒 (kyūbyō) |九着 (kyūchaku) |九足 (kyūsoku) |九切れ (kyūkire) |- |十 |十年 (jūnen) |十ヶ月 (jukkagetsu) |十日間 (tōkakan) |十週間 (jusshūkan) |十錠 (jūjō) |十時 (jūji) |十分 (juppun) |十秒 (jūbyō) |十着 (jutchaku) |十足 (jussoku) |十切れ (jukkire) |} {|class="wikitable" !Chiffre !課 ''ka'' (chapitre d'un livre, section d'un manuel) !巻 ''kan'' (tome d'une histoire en plusieurs volumes) !篇 ''hen'' (arc narratif) !部 ''bu'' (saison d'une sérié télévisée) !幕 ''maku'' (acte de théâtre) !弾 ''dan'' (acte d'intrigue) !軒 ''ken'' (maison, petit bâtiment) !棟 ''tō'' (immeuble, grand bâtiment) !脚 ''kyaku'' (chaise) !膳 ''zen'' (paire de baguettes) !位 ''i'' (numéro [rang]) !号 ''gō'' (numéro [identifiant numérique]) |- |一 |一課 (ichika) |一巻(ichikan) |一篇 (ichihen) |一部 (ichibu) |一幕 (ichimaku) |一弾 (ichidan) |一軒 (ichiken) |一棟 (ichitō) |一脚 (ichikyaku) |一膳 (ichizen) |一位 (ichi'i) |一号 (ichigō) |- |二 |二課 (nika) |二巻 (nikan) |二篇 (nihen) |二部 (nibu) |二幕 (nimaku) |二弾 (nidan) |二軒 (niken) |二棟 (nitō) |二脚 (nikyaku) |二膳 (nizen) |二位 (ni'i) |二号 (nigō) |- |三 |三課 (sanka) |三巻 (sankan) |三篇 (sanhen) |三部 (sanbu) |三幕 (sanmaku) |三弾 (sandan) |三軒 (sanken) |三棟 (santō) |三脚 (sankyaku) |三膳 (sanzen) |三位 (san'i) |三号 (sangō) |- |四 |四課 (yonka) |四巻 (yonkan) |四篇 (yonhen) |四部 (yonbu) |四幕 (yonmaku) |四弾 (yondan) |四軒 (yonken) |四棟 (yontō) |四脚 (yonkyaku) |四膳 (yonzen) |四位 (yon'i) |四号 (yongō) |- |五 |五課 (goka) |五巻 (gokan) |五篇 (gohen) |五部 (gobu) |五幕 (gomaku) |五弾 (godan) |五軒 (goken) |五棟 (gotō) |五脚 (gokyaku) |五膳 (gozen) |五位 (go'i) |五号 (gogō) |- |六 |六課 (rokuka) |六巻 (rokukan) |六篇 (rokuhen) |六部 (rokubu) |六幕 (rokumaku) |六弾 (rokudan) |六軒 (rokuken) |六棟 (rokutō) |六脚 (rokukyaku) |六膳 (rokuzen) |六位 (roku'i) |六号 (rokugō) |- |七 |七課 (nanaka) |七巻 (nanakan) |七篇 (nanahen) |七部 (nanabu) |七幕 (nanamaku) |七弾 (nanadan) |七軒 (nanaken) |七棟 (nanatō) |七脚 (nanakyaku) |七膳 (nanazen) |七位 (nana'i) |七号 (nanagō) |- |八 |八課 (hachika) |八巻 (hachikan) |八篇 (hachihen) |八部 (hachibu) |八幕 (hachimaku) |八弾 (hachidan) |八軒 (hachiken) |八棟 (hachitō) |八脚 (hachikyaku) |八膳 (hachizen) |八位 (hachi'i) |八号 (hachigō) |- |九 |九課 (kyūka) |九巻 (kyūkan) |九篇 (kyūhen) |九部 (kyūbu) |九幕 (kyūmaku) |九弾 (kyūdan) |九軒 (kyūken) |九棟 (kyūtō) |九脚 (kyūkyaku) |九膳 (kyūzen) |九位 (kyū'i) |九号 (kyūgō) |- |十 |十課 (jūka) |十巻 (jūkan) |十篇 (jūhen) |十部 (jūbu) |十幕 (jūmaku) |十弾 (jūdan) |十軒 (jūken) |十棟 (jūtō) |十脚 (jūkyaku) |十膳 (jūzen) |十位 (jū'i) |十号 (jūgō) |} '''''Note :''''' Le classificateur '''つ''' utilise les chiffres japonais (et non les chiffres sino-japonais) et ne peut être utilisé que pour compter de '''一''' (ichi) à '''十''' (jū). On l'utilise surtout pour les éléments n'ayant pas de classificateur spécifique. ==Les unités de mesure== Les unités de mesures s'utilisent comme des classificateurs numéraux. Voici des tableaux contenant quelques unités : {|class="wikitable" !Chiffre !階 ''kai'' (étage) !段 ''dan'' (niveau) !話 ''wa'' (épisode) !泊 ''haku'' (nuitée) !歳 ''sai'' (âge) !代 ''dai'' (génération) !番 ''ban'' (numéro [tour]) !倍 ''bai'' (multiplicateur) !回 ''kai'' (nombre de fois) !度 ''do'' (nombre de répétitions) !第 ''dai'' (ordre prédéterminé) !目 ''me'' (classement) |- |一 |一階(ichikai) |一段(ichidan) |一話 (ichiwa) |一泊 (ichihaku) |一歳 (ichisai) |一代 (ichidai) |一番 (ichiban) |一倍 (ichibai) |一回 (ichikai) |一度 (ichido) |第一 (daiichi) |一目 (ichime) |- |二 |二階 (nikai) |二段 (nidan) |二話 (niwa) |二泊 (nihaku) |二歳 (nisai) |二代 (nidai) |二番 (niban) |二倍 (nibai) |二回 (nikai) |二度 (nido) |第二 (daini) |二目 (nime) |- |三 |三階 (sankai) |三段 (sandan) |三話 (sanwa) |三泊(sanhaku) |三歳 (sansai) |三代 (sandai) |三番(sanban) |三倍 (sanbai) |三回 (sankai) |三度 (sando) |第三 (daisan) |三目 (sanme) |- |四 |四階 (yonkai) |四段 (yondan) |四話 (yonwa) |四泊(yonhaku) |四歳(yonsai) |四代 (yondai) |四番 (yonban) |四倍 (yonbai) |四回(yonkai) |四度 (yondo) |第四 (daiyon) |四目 (yonme) |- |五 |五階 (gokai) |五段 (godan) |五話 (gowa) |五泊 (gohaku) |五歳(gosai) |五代 (godai) |五番 (goban) |五倍 (gobai) |五回(gokai) |五度 (godo) |第五 (daigo) |五目 (gome) |- |六 |六階 (rokukai) |六段(rokudan) |六話(rokuwa) |六泊(rokuhaku) |六歳(rokusai) |六代 (rokudai) |六番(rokuban) |六倍 (rokubai) |六回 (rokukai) |六度 (rokudo) |第六 (dairoku) |六目 (rokume) |- |七 |七階(nanakai) |七段(nanadan) |七話(nanawa) |七泊(nanahaku) |七歳(nanasai) |七代 (nanadai) |七番(nanaban) |七倍 (nanabai) |七回(nanakai) |七度(nanado) |第七 (dainana) |七目(naname) |- |八 |八階(hachikai) |八段(hachidan) |八話(hachiwa) |八泊(hachihaku) |八歳(hachisai) |八代(hachidai) |八番(hachiban) |八倍 (hachibai) |八回(hachikai) |八度(hachido) |第八 (daihachi) |八目 (hachime) |- |九 |九階(kyūkai) |九段(kyūdan) |九話(kyūwa) |九泊(kyūhaku) |九歳(kyūsai) |九代 (kyūdai) |九番(kyūban) |九倍 (kyūbai) |九回(kyūkai) |九度 (kyūdo) |第九 (daikyū) |九目 (kyūme) |- |十 |十階(jūkai) |十段(jūdan) |十話(jūwa) |十泊(jūhaku) |十歳 (jūsai) |十代 (jūdai) |十番(jūban) |十倍 (jūbai) |十回(jūkai) |十度 (jūdo) |第十 (daijū) |十目 (jūme) |} == Voir aussi == *[[wikt:Catégorie:Noms communs japonais|Les noms dans le Wiktionnaire]] *[[w:Numération japonaise|La numération japonaise sur Wikipédia]] *[[w:Compter en japonais|Compter en japonais sur Wikipédia]] {{Glossaires de Japonais}} [[Catégorie:Glossaires de Japonais]] [[en:Japanese/Vocabulary/Numbers]] [[es:Japonés/Vocabulario/Números‎]] 3dq2q00jfove2f3ctwsm3mxjd82v674 Programmation PHP/DOMDocument 0 6867 744694 730736 2025-06-13T17:56:54Z 2A01:CB18:1150:4600:C5FF:3372:4F9:EE19 /* La classe DOMElement */Correction de "si il" en "s'il" 744694 wikitext text/x-wiki <noinclude>{{Programmation PHP}}</noinclude> == Introduction == Une des recherches majeures du développeur a toujours été de chercher à séparer les langages de programmation, clarifiant ainsi ses scripts et simplifiant sa tâche. Ainsi, il est possible d'enregistrer son CSS dans des fichiers externes comportant l'extension .css, de séparer le Javascript du HTML en l'enregistrant dans des fichiers .js. Il reste cependant le problème de la séparation du PHP et du XML (incluant le HTML). La bibliothèque {{w|Document Object Model|DOMDocument}} va repousser ces limites. == Qu'est-ce que DOMDocument ? == DOMDocument est une bibliothèque de fonctions apparue avec PHP5<ref>http://php.net/manual/fr/book.dom.php</ref> et activée par défaut. Elle permet de concevoir des pages [[Programmation HTML|HTML]] sous forme d'objets. == Les avantages et les inconvénients == * Concevoir des pages HTML par cette méthode permet d’annihiler un problème majeur de la programmation procédurale : l'édition du code n'est plus en fonction de sa position dans le script. Pour être plus clair, chaque balise jusqu'à la {{w|DTD}} peut être modifiée à tout moment dans l'objet HTML. * Il est possible d'enregistrer la page HTML dans un fichier sans l'afficher. mais... * Le code est plus long à éditer. == Principe du DOM == Cette bibliothèque présente de nombreuses similitudes avec le Javascript aussi bien dans le fonctionnement que dans le nom de ses fonctions. Le DOM (Document Object Model) est basé sur un système de nodes (nœuds). Un node est un élément qui est - soit une balise (nodes Tag) - soit du texte - soit un attribut de balise Les nodes sont liés par un système hiérarchique : <syntaxhighlight lang=php> <p>Ce texte est <strong>important</strong></p> </syntaxhighlight> On dit alors que le node Tag &lt;strong> est fils du node &lt;p> Le node texte "Ce texte est" est également fils de &lt;p> qui est parent du node texte. Il existe un certain nombre de classes prédéfinies : DOMDocument, DOMNode, DOMElement, DOMText, DOMAttr, DOMList... Certaines sont très simples, d'autres possèdent des fonctionnalités très avancées. == Importer une page préexistante == Il est possible d'importer une page HTML. Cela simplifiera considérablement la tâche du programmeur qui n'aura qu'à apporter les modifications nécessaires avant de l'afficher ou de réenregistrer la page. Voici le code important la page. <syntaxhighlight lang=php> <?php $doc = DOMDocument::loadHTMLFile("fichier.html"); </syntaxhighlight> La variable $doc contient donc un objet DOMDocument avec toutes les balises sous formes de nodes. Il est maintenant possible d'accéder aux nodes par le biais de fonctions préexistantes. NB : il est également possible d'avoir recours au code suivant. <syntaxhighlight lang=php> <?php $doc = new DOMDocument(); $doc->loadHTMLFile("fichier.html"); </syntaxhighlight> Il est également possible d'importer le code à partir d'une chaîne de caractères : <syntaxhighlight lang=php> <?php $code = "<html><head></head><body></body></html>"; $doc = new DOMDocument(); $doc->loadHTML( $code ); </syntaxhighlight> == Enregistrer une page == Un des grands avantages de cette bibliothèque est la capacité à enregistrer la page générée dans un fichier pour un affichage ultérieur. Il suffit d'avoir recours au code suivant : <syntaxhighlight lang=php> $doc->saveHTMLFile("fichier.html"); </syntaxhighlight> Si vous voulez l'afficher, il vous suffit d'exécuter la fonction suivante : <syntaxhighlight lang=php> <?php echo $doc->saveHTML(); </syntaxhighlight> La méthode retourne une chaîne de caractères que la fonction <code>echo</code> affiche. {{attention|Le résultat n'est pas en Unicode, donc les lettres avec diacritiques seront mal affichées par défaut, en français : ''àâçéèêëîïôöüù''. Cela peut aussi générer un ''Warning: DOMDocumentFragment::appendXML(): Entity: line 1: parser error : Input is not proper UTF-8, indicate encoding !''}} <syntaxhighlight lang=php> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr-FR"> <head> <meta http-equiv="Content-type" content="text/html; charset=UTF-8"/> </head> <body> <?php $code = 'Les àâçéèêëîïôöüù'; echo $code; // affichage normal $page = new DOMDocument(); $page->loadHTML($code); echo $page->saveHTML(); // Les à âçéèêëîïôöüù // Solution $page2 = new DOMDocument(); $page2->loadHTML(utf8_decode($code)); echo $page2->saveHTML(); // Les àâçéèêëîïôöüù ?> </body> </html> </syntaxhighlight> == La classe DOMNode == Les classes DOMElement, DOMText et DOMAttribute sont dérivées de cette classe. Ainsi, les méthodes et propriétés présentées ici seront disponibles pour leurs classes filles. Attention : un nœud une fois créé ne se trouve pas dans le document. Ajouter un nœud va se dérouler en deux étapes : # on crée le nœud # on l'insère dans le nœud parent ou à la racine du document. Voici les propriétés accessibles à tous les nœuds : <syntaxhighlight lang=php> $node->nodeType // Type de nœud. Vaut 1 pour un élément XML, 3 pour un texte $node->childNodes //Retourne un objet NodeList qui contient tous les éléments enfants de ce nœud $node->firstChild //Retourne le premier nœud enfant $node->lastChild // Retourne le dernier nœud enfant $node->previousSibling // Retourne le nœud juste avant celui-ci $node->nextSibling // Retourne le nœud juste après celui-ci </syntaxhighlight> Les méthodes sont les suivantes : ;'''AppendChild''':Le nœud $enfant devient enfant de $node <syntaxhighlight lang=php> $node->appendChild( $enfant ); </syntaxhighlight> ;'''RemoveChild''':Supprime le nœud $enfant du node $node <syntaxhighlight lang=php> $node->removeChild( $enfant ); </syntaxhighlight> == La classe DOMElement == *Les éléments possèdent les propriétés suivantes : <syntaxhighlight lang=php> $node->tagName // par exemple "p" pour un paragraphe. Sa valeur ne peut être modifiée </syntaxhighlight> Attention : seules les principales propriétés sont présentées. Si vous voulez en avoir la liste complète, vous pouvez consulter la documentation de référence sur http://php.net/manual/fr/class.domnode.php. * Et les méthodes suivantes : - Méthodes d'attributs <syntaxhighlight lang=php> $node->hasAttribute(); //Renvoie true s'il possède des attributs $node->getAttribute("name"); //Retourne la valeur de l'attribut $node->removeAttribute("name"); //Supprime l'attribut ''name'' $node->setAttribute("name","value"); //Modifie un attribut $node->getAttributeNode ("name" ); //Retourne un objet DOMAttr </syntaxhighlight> - Autres méthodes <syntaxhighlight lang=php> $newNode = $node->cloneNode(); //duplique un élément $nodeList = $node->getElementsByTagName("strong"); </syntaxhighlight> Cette fonction retourne un objet ''nodeList'' qui contient une liste des balises &lt;strong> enfants du nœud. Pour récupérer le n+1ème nœud de la liste, il suffit d'avoir recours à la méthode de l'objet ''nodeList'' suivante : <syntaxhighlight lang=php> $strong5 = $nodeList->item(4); //Sélectionne la 5e balise <strong> </syntaxhighlight> L'attribut ''length'' donne le nombre d'éléments de la liste. Exemple : <syntaxhighlight lang=php> for ($i=0; $i<$nodeList->length; $i++) { echo $nodeList->item( $i )->tagName; } </syntaxhighlight> Comme vous le savez, ''tagName'' retourne le nom de la balise. Ici, le code retournera "strongstrongstrong…". En effet, seuls les nœuds ''strong'' ont "été sélectionnés. Comme vous avez pu le remarquer, il est possible d'exécuter plusieurs méthodes et propriétés en même temps. Voici l'ordre d'exécution : - La méthode item($i) est exécutée et retourne un ''nodeTag''. - La propriété ''tagName'' du nœud est appelée. Attention : c'est celle de l'objet retourné. - La fonction ''echo'' affiche le nom de la balise retournée par la propriété ''tagName''. == La classe DOMText == La classe DOMText contient l'unique propriété suivante : <syntaxhighlight lang="php"> $node->wholeText </syntaxhighlight> Elle n'est accessible qu'en lecture seule. La classe ''DOMText'' admet deux méthodes : <syntaxhighlight lang="php"> /* Retourne true si la chaîne de caractère contient des espaces */ $node->isWhitespaceInElementContent(); </syntaxhighlight> /* Retourne dans $end un objet Text qui contient la fin du texte de $node. $node ne contiendra plus que les 5 premiers caractères de sa chaîne */ <syntaxhighlight lang="php"> $end = $node->splitText(5) </syntaxhighlight> Exemple : <syntaxhighlight lang=php> <?php $doc = new DOMDocument(); $text = $doc->createTextNode("Je suis celui qui est"); $text2 = $text->splitText(7); echo $text->wholeText."<br />"; echo $text2->wholeText </syntaxhighlight> Ce code retournera "je suis&lt;br /> celui qui est" == La classe DOMAttr == La classe DOMAttr est comme son nom l'indique un attribut, qui est donc dépendant de la balise. Elle contient les propriétés suivantes : <syntaxhighlight lang="php"> $node->name // Nom de l'attribut $node->value // Valeur de l'attribut /* Nom de la balise qui contient l'attribut. La valeur retournée est un objet DOMElement */ $node->ownerElement </syntaxhighlight> Seule la propriété value n'est pas en lecture seule, c'est à dire qu'il est possible d'avoir recours au code suivant : <syntaxhighlight lang=php> $node->value = "maValeur"; </syntaxhighlight> == Accéder à un nœud == Il existe plusieurs modes de recherche du nœud. Il est par exemple possible de le sélectionner par son id : <syntaxhighlight lang=php> $node = $doc->getElementById("sonId"); // Retourne un objet nœud </syntaxhighlight> Sachant qu'il ne peut y avoir qu'un nœud possédant l'id recherché, la méthode retournera un objet nœud au lieu d'un objet nodeList. Il est cependant possible de récupérer une liste de nœuds en les sélectionnant par leur nom de balise. Évidemment, seuls les nœuds Tag peuvent être sélectionnés. <syntaxhighlight lang=php> $nodeList = $doc->getElementsByTagName("acronym"); //Sélectionne toutes les balises <acronym> </syntaxhighlight> Comme nous l'avons déjà dit, il faut, pour récupérer un nœud particulier de la liste, utiliser la méthode suivante : <syntaxhighlight lang=php> $acronym5 = $nodeList->item(4); //Sélectionne le 5e nœud de la liste; </syntaxhighlight> == XML == Création : <pre> $xml = new DOMDocument("1.0", "ISO-8859-15"); $xml_node1 = $xml->createElement("Node_1"); $xml_node2 = $xml->createElement("Node_2", "Feuille"); $xml_node2->setAttribute("Attribut_1", "Valeur_1"); $xml_node1->appendChild($xml_node2); $xml->appendChild($xml_node1); $xml->save('Fichier_1.xml'); </pre> Lecture : <pre> $xml = new DomDocument; $xml->load('Fichier_1.xml'); $fields = $xml->getElementsByTagName('fields'); foreach ($fields as $field) { /** @var DOMElement|DomText $fieldChild */ foreach ($field->childNodes as $fieldChild) { var_dump($fieldChild->nodeValue); } } </pre> ==Références== <references/> <noinclude> == Voir aussi == * [[../Exemples/DomXml|DOM XML]] </noinclude> akqvpfobj1il9li83wifmik841rikyd Programmation C sharp/Les variables et les constantes 0 14122 744725 659123 2025-06-14T09:41:33Z 197.149.187.26 /* Les variables */ 744725 wikitext text/x-wiki <noinclude>{{Programmation C sharp}}{{NavTitre|book={{BASEPAGENAME}}|prev=Les espaces de noms|next=Les types de base et les déclarations}}</noinclude> == Les variables == Une variable est une espace ou un conteneur en mémoire pour stocker des données : résultats d'expressions, données lues depuis un fichier, Elle est associée à un nom. === Déclaration === Une variable possède un nom et un type. Ce type détermine ce que peut stocker la variable : un nombre, une chaîne de caractère, un objet d'une classe particulière. La syntaxe est la suivante : ''type nom ['' = ''expression ]'' ; Exemples : <syntaxhighlight lang="csharp"> double prix_unitaire; int quantite = 50; string article = "Pommes"; bool rabais = false; </syntaxhighlight> Assigner une variable (initialiser) à la déclaration n'est pas obligatoire. Dans ce cas, elle possède la valeur par défaut correspondant au type : * la valeur par défaut des types numériques et caractères (<code>char</code>) est zéro (0), * la valeur par défaut du type booléen (<code>bool</code>) est <code>false</code>, * la valeur par défaut des types références (objet, chaîne de caractère, tableaux) est <code>null</code>, * la valeur par défaut des types énumérés (<code>enum</code>) est celle qui correspond au type sous-jacent (<code>int</code> par défaut). Il est possible de regrouper la déclaration de plusieurs variables du même type en utilisant une virgule pour séparer les différentes variables. Exemple : <syntaxhighlight lang="csharp"> int quantite = 50, nombre, quantite_en_stock = 100 ; </syntaxhighlight> Ou en plaçant une variable par ligne pour plus de clarté : <syntaxhighlight lang="csharp"> int quantite = 50, nombre, quantite_en_stock = 100 ; </syntaxhighlight> === Utilisation === Une variable est utilisée pour stocker des résultats intermédiaires, des données qui serviront ultérieurement. La valeur d'une variable peut être modifiée autant de fois que nécessaire. Exemple : <syntaxhighlight lang="csharp"> prix_unitaire = 1.50; if (rabais) prix_unitaire = prix_unitaire - 0.20; double prix_total = prix_unitaire * quantite ; </syntaxhighlight> === Portée d'une variable === La portée d'une variable est l'ensemble des emplacements dans le code où cette variable est accessible. En dehors de cette portée, l'utilisation du même nom correspondra à une autre variable ou plus souvent à aucune variable (erreur de compilation dans ce cas). La portée d'une variable correspond au bloc d'accolades où elle est déclarée. Une variable membre d'une classe est déclarée au niveau de la classe et est accessible depuis les autres membres de la classe (méthodes, indexeurs, propriétés, ...). Une variable locale est déclarée à l'intérieur d'un bloc d'accolades (celui de la méthode/propriété membre, ou un bloc interne à celle-ci). Exemple : <syntaxhighlight lang="csharp"> public class Exemple { // variable membre de la classe : int nombre = 5; // portée : la classe Exemple public int MethodeUne() { // variable locale à la méthode : int nombre = 10; // portée : la méthode MethodeUne() return nombre + // la variable locale this.nombre; // la variable membre // retourne 10 + 5 , c'est à dire 15 } public int MethodeDeux() { System.Console.WriteLine("Nombre = " + nombre); // la variable membre // Nombre = 5 { int nombre = 20; // variable locale au bloc d'accolades System.Console.WriteLine("Nombre = " + nombre); // la variable locale // Nombre = 20 } // la variable locale est hors de portée System.Console.WriteLine("Nombre = " + nombre); // la variable membre // Nombre = 5 } } </syntaxhighlight> === Durée de vie d'une variable === La durée de vie d'une variable est la période durant laquelle une variable existe, c'est à dire conserve son emplacement en mémoire et durant laquelle elle peut donc stocker des données. Pour une variable locale d'une méthode, la durée de vie correspond à celle de l'exécution du bloc de code : elle est allouée au début du bloc de déclaration sur la pile, et libérée de la pile après exécution du bloc. Pour une variable membre d'une classe, la durée de vie correspond à celle de l'objet (l'instance de la classe) : elle est allouée à la construction d'un objet et libérée lors de la libération de l'objet. === Variable en lecture seule === Une variable peut être déclarée en lecture seule en utilisant le mot-clé <code>readonly</code>. Exemple : <syntaxhighlight lang="csharp"> readonly double taux_tva = 19.6; </syntaxhighlight> Il n'est pas obligatoire d'initialiser une variable en lecture seule lors de sa déclaration. Ce qui permet de déterminer la valeur en fonction d'autres données, ou de lire sa valeur depuis un fichier par exemple, et d'empêcher sa modification après affectation. Exemple : <syntaxhighlight lang="csharp"> class Facture { public readonly double taux_tva; public Facture(bool taux1) { if (taux1) taux_tva = 19.6; else taux_tva = 5.5; } } </syntaxhighlight> == Les constantes == Une constante nommée est associée à une valeur pour toute la durée de l'application. Sa valeur ne peut changer. === Déclaration === La syntaxe est similaire à celle de la déclaration d'une variable, excepté que le mot clé <code>const</code> précède la déclaration, et que l'initialisation à la déclaration est obligatoire : const ''type nom'' = ''expression'' ; Exemple : <syntaxhighlight lang="csharp"> const double prix_unitaire_unique = 1.50 ; const double taux_euro_en_francs = 6.55957 ; </syntaxhighlight> Comme les variables il est également possible de regrouper la déclaration de plusieurs constantes du même type : <syntaxhighlight lang="csharp"> const double prix_unitaire_unique = 1.50 , taux_euro_en_francs = 6.55957 ; </syntaxhighlight> '''N.B.''' : Une constante est implicitement statique. Il est donc inutile d'ajouter le mot-clé <code>static</code>, sinon le compilateur génère une erreur. === Types des constantes === Le mot <code>const</code> ne s'applique qu'aux types standards numériques, booléens, <code>string</code>, et énumérations. === Portée et durée de vie d'une constante === Les mêmes règles que celles pour les variables s'appliquent également aux constantes. tv0ha9f2v6legpioghvrv5vnlobz40r Modèle:Photo màj 10 22266 744722 679628 2025-06-14T01:01:14Z 2600:1003:B881:4C40:A4FF:BC4F:5818:85F 744722 wikitext text/x-wiki <s></s><div style="text-align: center;">'''les 10 dernières mises à jour notables'''</div> # [[Photographie/Personnalités/S/Southworth & Hawes|Southworth & Hawes]] (2 mars) # [[Photographie/Fabricants/Showa Optical Works|Showa Optical Works]] (29 février) # [[Photographie/Personnalités/B/Bradley & Rulofson|Bradley & Rulofson]] (4 janvier, MàJ) - - - - - - - - - - # [[Photographie/Personnalités/L/Lucien Lorelle|Lucien Lorelle]]décembre, MàJ) # [[Photographie/Sociétés et Organisations/Associations et clubs/Groupe des XV|Groupe des XV]] (10 décembre) # [[Photographie/Fabricants/Sony|MàJ de la liste des APN Sony]] (10 décembre) # [[Photographie/Personnalités/S/Alfred Stieglitz|Alfred Stieglitz]] (17 novembre) # [[Photographie/Classement et archivage|Classement et archivage]] (12 novembre) # [[Photographie/Fabricants/ADOX|ADOX]] (24 août) # [[Photographie/Personnalités/B/Hippolyte Bayard|Hippolyte Bayard]] (22 août) {{Boîte déroulante début|titre=mises à jour précédentes|}} ---------- # [[Photographie/Personnalités/C/Charles Louis Chevalier|Famille Chevalier (opticiens)]] (22 août) # [[Photographie/Personnalités/K/Ronald Kroon|Ronald Kroon]] (8 août) # [[Photographie/Personnalités/S/Giorgio_Sommer|Giorgio Sommer]] (27 juillet) # [[Photographie/Personnalités/H/Frank Jay Haynes|Frank Jay Haynes]] (22 juillet) # [[Photographie/Personnalités/H/Oliver Wendell Holmes Sr.|Oliver Wendell Holmes Sr.]] (6 juin) # [[Photographie/Personnalités/R/Paul Rudolph|Paul Rudolph]] (26 mai) # [[Photographie/Personnalités/W/Sabine Weiss|Sabine Weiss]] (1er juillet) # [[Photographie/Personnalités/B/Frédéric Boissonnas|Frédéric Boissonnas]] (10 février) # [[Photographie/Personnalités/V/Ariel Varges|Ariel Varges]] (8 février) # [[Photographie/Personnalités/G/Andreas Gursky|Andreas Gursky]] (9 janvier) # [[Photographie/Personnalités/E/Antonio Esplugas Puig|Antonio Esplugas Puig]] (8 janvier) # [[Photographie/Fabricants/Cosina/Cosina CS-3|Cosina CS-3]] (7 janvier) # [[Photographie/Fabricants/Cosina/Cosina CS-2|Cosina CS-2]] (7 janvier) # [[Photographie/Fabricants/Cosina/Cosina CS-1|Cosina CS-1]] (7 janvier) # [[Photographie/Fabricants/Cosina/Cosina CT-1|Cosina CT-1]] (7 janvier) # [[Photographie/Fabricants/Vivitar/Vivitar XV-2|Vivitar XV-2]] (7 janvier) # [[Photographie/Fabricants/Fujifilm/Fujifilm Fujica AX-3|Fujica AX-3]] (7 janvier) # [[Photographie/Fabricants/Mamiya/Mamiya ZE|Mamiya ZE]] (7 janvier) # [[Photographie/Personnalités/L/G. Lékégian|G. Lékégian]] (3 janvier) # [[Photographie/Fabricants/Vivitar/Vivitar Zoom Thyristor 285|Vivitar Zoom Thyristor 285]] (3 janvier) # [[Photographie/Fabricants/Sunpak/Sunpak Autozoom 3600|Sunpak Autozoom 3600]] (2 janvier) # [[Photographie/Fabricants/Panasonic/Panasonic National PE-3057|National PE-3057]] (2 janvier) # [[Photographie/Fabricants/Minolta/Minolta Auto 320X|Minolta Auto 320X]] (2 janvier) # [[Photographie/Fabricants/Mamiya/Mamiya Mamiyalite ZE|Mamiyalite ZE]] (2 janvier) # [[Photographie/Fabricants/Konica/Konica X-24 Auto|Konica X-24 Auto]] (2 janvier) # [[Photographie/Fabricants/Fujifilm/Fujifilm Fujica Auto Strobo 300 X|Fujica Auto Strobo 300 X]] (2 janvier) # [[Photographie/Fabricants/Yashica/Contax TLA 20|Contax TLA 20]] (2 janvier) # [[Photographie/Personnalités/J/Pierre Jaffeux|Pierre Jaffeux]] (2 janvier) '''Archives''' <!--[[Photographie/Mises à jour, archives 2017|2017]] --> [[Photographie/Mises à jour, archives 2016|2016]] [[Photographie/Mises à jour, archives 2015|2015]] [[Photographie/Mises à jour, archives 2014|2014]] [[Photographie/Mises à jour, archives 2013|2013]] [[Photographie/Mises à jour, archives 2012|2012]] [[Photographie/Mises à jour, archives 2011|2011]] [[Photographie/Mises à jour, archives 2010|2010]] [[Photographie/Mises à jour, archives 2009|2009]] [[Photographie/Mises à jour, archives 2008|2008]] {{Boîte déroulante fin}} <noinclude> {{documentation|{{FULLPAGENAME}}}} {{Photo màj}} [[Catégorie:Modèles non imprimables du livre de photographie]] </noinclude> 4m7zv7rk77bifrbfet705ztvkmie13d Technologie/Moteurs thermiques/Moteur Diesel/Système de distribution 0 52911 744671 455615 2025-06-13T16:28:57Z 2A01:CB18:1150:4600:C5FF:3372:4F9:EE19 /* Différentes techniques */Correction de "si ils" en "s'ils" 744671 wikitext text/x-wiki {{Moteur Diesel}} En mécanique, la '''distribution''' regroupe les mécanismes qui assurent l'admission et l'échappement des gaz dans les cylindres d'un moteur à explosion. Lors de son fonctionnement, un moteur à combustion interne effectue différentes phases réalisées dans un ordre précis appelées « temps ». Afin que celles-ci se déroulent d'une manière ordonnée, il est nécessaire de synchroniser les différentes phases. == Description et fonctionnement == [[Fichier:Engine movingparts.jpg|thumb|left|Éclaté d'un moteur à double arbre à cames en tête.]] Un moteur à deux temps utilise peu de pièces mécaniques dans la mesure où il s'agit de découvrir lors de la course du piston différentes lumières d'entrées et de sortie du mélange carburé à l'admission et des gaz brulés à l'échappement et parfois d'actionner, par dépression, des clapets sur le circuit d'admission. Le moteur à quatre temps est un système plus complexe car utilisant de nombreuses pièces en mouvement simultané et synchronisé, se rapprochant d'un mouvement d'horlogerie « dispositif de synchronisation ». Dans ce cas, le mouvement circulaire décrit par le vilebrequin est transmis au mécanisme d'ouverture des soupapes de l'arbre à cames, soit par courroie, soit par chaîne de transmission, ou bien encore par une cascade de pignons. == Différentes techniques == [[Fichier:Nockenwellenantrieb.jpg|thumb|left|Chaîne de distribution d'un moteur à double arbre à cames en tête.]] * Les moteurs de conception assez ancienne sont pourvus d'une chaîne, ou d'une cascade de pignons, pour transmettre le mouvement du vilebrequin à l'arbre à cames. Leur durée de vie est théoriquement égale à celle de l'ensemble du moteur, s'ils sont correctement lubrifiés, cependant ces techniques d'entrainement sont consommatrices de puissance, à l’accélération, du fait de leur inertie, car ils sont assez lourds ; ils nuisent donc à l'efficacité mécanique globale du moteur. * Dans les moteurs modernes, très souvent, des courroies de distribution sont utilisées. Elles ont l'avantage de ne nécessiter aucun graissage et d'être silencieuses ; par contre, leur remplacement périodique est impératif (tous les {{nombre|50000}} à {{unité|240000|kilomètres}}, mais aussi après quelques années, entre cinq et dix ans, lorsque le kilométrage n'est pas atteint, du fait de la dégradation progressive des composants de cette courroie, essentiellement du caoutchouc enrobant une tresse (âme) en aramide ou fibre de verre. La rupture de cette courroie est à coup sûr destructrice pour l'ensemble du haut moteur, sauf pour les moteurs à non interférence (les pistons en position haute ne peuvent pas toucher les soupapes même si ces dernieres sont descendues au maximum). ==Distribution variable== [[Image:K20 head.jpg|thumb|upright=2|left|L'intérieur d'un moteur.]] La '''distribution variable''' est une technologie permettant de faire varier plusieurs paramètres dans un moteur à combustion interne : le calage, la durée d'ouverture et/ou la levée des soupapes d'admission et d'échappement. Ces paramètres varient essentiellement en fonction du régime, de la charge et de la demande d'accélération. Les bénéfices de la distribution variable sont un couple important à bas régime, une forte puissance à haut régime, un meilleur rendement (autorisant le fonctionnement du moteur en [[:w:Cycle d'Atkinson|cycle d'Atkinson]] et la diminution des pertes par pompage) et une moindre pollution. Toutefois, la tendance est à l'implémentation de la distribution variable sans arbres à cames, en remplaçant ces derniers par des « actuateurs » (technique camless, qui se marie idéalement avec elle), augmentant encore les bénéfices économique d'énergie. ==Le distributeur== Dans les [[:w:automatisme|systèmes automatisés]], le '''distributeur''' est l'élément de la chaîne de transmission d'énergie utilisé pour commuter et contrôler la circulation des fluides sous pression. Bien que certains capteurs fonctionnent sur les mêmes principes, on réserve plus particulièrement ce terme au préactionneur alors équivalent du [[:w:relais électromécanique|relais]] pour l'électricité. Généralement constitué d'un tiroir qui coulisse dans un corps, il met en communication des orifices (connectables ou non) suivant plusieurs associations. Le tiroir peut être actionné par un levier, une bobine, un piston, ou un ressort de rappel (pour ceux disposant d'une position neutre ou stable). Le tiroir possède un jeu fonctionnel qui laisse passer une légère fuite. Les distributeurs à clapet ou les cartouches logiques suppriment cet inconvénient. == Classification et Représentation == === Types de distributeurs === [[Image:Distributeurs cases.svg|thumb|Représentation schématique des principaux distributeurs selon le nombre d'orifices et le nombre de positions]] On distingue les distributeurs d'abord par le nombre d'orifices et le nombre de positions, puis la nature des commandes, la gestion du flux de puissance ("tout ou rien" ou progressif). Le nombre d'orifices est le nombre de conduites (connectables ou non) sortant du corps du distributeur. Il y en a donc au moins deux. Le nombre de positions correspond au nombre de situations du composant. Pour chaque position, les conduites sont associées suivant une combinaison différente. Certains composants passifs (comme les clapets anti-retour ou pressostat) sont considérés, du point de vue de la représentation comme des distributeurs à une position. Les cas courants comportent deux ou trois cases. La désignation d'un distributeur se présente comme une fraction donnant le nombre d'orifices puis le nombre de positions. La représentation schématique des distributeurs est une juxtaposition de cases carrées: *Il y a autant de cases que de positions. *Pour chaque position les flèches représentent les connexions internes reliant les orifices. La flèche donne le sens d'écoulement. On retrouve dans chaque case un même emplacement pour chaque orifice. Un orifice orphelin est marqué par un bouchon en forme de T. === Raccordement === [[Image:Distributeurs connexions.png|thumb|Exemples de raccordement des orifices sur la case active (ici en jaune)]] À l'extérieur, dans le prolongement des orifices, on représente les conduites amenant le fluide. *Elles sont obligatoirement raccordées à la même case associée à la position de référence, appelée position active pour le composant mais position initiale pour le système. *La source de pression et l'échappement sont identifiables par leur symbole spécifique qui simplifie le schéma global. *Les autres conduites sont représentées par un trait simple reliant deux composants. *Les conduites auxiliaires sont représentées en trait interrompu (pointillé). === Commandes ou pilotages === [[Image:distributeurs_commandes.png|thumb|Exemples de commandes sur différents modèles de distributeurs]] Sur le côté libre des cases, le dispositif de commande est représenté dans le respect des conventions. Les codes de représentation sont sensiblement les mêmes que pour les composants électrotechniques. La nature des commandes peut être très variée, simple ou parfois multiple: * commande manuelle par poussoir, coup de poing, levier ou pédale, * commande électrique par solénoïde, * commande hydraulique ou pneumatique, * commande mécanique pour les capteurs, * commande par ressort (il s'agit alors de distributeurs monostables). Par défaut, la case centrale correspond à une situation stable, forcée par un ou plusieurs ressorts. ==== Exemples présentés, et usage ==== L'illustration de la section ci-dessus donne la représentation de cas très courants, et leur emploi : * 2/2 NO monostable par bouton poussoir : bouton de commande. * 3/2 NF capteur à galet : détection de présence mécanique. * 4/2 à commandes pneumatiques : alimentation de vérin à double effet. * 5/2 à commandes électriques ou manuelles : alimentation de vérin à double effet, pilotage direct par automate. * 4/3 à commande par levier et sortie progressive (joystick) : commande d'engin hydraulique. Même si sur le plan théorique, certains capteurs ou boutons de commande fonctionnent comme des distributeurs, ils sont très différents technologiquement des composants de puissance. En pratique, on ne désigne par ''distributeur'' que les préactionneurs chargés de contrôler l'énergie envoyée aux vérins et autres moteurs. === Sens d'action === [[Image:3-2NFcapteur.png|thumb|Vue en coupe de capteur à galet [[:w:Distributeur 3/2|3/2NF]].]] Pour les distributeurs monostables à deux positions, donc à rappel par ressort, on distingue deux familles suivant que la puissance est délivrée par défaut ou sur ordre : * Distributeur normalement fermé (NF) : la sortie n'est pas alimentée en absence de consigne. * Distributeur normalement ouvert (NO) : la sortie est alimentée en absence de consigne. Il faut donc commander la coupure d'énergie. == Choix == Le choix du distributeur est fortement lié à son environnement d'implantation. Il faut tenir compte de : * la nature du fluide : air ou huile, * de la puissance requise par l'actionneur piloté, problème de débit éventuel, * de la nature de l'énergie de commande : manuelle, électrique, pneumatique ou hydraulique, * de l'action désirée : avec ou sans mémorisation, par défaut ou sur ordre. [[Catégorie:Moteur Diesel (livre)]] fkrt0h3jfo4wnul25vqdiizghwb4s1z Boîte à jeux/10.000 0 53113 744723 680345 2025-06-14T03:32:16Z 2A01:CB18:1150:4600:C5FF:3372:4F9:EE19 /* Déroulement */Correction de "si il" en "s'il" et ponctuation 744723 wikitext text/x-wiki {{Boîte à jeux|dés}} == Matériel == * 5 dés * 1 crayon * 1 papier == But du jeu == Atteindre précisément {{formatnum:10000}} points == Mise en place == Chaque joueur tire un dé le plus grand résultat joue en premier. En cas d'égalité on retire un dé entre les gagnants. == Figures == * Le 1 vaut 100 points * Le 5 vaut 50 points * Un brelan vaut 100 x le dé du brelan (un brelan de 3 vaut 300, un brelan de 5 vaut 500, un brelan de 1 vaut 1000) * Un carré double la valeur du brelan * Un quintuple double la valeur du carré (un quintuple de 1 fait automatiquement gagné la partie) * Un full vaut 100 * le dé du brelan + 50 * le dé de la paire * Une suite vaut 1500 points == Déroulement == Le joueur dont c'est le tour lance l'ensemble des 5 dés. S'il n'obtient aucune figure, son tour est fini, et c'est au joueur suivant de jouer. S'il obtient une figure, il peut relancer les dés restants pour compléter la figure obtenue ou faire une autre figure. S'il obtient plusieurs figures, il peut choisir d'en relancer certaines afin de jeter plus de dés. Le joueur peut s’arrêter après un jeté comprenant au moins un dé gagnant, on ajoute alors le score de ses lancés à son score actuel. Si le joueur n'obtient aucune figure sur un jeté, il perd tous les points qu'il avait accumulé à ce tour. Si après plusieurs jetés successifs tous les dés sont utilisés dans des figures, le joueur relance tous les dés en gardant son score en mémoire. Cependant, afin de commencer à marquer des points, il est nécessaire d'obtenir un score d'au moins 500 points. == Variantes == * On peut augmenter le score minimum pour marquer le premier score à {{formatnum:1000}} points. obkmqr3rjcm9kjyswpcptues51ru1aq Fonctionnement d'un ordinateur/L'espace d'adressage du processeur 0 79337 744676 744653 2025-06-13T17:16:03Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744676 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets, chose que nous détaillerons cela dans le chapitre suivant. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Cependant, beaucoup de programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Il faut noter que le DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Et beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode virtuel 8086 faisait que l'OS pouvait adresser 16 mébioctets de mémoire, voire 4 gigaoctets sur le 386 et plus. Par contre, les applications étaient limitées au mébioctet du mode réel. Cependant, le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé le ''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. L'intermédiaire ''Extended Memory Manager'' s'occupe d’échanger des données entre mémoire conventionnelle et mémoire étendue. Pour cela, il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory'', les deux ne sont pas la même chose. L'''expanded memory'' autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute, géré par un système de commutation de banque. Elle fonctionne sans mode protégé. La mémoire étendue ne gère pas de commutation de banque. Et les va-et-vient avec la mémoire conventionnelle demandent de switcher entre mode réel et protégé et demande donc l'existence de ce dernier. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques pour que de la RAM soit traitée soit comme mémoire étendue, soit comme ''expanded memory''. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. Le système d'exploitation pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 12zt31uja0vcyy8v18flnxxa3ydj7wj 744677 744676 2025-06-13T17:17:20Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744677 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets, chose que nous détaillerons cela dans le chapitre suivant. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Cependant, beaucoup de programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Il faut noter que le DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Et beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode virtuel 8086 faisait que l'OS pouvait adresser 16 mébioctets de mémoire sur le 286, 4 gigaoctets sur le 386 et plus. Par contre, les applications étaient limitées au mébioctet du mode réel. Cependant, le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé le ''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. L'intermédiaire ''Extended Memory Manager'' s'occupe d’échanger des données entre mémoire conventionnelle et mémoire étendue. Pour cela, il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory'', les deux ne sont pas la même chose. L'''expanded memory'' autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute, géré par un système de commutation de banque. Elle fonctionne sans mode protégé. La mémoire étendue ne gère pas de commutation de banque. Et les va-et-vient avec la mémoire conventionnelle demandent de switcher entre mode réel et protégé et demande donc l'existence de ce dernier. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques pour que de la RAM soit traitée soit comme mémoire étendue, soit comme ''expanded memory''. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. Le système d'exploitation pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 33ge0xzrorcckw9ejscxa1m6rvvmj7w 744678 744677 2025-06-13T17:21:31Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744678 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets, chose que nous détaillerons cela dans le chapitre suivant. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Cependant, beaucoup de programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. L'OS devait être programmé pour utiliser le mode protégé et ce n'est qu'à partir de Windows 3.0 que ce fut le cas. Il faut noter que le DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Et beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode virtuel 8086 faisait que l'OS pouvait adresser 16 mébioctets de mémoire sur le 286, 4 gigaoctets sur le 386 et plus. Par contre, les applications étaient limitées au mébioctet du mode réel. Cependant, le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé le ''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. L'intermédiaire ''Extended Memory Manager'' s'occupe d’échanger des données entre mémoire conventionnelle et mémoire étendue. Pour cela, il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory'', les deux ne sont pas la même chose. L'''expanded memory'' autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute, géré par un système de commutation de banque. Elle fonctionne sans mode protégé. La mémoire étendue ne gère pas de commutation de banque. Et les va-et-vient avec la mémoire conventionnelle demandent de switcher entre mode réel et protégé et demande donc l'existence de ce dernier. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques pour que de la RAM soit traitée soit comme mémoire étendue, soit comme ''expanded memory''. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. Le système d'exploitation pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> qi68u9hwyhv9prhpd60ka7sco6dwicy 744685 744678 2025-06-13T17:32:42Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744685 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets, chose que nous détaillerons cela dans le chapitre suivant. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Cependant, beaucoup de programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes DOS de fonctionner en mode réel. Par exemple, Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche sous-jacent comme Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode virtuel 8086 faisait que l'OS pouvait adresser 16 mébioctets de mémoire sur le 286, 4 gigaoctets sur le 386 et plus. Par contre, les applications étaient limitées au mébioctet du mode réel. Cependant, le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé le ''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. L'intermédiaire ''Extended Memory Manager'' s'occupe d’échanger des données entre mémoire conventionnelle et mémoire étendue. Pour cela, il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory'', les deux ne sont pas la même chose. L'''expanded memory'' autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute, géré par un système de commutation de banque. Elle fonctionne sans mode protégé. La mémoire étendue ne gère pas de commutation de banque. Et les va-et-vient avec la mémoire conventionnelle demandent de switcher entre mode réel et protégé et demande donc l'existence de ce dernier. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques pour que de la RAM soit traitée soit comme mémoire étendue, soit comme ''expanded memory''. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. Le système d'exploitation pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> ebwcoay4mjaegwmdo1euh1y0bc54oaw 744686 744685 2025-06-13T17:38:23Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744686 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets, chose que nous détaillerons cela dans le chapitre suivant. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes DOS de fonctionner en mode réel. Par exemple, Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche sous-jacent comme Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Les applications DOS émulées sont censées être limitées au mébioctet du mode réel. Cependant, le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé le ''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. L'intermédiaire ''Extended Memory Manager'' s'occupe d’échanger des données entre mémoire conventionnelle et mémoire étendue. Pour cela, il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory'', les deux ne sont pas la même chose. L'''expanded memory'' autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute, géré par un système de commutation de banque. Elle fonctionne sans mode protégé. La mémoire étendue ne gère pas de commutation de banque. Et les va-et-vient avec la mémoire conventionnelle demandent de switcher entre mode réel et protégé et demande donc l'existence de ce dernier. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques pour que de la RAM soit traitée soit comme mémoire étendue, soit comme ''expanded memory''. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. Le système d'exploitation pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 5366u03erq2cuqmem39kw1lqd5n4kf1 744689 744686 2025-06-13T17:45:19Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744689 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets, chose que nous détaillerons cela dans le chapitre suivant. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes DOS de fonctionner en mode réel. Par exemple, Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche sous-jacent comme Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé le ''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. Pour cela, l'''Extended Memory Manager'' il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory'', les deux ne sont pas la même chose. L'''expanded memory'' autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute, géré par un système de commutation de banque. Elle fonctionne sans mode protégé. La mémoire étendue ne gère pas de commutation de banque. Et les va-et-vient avec la mémoire conventionnelle demandent de switcher entre mode réel et protégé et demande donc l'existence de ce dernier. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques pour que de la RAM soit traitée soit comme mémoire étendue, soit comme ''expanded memory''. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. Le système d'exploitation pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> gx2qra15m136zk713k3d2kewheg080w 744690 744689 2025-06-13T17:47:40Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744690 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets, chose que nous détaillerons cela dans le chapitre suivant. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes DOS de fonctionner en mode réel. Par exemple, Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche sous-jacent comme Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé le ''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS) qui communiquait avec un Windows exécuté en mode protégé. Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. Pour cela, l'''Extended Memory Manager'' il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory''. L'''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais demande d'utiliser le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques pour, mais une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> cff2tkmlodbizhnni7x1phdzf567r08 744691 744690 2025-06-13T17:50:39Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744691 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets, chose que nous détaillerons cela dans le chapitre suivant. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes DOS de fonctionner en mode réel. Par exemple, Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche sous-jacent comme Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé l'''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS) qui communiquait avec un Windows exécuté en mode protégé. Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. Pour cela, l'EMM switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> t4znrtmwzzudr8gn98anj46bw61u22z 744692 744691 2025-06-13T17:52:34Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744692 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes DOS de fonctionner en mode réel. Par exemple, Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche sous-jacent comme Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé l'''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS) qui communiquait avec un Windows exécuté en mode protégé. Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. Pour cela, l'EMM switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> m38m0rn1tygdmnz0y4z2sry2t1h5kxj 744693 744692 2025-06-13T17:53:06Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744693 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes DOS de fonctionner en mode réel. Par exemple, Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche sous-jacent comme Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé l'''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS) qui communiquait avec un Windows exécuté en mode protégé. Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. Pour cela, l'EMM switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> mdtbbmdemdv7fvq06ybf981rn0pwcui 744695 744693 2025-06-13T17:59:15Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744695 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation qui permet à des programmes DOS de fonctionner en mode réel. Par exemple, Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche sous-jacent comme Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, appelé l'''Extended Memory Manager'' (EMM), qui est concrètement implémenté par un driver sur DOS (HIMEM.SYS) qui switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 9csk4ewulr0p2rdiufek3bkovj1ts1q 744697 744695 2025-06-13T18:37:18Z Mewtow 31375 /* La memory map des PC IBM x86 */ 744697 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour permettre aux applications DOS de profiter de la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un intermédiaire logiciel, typiquement un driver DOS (HIMEM.SYS). Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> i9xcvfmsk54u5jg6610ovev74y81wd9 744698 744697 2025-06-13T18:43:39Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744698 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un intermédiaire logiciel, typiquement un driver DOS (HIMEM.SYS). Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> kbcrf6lwjivnuzlvujgfvinhlg0t3w3 744699 744698 2025-06-13T18:44:23Z Mewtow 31375 /* Le mode protégé : l'adressage sur 24 et 32 bits */ 744699 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> llsoy5kodg2vt9skstqoklpj9zssvfs 744700 744699 2025-06-13T18:44:48Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744700 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> krmsm0d1kyf4dlrlgc3oz63izfseh83 744701 744700 2025-06-13T18:45:11Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744701 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. Le tout peut être résumé ainsi : [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> llsoy5kodg2vt9skstqoklpj9zssvfs 744702 744701 2025-06-13T18:46:52Z Mewtow 31375 /* La memory map des PC IBM x86 */ 744702 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> mhp94p9jwdn52zjaj0h3xtji5ryjl9m 744703 744702 2025-06-13T18:51:29Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744703 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] [[File:Expanded memory description 1.svg|vignette|upright=0.75|''Expanded memory'', description. A gauche, la mémoire RAM de l'ordinateur. A droite, la mémoire installée sur la carte d'extension. La page de 64 kibioctets est en violet, vous voyez qu'elle est dans la mémoire haute.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets placé dans la mémoire haute, à une position très précise. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 4lex7xq4wcoqemljm1r0qboze2zh1vt 744704 744703 2025-06-13T18:52:21Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744704 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 78nf2uz8afi3d217l5v3rrff88h2o6q 744705 744704 2025-06-13T18:52:45Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744705 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. [[File:Gestion de la mémoire sur les OS monoprogrammés.png|centre|vignette|upright=2.0|Gestion de la mémoire sur les OS monoprogrammés.]] D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> hrt7s1l30wq4cb3sodmfgvjak2mvqjg 744706 744705 2025-06-13T18:56:56Z Mewtow 31375 /* Un seul espace d'adressage, partagé avec le système d'exploitation */ 744706 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. D'ordinaire, le système d'exploitation est est en mémoire RAM, comme le programme à lancer. Il faut alors copier le système d'exploitation dans la RAM, depuis une mémoire de masse. Sur d'autres systèmes, le système d'exploitation est placé dans une mémoire ROM. C'est le cas si le système d'exploitation prend très peu de mémoire, comme c'est le cas sur les systèmes les plus anciens, ou encore sur certains systèmes embarqués rudimentaires. Les deux cas font généralement un usage différent de l'espace d'adressage. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> bp6xa6k00rutw75lklcpq4dym0mm6l4 744707 744706 2025-06-13T18:58:48Z Mewtow 31375 /* Un seul espace d'adressage, partagé avec le système d'exploitation */ 744707 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et bien d'autres fonctionnalités qui sont activées uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent en mode 64 bits. Les programmes 32 et 16 bits s'exécutent eux dans un sous-mode de compatibilité dédié. Sur les systèmes 64 bits, l'espace d'adressage est énorme : plusieurs millions de téraoctets. Mais le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 55rd61h9fs5ah9pb7zue11jljkobyeo 744708 744707 2025-06-13T19:00:00Z Mewtow 31375 /* Le mode long : l'adressage 64 bits */ 744708 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Sur les ordinateurs avec un seul espace d'adressage, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> gn5uysyjsieitig5a3t0i2ptm7v4vp6 744709 744708 2025-06-13T19:00:59Z Mewtow 31375 /* Les entrées-sorties mappées en mémoire */ 744709 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> n2ivi4k3gshq8rlhmfz9nlp9y5sgth0 744710 744709 2025-06-13T19:01:12Z Mewtow 31375 /* L'adressage des périphériques */ 744710 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. L'idée est que le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers le périphérique en question. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque ainsi le défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la mémoire RAM. Dit autrement, on ne peut plus adresser autant de mémoire qu'avant. La perte peut être très légère ou très importante, en fonction des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait autrefois un problème assez connu sur les ordinateurs 32 bits, qui ne géraient que 2^32 octets = 4 gibioctets. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Notons qu'il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes 32 et 64 bits, avec un bus PCI-Express d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 3f07orxn4h5k492oqh5cc035c02cfdm 744711 744710 2025-06-13T19:05:14Z Mewtow 31375 /* Les entrées-sorties mappées en mémoire */ 744711 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. [[File:Partitions mémoire.png|centre|vignette|upright=2|Partitions mémoire]] Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 3x6wiygfc5gqtr6nnqe1cj6fp6zs7ng 744712 744711 2025-06-13T19:09:14Z Mewtow 31375 /* Un seul espace d'adressage, partagé entre plusieurs programmes */ 744712 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> jmx3dslj8b3kbjiedtgu2f59v8wyto0 744713 744712 2025-06-13T19:09:37Z Mewtow 31375 /* Un seul espace d'adressage, partagé entre plusieurs programmes */ 744713 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui consiste à créer une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur, et elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, alors une erreur est levée. L'intérêt est une question de sécurité. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle. En cas de modification du pointeur, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> bcop4hn0h7snfwl4y5u6y158edsr8z0 744714 744713 2025-06-13T19:12:50Z Mewtow 31375 /* Le mode long : l'adressage 64 bits */ 744714 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui crée une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur. Elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, une erreur est levée. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 2vfhy5opehra9yiyd2qnl3dj1g1qgz7 744715 744714 2025-06-13T19:13:07Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744715 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui crée une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur. Elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, une erreur est levée. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 2m4cibxqrr9i5l1a6aicwwjugjng3xl 744716 744715 2025-06-13T19:13:21Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744716 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était disponible dans cette optique. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui interagissait avec les programmes via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui crée une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur. Elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, une erreur est levée. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 2vfhy5opehra9yiyd2qnl3dj1g1qgz7 744717 744716 2025-06-13T19:14:43Z Mewtow 31375 /* Interlude : les technologies d'expanded memory */ 744717 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était libre. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui était appelé via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui crée une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur. Elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, une erreur est levée. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Sur ARM, il s'agit de la technique du TBI : ''Top Byte Ignore''. Chez Intel et AMD, il s'agit des fonctionnalités UAI (''Upper Address Ignore'') d'AMD et de LAM (''Linear Address Masking'') d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> 52mfv0fds2hflkycozdo05kwjfc0f70 744718 744717 2025-06-13T19:16:23Z Mewtow 31375 /* Le mode long : l'adressage 64 bits */ 744718 wikitext text/x-wiki L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, comme nous le verrons plus bas. Nous verrons aussi qu'un processeur peut avoir plusieurs espaces d'adressages séparés. ==L'adressage de la RAM et de la ROM== Avoir plusieurs espaces d'adressage spécialisés est quelque chose que nous avons déjà rencontré dans les chapitres précédents, mais sans le dire ouvertement. Aussi, nous allons faire quelques rappels sur les cas déjà rencontrés. En premier lieu, nous allons rappeler la différence entre architectures Von Neumann et Hardvard. La différence entre les deux tient dans l'adressage des mémoires RAM et ROM : est-ce qu'elles sont dans un seul espace d'adressage, ou dans des espaces d'adressage séparés. L''''architecture Von Neumann''' a un seul espace d'adressage, découpé entre la mémoire RAM d'un côté et la mémoire ROM de l'autre. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM est placée dans les adresses hautes, les plus élevées, alors que la RAM est placée dans les adresses basses en commençant par l'adresse 0. C'est une convention qui n'est pas toujours respectée, aussi mieux vaut éviter de la tenir pour acquise. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] L''''architecture Harvard''' utilise des espaces d'adressage séparés pour la RAM et la ROM. Une même adresse peut correspondre soit à la ROM, soit à la RAM. Le processeur voit bien deux mémoires séparées, chacune dans son propre espace d'adressage. Les deux espaces d'adressage n'ont pas forcément la même taille. Il est possible d'avoir un plus gros espace d'adressage pour la RAM que pour la ROM. Cela implique que les adresses des instructions et des données soient de taille différentes. C'est peu pratique et c'est rarement implémenté, ce qui fait que le cas le plus courant est celui où les deux espaces d'adressages ont la même taille. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] ==L'adressage des périphériques== Passons maintenant à l'adressage des périphériques. La communication avec les périphériques se fait par l'intermédiaire de registres d’interfaçage. Et ces registres peuvent soit avoir un espace d'adressage séparé, soit être inclus dans l'espace d'adressage des mémoires. Dans ce qui suit, nous allons supposer que l'architecture des de type Von Neumann pour simplifier les explications. ===Les entrées-sorties mappées en mémoire=== Une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques. Le périphérique se retrouve inclus dans l'ensemble des adresses utilisées pour manipuler la mémoire : on dit qu'il est mappé en mémoire. Les adresses mémoires associées à un périphérique sont redirigées automatiquement vers les registres du périphérique en question, voire vers sa mémoire intégrée. On parle alors d''''entrées-sorties mappées en mémoire'''. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] On remarque un défaut inhérent à cette technique : les adresses utilisées pour les périphériques ne sont plus disponibles pour la RAM. On ne peut plus adresser autant de RAM qu'avant. La perte dépend des périphériques installés et de leur gourmandise en adresses mémoires. C'est ce qui causait un problème assez connu sur les ordinateurs 32 bits, capables d'adresser 4 gibioctets de mémoire. Certaines personnes installaient 4 gigaoctets de mémoire sur leur ordinateur 32 bits et se retrouvaient avec « seulement » 3,5 à 3,8 gigaoctets de mémoire, les périphériques prenant le reste. Il est possible que la RAM d'un périphérique soit mappée en RAM. Un exemple classique est celui des cartes graphiques qui incorporent une mémoire RAM appelée la mémoire vidéo. La mémoire vidéo est mappée en mémoire, ce qui permet au processeur d'écrire directement dedans. Toute lecture ou écriture dans les adresses associées est redirigée vers le bus PCI/AGP/PCI-Express. Nous verrons plus bas des exemples d'ordinateurs où la mémoire vidéo est mappée en mémoire, que ce soit totalement ou partiellement. Si je dis totalement ou partiellement, c'est parce que les cartes graphiques modernes disposent de tellement de mémoire qu'on ne peut pas la mapper totalement dans l'espace d'adressage. Sur les systèmes d'avant 2008, seuls 256 mégaoctets de mémoire vidéo sont mappés en mémoire RAM. Le reste de la mémoire vidéo est invisible du point de vue du processeur, mais manipulable par le GPU à sa guise. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''Resizable Bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo. ===L'espace d'adressage séparé pour les entrées-sorties=== Les entrées-sorties et périphériques peuvent avoir leur propre espace d'adressage dédié, séparé de celui utilisé pour les mémoires RAM et ROM. [[File:Espaces_d'adressages_séparés_entre_mémoire_et_périphérique.png|centre|vignette|upright=3|Bit IO.]] Une même adresse peut donc adresser soit une entrée-sortie, soit une case mémoire. Et pour faire la différence, le processeur doit avoir des instructions séparées pour adresser les périphériques et la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. Cela élimine aussi les problèmes avec les caches : les accès à l'espace d'adressage de la RAM passent par l'intermédiaire de la mémoire cache, alors les accès dans l'espace d'adressage des périphériques le contournent totalement. Là encore, les deux espaces d'adressage n'ont pas forcément la même taille. Il arrive que les deux espaces d'adressage aient la même taille, le plus souvent sur des ordinateurs complexes avec beaucoup de périphériques. Mais les systèmes embarqués ont souvent des espaces d'adressage plus petits pour les périphériques que pour la ou les mémoires. L'implémentation varie grandement suivant le cas, la première méthode imposant d'avoir deux bus séparés pour les mémoires et les périphériques, l'autre permettant un certain partage du bus d'adresse. Nous reviendrons dessus plus en détail dans le chapitre sur l'adressage des périphériques. ==La ''memory map'' d'un ordinateur== Les deux sections précédentes nous ont appris que l'on peut utiliser un espace d'adressage séparé pour la ROM et un autre pour les périphériques. En tout, cela donne quatre possibilités distinctes. {|class="wikitable" |- ! ! IO mappées en mémoire ! IO séparées |- ! ROM mappée en mémoire (architecture Von Neumann) | Un seul espace d'adressage | Deux espaces d'adressage : * un pour les mémoires RAM/ROM ; * un pour les IO. |- ! ROM séparée (architecture Harvard) | Deux espaces d'adressage : * un pour la RAM et les IO ; * un pour la ROM. | Trois espaces d'adressages : * un pour la RAM ; * un pour la ROM ; * un pour les IO. |} Les quatre solutions ont des avantages et inconvénients divers, mais il est intéressant de contraster un espace d'adressage unique avec plusieurs espaces d'adressages. ===L'espace d'adressage unique=== Avec un espace d'adressage unique, la ROM est au sommet de l'espace d'adressage, les périphériques sont juste en-dessous, la RAM commence à l'adresse 0 et prend les adresses basses. Faire simplifie grandement l'implémentation matérielle de l'adressage. Notons que d'autres composants que les périphériques ou les mémoires peuvent se trouver dans l'espace d'adressage. On peut y trouver les horloges temps réels, des timers, des senseurs de température, ou d'autres composants placés sur la carte mère. Un exemple un peu original est le suivant : la console de jeu Nintendo DS incorporait une unité de calcul spécialisée dans les divisions et racines carrées, séparée du processeur, qui était justement mappée en mémoire ! [[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]] L'espace d'adressage unique est plus simple pour les programmeurs, malgré un désavantage dont il faut parler. Le problème est que des adresses censées être disponibles pour la RAM sont détournées vers la ROM ou les périphériques, ce qui limite la quantité de RAM qui peut être réellement adressée en pratique. C'est un défaut qui se manifeste seulement pour les petits espaces d'adressages, de 8 à 16 bits, qui ne permettent pas d'adresser beaucoup de RAM. Avec des adresses codées sur 16 bits, un espace d'adressage de 64 kibioctets de RAM est grignoté par des entrées-sorties assez rapidement. Par contre, pour des adresses de 32 à 64 bits, une bonne partie de l'espace d'adressage est inutilisé, car il n'y a pas assez de RAM. L'espace d'adressage unique n'a donc pas de désavantages. Les vielles consoles de jeux et certains vieux ordinateurs (Commodores et Amiga), utilisaient un espace d'adressage unique. Elles n'avaient pas une variété de cartes graphiques ou de cartes sons différentes, comme sur les PCs modernes. Au contraire, elles avaient la même configuration matérielle, le matériel ne pouvait pas être changé ni upgradé. Le système d'exploitation était rudimentaire et ne contrôlait pas vraiment l'accès au matériel. Les programmeurs avaient donc totalement accès au matériel et mapper les entrées/sorties en mémoire rendait la programmation des périphériques très simple. <gallery widths=200px heights=500px> N5200mk2 memory map.svg|Adressage mémoire (carte mémoire) du N5200mk2. PC-9801VM memory map.svg|Adressage mémoire (carte mémoire) du PC-9801VM. </gallery> ===La commutation de banques (''bank switching'')=== Le '''''bank switching''''', aussi appelé '''commutation de banque''', permet d'utiliser plusieurs espaces d'adressage sur un même processeur, sans attribuer chaque espace d'adressage pour une raison précise. L'espace d'adressage est présent en plusieurs exemplaires appelés des '''banques'''. Les banques sont numérotées, chaque numéro de banque permettant de l'identifier et de le sélectionner. Le but de cette technique est d'augmenter la mémoire disponible pour l'ordinateur. Par exemple, supposons que j'ai besoin d'adresser une mémoire ROM de 4 kibioctets, une RAM de 8 kibioctets, et divers périphériques. Le processeur a un bus d'adresse de 12 bits, ce qui limite l'espace d'adressage à 4 kibioctets. Dans ce cas, je peux réserver 4 banques : une pour la ROM, une pour les périphériques, et deux banques qui contiennent chacune la moitié de la RAM. La simplicité et l'efficacité de cette technique font qu'elle est beaucoup utilisée dans l'informatique embarquée. [[File:PC-8801MemoryMap.gif|centre|vignette|upright=2|exemple de Bank switching.]] Cette technique demande d'utiliser un bus d'adresse plus grand que les adresses du processeur. L'adresse réelle se calcule en concaténant le numéro de banque avec l'adresse accédée. Le numéro de la banque actuellement en cours d'utilisation est mémorisé dans un registre appelé le '''registre de banque'''. On peut changer de banque en changeant le contenu de ce registre. Le processeur dispose souvent d'instructions spécialisées qui en sont capables. {| class="wikitable flexible" |[[File:Banque mémoire.png|Banque mémoire.]] |[[File:Registre de banque.png|Registre de banque.]] |} ==Le placement des programmes dans l'espace d'adressage== L'espace d'adressage inclut des périphériques, de la ROM, mais aussi une RAM. Sur les ordinateurs modernes, le système d'exploitation et les programmes sont copiés en mémoire RAM avant d'être exécutés. Ils ne sont pas exécutés depuis une mémoire ROM, cela est réservé aux systèmes embarqués simples. Et cela a une influence sur l'espace d'adressage. Les programmes et le système d'exploitation doivent se partager l'espace d'adressage. Voyons voir comment ce partage se fait. Dans ce chapitre, nous allons volontairement mettre de côté les ordinateurs qui supportent la mémoire virtuelle. Nous en parlerons dans le prochain chapitre. Le principe est de voir comment un processeur avec un seul espace d'adressage gèrent la présence d'un ou de plusieurs programmes. ===Un seul espace d'adressage, non-partagé=== Le cas le plus simple est celui où il n'y a pas de système d'exploitation et où un seul programme s'exécute sur l'ordinateur. Le programme a alors accès à tout l'espace d'adressage. L'usage qu'il fait du ou des espaces d'adressage dépend de si on est sur une architecture Von Neumann ou Harvard. Sur une architecture Von Neumann, le programme et ses données sont placées dans le même espace d'adressage. Le programme organise la mémoire en plusieurs sections, dans lesquelles le programme range des données différentes. Typiquement, on trouve quatre blocs de mémoire, appelés des segments, chacun étant spécialisé pour les données, le code, la pile, etc. Voici ces trois sections : * Le segment '''''text''''' contient le code machine du programme, de taille fixe. * Le segment '''''data''''' contient des données de taille fixe qui occupent de la mémoire de façon permanente. * Le segment pour la '''pile''', de taille variable. * le reste est appelé le '''tas''', de taille variable. [[File:Organisation d'un espace d'adressage unique utilisé par un programme unique.png|centre|vignette|upright=2|Organisation d'un espace d'adressage unique utilisé par un programme unique]] Sur une architecture Harvard, le programme est placé dans un espace d'adressage à part du reste. Le problème, c'est que cet espace d'adressage ne contient pas que le code machine à exécuter. Il contient aussi des constantes, à savoir des données qui gardent la même valeur lors de l'exécution du programme. Elles peuvent être lues, mais pas modifiées durant l'exécution du programme. L'accès à ces constantes demande d'aller lire celles-ci dans l'autre espace d'adressage, pour les copier dans l'autre espace d'adressage. Pour cela, les architectures Harvard modifiées ajoutent des instructions pour copier les constantes d'un espace d'adressage à l'autre. [[File:Organisation des espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=3|Organisation des espaces d'adressage sur une archi harvard modifiée]] [[File:Typical computer data memory arrangement.png|vignette|upright=0.5|Typical computer data memory arrangement]] La pile et le tas sont de taille variable, ce qui veut dire qu'ils peuvent grandir ou diminuer à volonté, contrairement au reste. Entre le tas et la pile, on trouve un espace de mémoire inutilisée, qui peut être réquisitionné selon les besoins. La pile commence généralement à l'adresse la plus haute et grandit en descendant, alors que le tas grandit en remontant vers les adresses hautes. Il s'agit là d'une convention, rien de plus. Il est possible d'inverser la pile et le tas sans problème, c'est juste que cette organisation est rentrée dans les usages. Avant de poursuivre, précisons qu'il est possible de regrouper plusieurs programmes distincts dans un seul, afin qu'ils partagent le même morceau de mémoire. Les programmes portent alors le nom de '''threads'''. Les ''threads'' d'un même processus partagent le même espace d'adressage. Ils partagent généralement certains segments : ils se partagent le code, le tas et les données statiques. Par contre, chaque thread dispose de sa propre pile d'appel. [[File:Single vs multithreaded processes.jpg|centre|vignette|upright=2.0|Distinction entre processus mono et multi-thread.]] Il va de soi que cette vision de l'espace d'adressage ne tient pas compte des périphériques. C'est-à-dire que les schémas précédents partent du principe qu'on a un espace d'adressage séparé pour les périphériques. Dans le cas où les entrées-sorties sont mappées en mémoire, l'organisation est plus compliquée. Généralement, les adresses associées aux périphériques sont placées juste au-dessus de la pile, dans les adresses hautes. ===Un seul espace d'adressage, partagé avec le système d'exploitation=== [[File:Gestion de la mémoire sur les OS monoprogrammés.png|vignette|upright=1.0|Gestion de la mémoire sur les OS monoprogrammés.]] Maintenant, étudions le cas où le programme partage la mémoire avec le système d'exploitation. Sur les systèmes d'exploitation les plus simples, on ne peut lancer qu'un seul programme à la fois. Le système d'exploitation réserve une portion de taille fixe réservée au système d'exploitation, alors que le reste de la mémoire est utilisé pour le programme à exécuter. Le programme est placé à un endroit en RAM qui est toujours le même. Le système d'exploitation peut être soit placé dans une ROM, soit copié en mémoire RAM depuis une mémoire de masse, comme le programme à lancer. Si le système d'exploitation est copié en mémoire RAM, il est généralement placé dans les premières adresses, les adresses basses. A l'inverse, un OS en mémoire ROM est généralement placé à la fin de la mémoire, dans les adresses hautes. Mais tout cela n'est qu'une convention, et les exceptions sont monnaie courante. Il existe aussi une organisation intermédiaire, où le système d'exploitation est chargé en RAM, mais utilise des mémoires ROM annexes, qui servent souvent pour accéder aux périphériques. On a alors un mélange des deux techniques précédentes : l'OS est situé au début de la mémoire, alors que les périphériques sont à la fin, et les programmes au milieu. [[File:Méthodes d'allocation de la mémoire avec un espace d'adressage unique.png|centre|vignette|upright=2.0|Méthodes d'allocation de la mémoire avec un espace d'adressage unique]] Sur de tels systèmes, il faut protéger l'OS contre une erreur ou malveillance d'un programme utilisateur. Notons qu'il s'agit d'une protection en écriture, pas en lecture. Le programme peut parfaitement lire les données de l'OS sans problèmes, et c'est même nécessaire pour certaines opérations courantes, comme les appels systèmes. Par contre, il faut rendre impossible au programme d'écrire dans la portion mémoire réservée au système d'exploitation. Si le système d'exploitation est placé dans une mémoire ROM, les écritures sont impossibles, l'OS est naturellement protégé. Mais dans le cas où le système d'exploitation est chargé en RAM, il faut prémunir l'OS contre des écritures fautives/malveillantes. Dans ce qui suit, on part du principe que l'OS est dans les adresses basses. La solution la plus courante interdit au programme d'écrire au-delà d'une limite au-delà de laquelle se trouve le système d’exploitation. Pour cela, le processeur incorpore un '''registre limite''', qui contient l'adresse limite au-delà de laquelle un programme peut pas écrire. Pour toute écriture en espace utilisateur, l'adresse écrite est comparée au registre limite. Si l'adresse est inférieure au registre limite, le programme cherche écrire dans la mémoire réservée à l'OS. L’accès mémoire est interdit, une exception matérielle est levée et l'OS affiche un message d'erreur. [[File:Circuit total.png|centre|vignette|upright=2.0|Protection mémoire avec un registre limite.]] ===Un seul espace d'adressage, partagé entre plusieurs programmes=== Les systèmes d’exploitation modernes implémentent la '''multiprogrammation''', le fait de pouvoir lancer plusieurs logiciels en même temps. Et ce même si un seul processeur est présent dans l'ordinateur : les logiciels sont alors exécutés à tour de rôle. Un point important est le partage de la RAM entre les différents programmes. Les différents programmes et leurs données sont placés en mémoire RAM, et il faut trouver un moyen pour répartir les différents programmes en RAM. Il y a plusieurs solutions pour cela, mais l'une d'entre elle est l'usage de segments mémoire. [[File:Partitions mémoire.png|vignette|upright=1|Partitions mémoire]] Avec elle, le système d'exploitation répartit les différents programmes dans la mémoire RAM et chaque programme se voit attribuer un ou plusieurs blocs de mémoire. Ils sont appelés des '''partitions mémoire''', ou encore des '''segments'''. Dans ce qui va suivre, nous allons parler de segments, pour simplifier les explications. Nous allons partir du principe qu'un programme est égal à un segment, pour simplifier les explications, mais sachez qu'un programme peut être éclaté en plusieurs segments dispersés dans la mémoire, et même être conçu pour ! Nous en reparlerons dans le chapitre sur le mémoire virtuelle. Le système d'exploitation mémorise une liste des segments importants en mémoire RAM, appelée la ''table des segments''. Il s'agit d'un tableau dont chaque entrée mémorise les informations pour un segment. Une entrée de ce tableau est appelée un '''descripteur de segment''' et contient son adresse de base, sa taille, et des autorisations liées à la protection mémoire. Précisément, l'entrée numéro N mémorise les informations du segment numéro N. A partir de l'adresse de base de la table des segments et du numéro de segment, on peut lire ou écrire un descripteur de segment. [[File:Global Descriptor table.png|centre|vignette|upright=2|Global Descriptor table]] Toutefois, l'usage de segments amène un paquet de problèmes qu'il faut résoudre au mieux. Le premier problème est tout simplement de placer les segments dans l'espace d'adressage, mais c'est quelque chose qui est du ressort du système d'exploitation. Par contre, cela implique qu'un segment peut être placé n'importe où en RAM et sa position en RAM change à chaque exécution. En conséquence, les adresses des branchements et des données ne sont jamais les mêmes d'une exécution à l'autre. Pour résoudre ce problème, le compilateur considère que le segment commence à l'adresse zéro, puis les adresses sont corrigées avant ou pendant l'exécution du programme. Cette correction est en général réalisée par l'OS lors de la copie du programme en mémoire, mais il existe aussi des techniques matérielles, que nous verrons dans le chapitre suivant. Il faut aussi prendre en compte le phénomène de l''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent déclamer de la mémoire au système d'exploitation, pour y placer des données. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. S'il n'y a pas assez de mémoire, l'appel échoue et le programme doit gérer la situation. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec des segments, cela revient à changer la taille d'un segment, plus précisément à la fin du segment. Et tout cela est géré par le système d'exploitation, aussi on laisse cela pour le prochain chapitre. Enfin, sans protection particulière, les programmes peuvent techniquement lire ou écrire les données des autres. Si un programme pouvait modifier les données d'un autre programme, on se retrouverait rapidement avec une situation non prévue par le programmeur, avec des conséquences qui vont d'un joli plantage à des failles de sécurité dangereuses. Il faut donc introduire des mécanismes de protection mémoire, pour isoler les programmes les uns des autres, et éviter toute modification problématique. La protection mémoire regroupe plusieurs techniques assez variées, qui ont des objectifs différents. Elles ont pour point commun de faire intervenir à des niveaux divers le système d'exploitation et le processeur. ==La ''memory map'' des PC IBM x86== Un autre exemple est celui des ordinateurs PC, avec des processeurs x86. L'organisation de leur espace d'adressage a évolué dans le temps et l'espace d'adressage s'est adapté. Mais il s'est adapté sans modifier l'existant, pour des raisons de compatibilité. Aussi, les processeurs x86 modernes peuvent fonctionner dans plusieurs modes, appelés mode réel, protégé, virtuel 8086, long, ''system management mode'', et compatibilité 32 bits. [[File:AMD64StateDiagram.svg|centre|vignette|upright=2|AMD64StateDiagram]] Le ''system management mode'' a déjà été abordé dans le chapitre sur les interruptions. Pour simplifier, il ne s'agit pas d'un véritable mode au sens "adressage". Dans ce mode, le système d'exploitation est mis en pause et le BIOS ou un autre firmware s'exécute sans restriction. On y rentre grâce à une interruption dédiée, lancée par le noyau de l'OS. Le système d'exploitation exécute cette interruption afin de laisser la main au BIOS, pour lui déléguer certaines tâches. Par contre, les autres modes sont bien plus intéressant, car ils ont chacun un espace d'adressage différent. Voyons-les dans le détail. ===Le mode réel : l'adressage sur 20 bits=== Le tout premier mode était le '''mode réel''', qui utilisait des adresses de 20 bits, ce qui fait 1 mébioctet de mémoire adressable. Il était utilisé sur des processeurs 16 bits, qui gérait donc des adresses de 16 bits capables d'adresser 64 kibioctets de mémoire. Pour contourner cette limitation, la mémoire en mode réel était composée de 16 blocs de 64 kibioctets. Pour générer une adresse de 20 bits, le processeur contient un registre de 4 bits qui précise quel bloc de 64 kibioctet est couramment adressé. En somme, une sorte de commutation de banque intégrée au processeur. : La réalité est plus complexe, comme on le verra dans le prochain chapitre, mais cette explication simpliste suffira pour le moment. Le processeur démarre systématiquement en mode réel, y compris sur les ordinateurs modernes. Le BIOS s'exécute dans ce mode, avant de passer en mode protégé et de laisser la main à l'UEFI et au système d'exploitation. Autrefois, le système d'exploitation s'exécutait aussi en mode réel, c'est le cas du DOS. En mode réel, l'espace d'adressage est peuplé par de la RAM, la ROM du BIOS et des périphériques. La RAM prenait les adresses basses, la ROM était au sommet de l'espace d'adressage, et les périphériques entre les deux. Les premiers 640 kibioctets sont réservés à la RAM et sont appelés la mémoire conventionnelle. Les octets restants forment la '''mémoire haute''', réservée aux ROM et aux périphériques. Elle est sectionnée en deux portions. Le sommet de la mémoire haute est réservé au BIOS, alors que le bas de la mémoire haute est réservé aux périphériques. Dans cette dernière, on trouve les BIOS des périphériques (dont celui de la carte vidéo, s'il existe), qui sont nécessaires pour les initialiser et pour communiquer avec eux. De plus, on y trouve la mémoire de la carte vidéo et éventuellement la mémoire d'autres périphériques comme la carte son. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 1024 - 960 Kio || class="f_jaune" | BIOS principal (ROM) |- | 960 Kio - 640 Kio | class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) </br> Mémoire vidéo des cartes EGA, MDA ou CGA |- | 640 Kio - 0 || class="f_rouge" | Mémoire RAM, mémoire conventionnelle |} Pour détailler un peu plus, il faut tenir compte du découpage de l'espace d'adressage en blocs de 64 kibioctets. Le découpage en blocs de 64 kibioctets est particulièrement important pour la mémoire haute. Des blocs sont attribués à un périphérique spécifique. Par exemple, le 16ème bloc est réservé au BIOS, le 12ème aux ROM des périphériques, le 10ème et le 11ème à la mémoire vidéo, etc. {| class="wikitable" style="text-align:center;" |+ Espace d'adressage en mode réel |- ! Numéro du bloc !! Contenu du bloc |- | 0 || rowspan="10" class="f_rouge" | Mémoire RAM, mémoire conventionnelle |- | 1 |- | 2 |- | 3 |- | 4 |- | 5 |- | 6 |- | 7 |- | 8 |- | 9 |- | 10 || class="f_vert" | Mémoire vidéo des cartes EGA |- | 11 || class="f_vert" | Mémoire vidéo des cartes MDA ou CGA |- | 12 || class="f_vert" | ROM d'extension des périphériques (XT, EGA, 3270 PC) |- | 13 || class="f_gris" | Autre, non-réservé |- | 14 || class="f_gris" | Autre, non-réservé |- | 15 || class="f_jaune" | ROM du BIOS |} Vous remarquerez que les blocs 13 et 14 sont non-réservés. Ils peuvent être utilisés pour mapper des périphériques en mémoire. Mais si aucun périphérique n'est mappé dedans, les blocs sont laissés libres. Les OS n'hésitaient pas à les utiliser pour adresser de la RAM. Par exemple, si l'ordinateur n'a pas de carte vidéo EGA, le bloc qui leur était réservé était réutilisé comme mémoire RAM. La mémoire conventionnelle passait alors de 640 à 704 kibioctets de RAM disponibles. Les deux premiers kibioctets de la mémoire conventionnelle sont initialisés au démarrage de l'ordinateur. Ils sont utilisés pour stocker le vecteur d'interruption (on expliquera cela dans quelques chapitres) et servent aussi au BIOS. La portion réservée au BIOS, la ''BIOS Data Area'', mémorise des informations en RAM. Elle commence à l'adresse 0040:0000h, a une taille de 255 octets, et est initialisée lors du démarrage de l'ordinateur. Le reste de la mémoire conventionnelle est réservée au le système d'exploitation (MS-DOS, avant sa version 5.0) et au programme en cours d’exécution. ===Interlude : les technologies d'expanded memory=== Déjà à l'époque, 1 mébioctet était une limite assez faible, même si cela n'a pas empêché à un grand dirigeant de l'industrie informatique de dire que "640K ought to be enough for anybody". Aussi, diverses solutions matérielles ont vu le jour. Elles étaient peu utilisées et ont concrètement servi à des applications spécifiques. La principale est l''''''expanded memory''''' (EMS), une solution utilisant une carte d'extension avec plusieurs modules de RAM installés dessus. Les modules étaient accédés via la technique de la commutation de banque. [[File:Expanded memory.svg|centre|vignette|upright=2|L'espace d'adressage avec ''Expanded memory''. La carte d'extension et le matériel requis ne sont pas représentés.]] Le terme ''expanded memory'' regroupe plusieurs technologies propriétaires semblables sur le principe mais différentes dans l'exécution. Les premières utilisaient des banques de 64 kibioctets, qui étaient mappées dans un bloc de 64 kibioctets libre en mémoire haute. L'usage de banques de 64 kibioctets collait bien avec l'adressage particulier du mode réel, où la mémoire était gérée par blocs de 64 kibioctets. Les technologies ultérieures utilisaient 2 à 4 pages de 16 kibioctets, qui pouvaient être placée n'importe où en mémoire conventionnelle. [[File:EmulexPersyst 4M ISA.jpeg|centre|vignette|upright=2|Carte d'extension ''Expanded memory'' de marque Emulex Persyst, de 4 mébioctets.]] L'''expanded memory'' demandait que les programmes soient codés pour utiliser cette fonctionnalité. Pour cela, ils devaient utiliser une technique de programmation nommée ''Overlay programming''. L'idée est de découper le programme en plusieurs blocs séparés, appelés des '''''overlays'''''. Certains blocs sont en permanence en RAM, mais d'autres sont soit chargés en RAM, soit stockés ailleurs. Le ailleurs est souvent le disque dur, ou dans la RAM ''expanded memory'' si elle est disponible. Le va-et-vient des ''overlays'' entre RAM et disque dur était réalisé en logiciel, par le programme lui-même. [[File:Overlay Programming.svg|centre|vignette|upright=1.5|''Overlay Programming''.]] L'utilisation de l'''expanded memory'' utilisait l'interruption logicielle 67h, une interruption peu utilisée à l'époque, qui était libre. La carte d'extension était utilisée à travers un pilote de périphérique dédié, qui était appelé via l'interruption 67h. ===Le mode protégé : l'adressage sur 24 et 32 bits=== Par la suite, le mode réel a été remplacé par le '''mode protégé'''. Il utilise la mémoire virtuelle, ce qui fait qu'on devrait le détailler dans le chapitre suivant. Il permettait aussi de dépasser la limite du mébioctet de mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. Pour corriger cela, le 286 a introduit le '''mode virtuel 8086''', aussi appelé mode V86. Il s'agit d'une technique de virtualisation, ce qui fait qu'on en parlera en détail dans le chapitre sur la virtualisation assistée en matériel. Tout ce que l'on peut dire ici est que le mode V86 a été détourné pour que les applications DOS utilisent la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue à travers un driver DOS dédié (HIMEM.SYS). Les logiciels DOS déplaçaient des données dans la mémoire étendue, puis les rapatriaient en mémoire conventionnelle quand ils en avaient besoin. Il ne faut pas confondre mémoire étendue et ''expanded memory''. La technique de ''expanded memory'' repose sur de la commutation de banque, demande une carte d'extension dédiée, fonctionne sans mode V86. A l'inverse, la mémoire étendue ne gère pas de commutation de banque, ne demande pas de carte d'extension, mais utilise le mode V86. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, avec la mémoire étendue. Quelques cartes mères intégraient des techniques pour, mais une émulation logicielle était aussi possible en réécrivant l'interruption 67h. ===Le mode long : l'adressage 64 bits=== Par la suite, les processeurs ont introduit le '''mode long''', dans lequel les adresses font 64 bits. Le CPU en mode long a aussi un jeu d'instruction totalement différent, des registres en plus et d'autres fonctionnalités actives uniquement dans ce mode. Les programmes compilés pour le 64 bits s'exécutent nativement dans ce mode, alors que les programmes 32 et 16 bits s'exécutent dans un mode de compatibilité dédié. Sur les systèmes 64 bits, le bus d'adresse fait seulement 48 bits afin d'économiser des interconnexions. Avec 48 bits, il manque 64 - 48 = 16 bits d'adresses, qui ne peuvent pas être utilisés pour adresser quoi que ce soit. La règle est que ces 16 bits doivent être tous égaux : soit ils valent tous 0, soient ils sont tous à 1. Les adresses qui respectent cette contrainte sont appelées des '''adresses canoniques'''. Le résultat est que l'espace d'adressage est coupé en trois sections, comme illustré ci-dessous. * Les '''adresses canoniques basses''' ont leurs 16 bits de poids fort à 0 et sont situées en bas de l'espace d'adressage, dans les premières adresses. * Les '''adresses canoniques hautes''' ont leurs 16 bits de poids fort à 1 et sont situées au sommet de l'espace d'adressage, dans les dernières adresses. * Entre les deux, se trouvent des '''adresses non-canoniques''', qui ne sont pas accessibles. Y accéder déclenche la levée d'une exception matérielle. {| |[[File:AMD64-canonical--48-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 48 bits.]] |[[File:AMD64-canonical--56-bit.png|vignette|Espace d'adressage x86-64 bits, avec des adresses physiques de 57 bits.]] |} : Les futurs systèmes x86 devraient passer à des adresses de 57 bits, les trois sections auront donc des frontières différentes. Les adresses non-canoniques sont censées être inutilisables. Mais les programmeurs aimeraient bien pouvoir les utiliser pour des ''pointeurs tagués'', à savoir des pointeurs/adresses associées à des informations. L'idée serait d'utiliser les 16 bits de poids fort pour stocker des informations liées au pointeur, seuls les 48 bits restant codant l'adresse. Les 16 bits peuvent être utilisés de manières très diverses. Un exemple est la technique du '''''memory tagging''''', qui crée une somme de contrôle au pointeur. La somme de contrôle est générée par un algorithme cryptographique, à partir de l'adresse du pointeur. Elle est vérifiée à chaque utilisation du pointeur. Si la somme de contrôle ne correspond pas au pointeur, une erreur est levée. Si jamais un virus ou un code malveillant modifie un pointeur, il ne saura pas comment calculer la somme de contrôle, la somme de contrôle a énormément de chances d'être incorrecte. Quelques fonctionnalités des processeurs visent à autoriser l'utilisation des adresses non-canoniques. L'idée est que les 16 bits de poids fort sont ignorées lors des accès mémoire, ce qui permet d'utiliser les 16 bits de poids fort à volonté. Il s'agit des techniques ''Top Byte Ignore'' d'ARM, ''Upper Address Ignore'' d'AMD, et ''Linear Address Masking'' d'Intel. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures actionnées par déplacement | prevText=Les architectures actionnées par déplacement | next=L'abstraction mémoire et la mémoire virtuelle | nextText=L'abstraction mémoire et la mémoire virtuelle }} </noinclude> bcid59wbyug6qnmh3fyf2twa053alna Discussion Wikilivres:Le Bistro/2025 5 82063 744721 744398 2025-06-13T23:00:28Z MediaWiki message delivery 36013 /* Vote now in the 2025 U4C Election */ nouvelle section 744721 wikitext text/x-wiki == Actualités techniques n° 2025-03 == <section begin="technews-2025-W03"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/03|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * Le système de connexion utilisateur unique (SUL) va être mis à jour durant les prochains mois. Il permet aux utilisateurs et utilisatrices d’être connectés sur tous les sites en même temps après avoir renseigné leurs identifiants sur un site Wikimedia. La mise à jour est nécessaire car les navigateurs restreignent de plus en plus les témoins de connexion inter-domaines. Pour s’adapter à ces restrictions, les pages de connexion et de création de compte seront déplacées vers un domaine central, mais cela apparaitra toujours comme si vous étiez sur le wiki d’origine. Le code mis à jour sera activé cette semaine pour les utilisations sur les wikis de test. Ce changement devrait être déployé pour tous durant février et mars. Consultez [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3#Deployment|la page du projet SUL3]] pour plus d’informations et un calendrier. '''Actualités pour la contribution''' * Sur les wikis ayant [[mw:Special:MyLanguage/Extension:PageAssessments|PageAssessments]] (évaluation des pages) installée, vous pouvez désormais [[mw:Special:MyLanguage/Extension:PageAssessments#Search|filtrer les résultats de recherche]] aux pages dans un projet donné à l’aide du mot-clé <code dir=ltr>inproject:</code>. (Ces wikis : {{int:project-localized-name-arwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-enwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-enwikivoyage/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-frwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-huwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-newiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-trwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-zhwiki/fr}}.) [https://phabricator.wikimedia.org/T378868] * Un nouveau wiki a été créé : une Wikipédia en [[d:Q34129|tigré]] ([[w:tig:|<code>w:tig:</code>]]) [https://phabricator.wikimedia.org/T381377] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:35|la tâche soumise|les {{formatnum:35}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:35||s}} la semaine dernière]]. Par exemple, il y avait un beugue de mise à jour du compteur de modifications de quelqu’un effectuant une annulation d’une autre modification : cela est maintenant corrigé. [https://phabricator.wikimedia.org/T382592] '''Actualités pour la contribution technique''' * [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Les utilisateurs et utilisatrices de l’API REST de Wikimedia (par exemple pour des robots ou des outils) peuvent être impactés par des mises à jour en cours. À partir de la semaine du 13 janvier, nous commencerons à rediriger [[phab:T374683|certains points terminaux de contenu de page]] depuis RESTbase vers les nouveaux points terminaux de l’API REST de MediaWiki pour tous les projets wiki. Ce changement était disponible sur testwiki, et ne devrait pas affecter les fonctionnalités existantes, mais les utilisateurs actifs des points terminaux concernés peuvent signaler directement à l’[[phab:project/view/6931/|équipe des interfaces de MediaWiki]] tout problème qui arriverait. * Les personnes maintenant des outils sur Toolforge peuvent désormais partager leurs retour sur Toolforge UI, un projet visant à fournir une plateforme web pour la création et la gestion d’outils Toolforge depuis une interface graphique, en plus des processus existant par ligne de commande. Ce projet vise à simplifier les tâches des mainteneurs et mainteneuses actifs, ainsi qu’à rendre l’inscription et les procédures de déploiement plus accessibles aux nouveaux et nouvelles créatrices d’outils. Le projet en est encore à ses balbutiements et l’équipe des services en infonuage recueille des retours de la communauté Toolforge pour aiderà concevoir la solution correspondant à leurs besoins. [[wikitech:Wikimedia Cloud Services team/EnhancementProposals/Toolforge UI|En savoir plus et donner son avis sur Toolforge UI]]. * [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] <span class="mw-translate-fuzzy">Pour le développement d’outil et bibliothèque qui utilisent le système OAuth : le point terminal d’identité utilisé pour [[mw:Special:MyLanguage/OAuth/For Developers#Identifying the user|OAuth 1]] et [[mw:Special:MyLanguage/OAuth/For Developers#Identifying the user 2|OAuth 2]] retournait un objet JSON avec un entier dans le sous-champ, ce qui était incorrect (le champ doit toujours être une chaine de caractère); Cela a été corrigé ; le correctif sera déployé sur les wikis Wikimedia la semaine du 13 janvier.</span> [https://phabricator.wikimedia.org/T382139] * De nombreux wikis utilisent actuellement le [[:mw:Parsoid/Parser Unification/Cite CSS|CSS de Cite]] pour insérer des marqueurs de note de bas de page personnalisés dans la sortie de Parsoid. À partir du 20 janvier, ces règles seront désactivées, mais les développeurs vous demandent de ''ne pas'' nettoyer votre <bdi lang="en" dir="ltr">[[MediaWiki:Common.css]]</bdi> avant le 20 février pour éviter des problèmes pendant la migration. Vos wikis rencontreront peut-être des petits changements dans les marqueurs de notes de bas page dans l’éditeur visuel ou en utilisant le mode de lecture expérimental Parsoid, mais s’il y a des changements, ils devraient garder le rendu cohérent avec la sortie de l’analyseur classique. [https://phabricator.wikimedia.org/T370027] '''Rencontres et évènements''' * Les prochaines réunions de la série des [[c:Special:MyLanguage/Commons:WMF support for Commons/Commons community calls|Discussions communautaires entre Wikimedia Foundation et la communauté de Wikimedia Commons]] aura lieu le [[m:Special:MyLanguage/Event:Commons community discussion - 15 January 2025 08:00 UTC|15 janvier à 8 h UTC]] et [[m:Special:MyLanguage/Event:Commons community discussion - 15 January 2025 16:00 UTC|à 16 h UTC]]. Le sujet de cette conférence porte sur la définition des priorités d’investissement en outils pour Commons. Les contributeurs et contributrices de tous les wikis sont les bienvenus pour participer, notamment celles et ceux qui maintiennent des outils pour Commons. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/03|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W03"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 14 janvier 2025 à 02:42 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28048614 --> == Launching! Join Us for Wiki Loves Ramadan 2025! == Dear All, We’re happy to announce the launch of [[m:Wiki Loves Ramadan 2025|Wiki Loves Ramadan 2025]], an annual international campaign dedicated to celebrating and preserving Islamic cultures and history through the power of Wikipedia. As an active contributor to the Local Wikipedia, you are specially invited to participate in the launch. This year’s campaign will be launched for you to join us write, edit, and improve articles that showcase the richness and diversity of Islamic traditions, history, and culture. * Topic: [[m:Event:Wiki Loves Ramadan 2025 Campaign Launch|Wiki Loves Ramadan 2025 Campaign Launch]] * When: Jan 19, 2025 * Time: 16:00 Universal Time UTC and runs throughout Ramadan (starting February 25, 2025). * Join Zoom Meeting: https://us02web.zoom.us/j/88420056597?pwd=NdrpqIhrwAVPeWB8FNb258n7qngqqo.1 * Zoom meeting hosted by [[m:Wikimedia Bangladesh|Wikimedia Bangladesh]] To get started, visit the [[m:Wiki Loves Ramadan 2025|campaign page]] for details, resources, and guidelines: Wiki Loves Ramadan 2025. Add [[m:Wiki Loves Ramadan 2025/Participant|your community here]], and organized Wiki Loves Ramadan 2025 in your local language. Whether you’re a first-time editor or an experienced Wikipedian, your contributions matter. Together, we can ensure Islamic cultures and traditions are well-represented and accessible to all. Feel free to invite your community and friends too. Kindly reach out if you have any questions or need support as you prepare to participate. Let’s make Wiki Loves Ramadan 2025 a success! For the [[m:Wiki Loves Ramadan 2025/Team|International Team]] 16 janvier 2025 à 13:08 (CET) <!-- Message envoyé par User:ZI Jony@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=27568454 --> == Actualités techniques n° 2025-04 == <section begin="technews-2025-W04"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/04|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * <span lang="en" dir="ltr" class="mw-content-ltr">Administrators can mass-delete multiple pages created by a user or IP address using [[mw:Special:MyLanguage/Extension:Nuke|Extension:Nuke]]. It previously only allowed deletion of pages created in the last 30 days. It can now delete pages from the last 90 days, provided it is targeting a specific user or IP address.</span> [https://phabricator.wikimedia.org/T380846] * <span lang="en" dir="ltr" class="mw-content-ltr">On [[phab:P72148|wikis that use]] the [[mw:Special:MyLanguage/Help:Patrolled edits|Patrolled edits]] feature, when the rollback feature is used to revert an unpatrolled page revision, that revision will now be marked as "manually patrolled" instead of "autopatrolled", which is more accurate. Some editors that use [[mw:Special:MyLanguage/Help:New filters for edit review/Filtering|filters]] on Recent Changes may need to update their filter settings.</span> [https://phabricator.wikimedia.org/T302140] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:31|la tâche soumise|les {{formatnum:31}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:31||s}} la semaine dernière]]. <span lang="en" dir="ltr" class="mw-content-ltr">For example, the Visual Editor's "Insert link" feature did not always suggest existing pages properly when an editor started typing, which has now been [[phab:T383497|fixed]].</span> '''Actualités pour la contribution technique''' * <span lang="en" dir="ltr" class="mw-content-ltr">The Structured Discussion extension (also known as Flow) is being progressively removed from the wikis. This extension is unmaintained and causes issues. It will be replaced by [[mw:Special:MyLanguage/Help:DiscussionTools|DiscussionTools]], which is used on any regular talk page. [[mw:Special:MyLanguage/Structured Discussions/Deprecation#Deprecation timeline|The last group of wikis]] ({{int:project-localized-name-cawikiquote/en}}{{int:comma-separator/en}}{{int:project-localized-name-fiwikimedia/en}}{{int:comma-separator/en}}{{int:project-localized-name-gomwiki/en}}{{int:comma-separator/en}}{{int:project-localized-name-kabwiki/en}}{{int:comma-separator/en}}{{int:project-localized-name-ptwikibooks/en}}{{int:comma-separator/en}}{{int:project-localized-name-sewikimedia/en}}) will soon be contacted. If you have questions about this process, please ping [[m:User:Trizek (WMF)|Trizek (WMF)]] at your wiki.</span> [https://phabricator.wikimedia.org/T380912] * <span lang="en" dir="ltr" class="mw-content-ltr">The latest quarterly [[mw:Technical_Community_Newsletter/2025/January|Technical Community Newsletter]] is now available. This edition includes: updates about services from the Data Platform Engineering teams, information about Codex from the Design System team, and more.</span> '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/04|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W04"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 21 janvier 2025 à 02:36 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28129769 --> == Universal Code of Conduct annual review: provide your comments on the UCoC and Enforcement Guidelines == <div lang="en" dir="ltr" class="mw-content-ltr"> My apologies for writing in English. {{Int:Please-translate}}. I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 3 February 2025. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]]. The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]]. Please share this information with other members in your community wherever else might be appropriate. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 24 janvier 2025 à 02:10 (CET) </div> <!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27746256 --> == Actualités techniques n° 2025-05 == <section begin="technews-2025-W05"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/05|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * <span lang="en" dir="ltr" class="mw-content-ltr">Patrollers and admins - what information or context about edits or users could help you to make patroller or admin decisions more quickly or easily? The Wikimedia Foundation wants to hear from you to help guide its upcoming annual plan. Please consider sharing your thoughts on this and [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Product & Technology OKRs|13 other questions]] to shape the technical direction for next year.</span> '''Actualités pour la contribution''' * <span lang="en" dir="ltr" class="mw-content-ltr">iOS Wikipedia App users worldwide can now access a [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/Personalized Wikipedia Year in Review/How your data is used|personalized Year in Review]] feature, which provides insights based on their reading and editing history on Wikipedia. This project is part of a broader effort to help welcome new readers as they discover and interact with encyclopedic content.</span> * [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] <span lang="en" dir="ltr" class="mw-content-ltr">Edit patrollers now have a new feature available that can highlight potentially problematic new pages. When a page is created with the same title as a page which was previously deleted, a tag ('Recreated') will now be added, which users can filter for in [[{{#special:RecentChanges}}]] and [[{{#special:NewPages}}]].</span> [https://phabricator.wikimedia.org/T56145] * <span lang="en" dir="ltr" class="mw-content-ltr">Later this week, there will be a new warning for editors if they attempt to create a redirect that links to another redirect (a [[mw:Special:MyLanguage/Help:Redirects#Double redirects|double redirect]]). The feature will recommend that they link directly to the second redirect's target page. Thanks to the user SomeRandomDeveloper for this improvement.</span> [https://phabricator.wikimedia.org/T326056] * [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] <span lang="en" dir="ltr" class="mw-content-ltr">Wikimedia wikis allow [[w:en:WebAuthn|WebAuthn]]-based second factor checks (such as hardware tokens) during login, but the feature is [[m:Community Wishlist Survey 2023/Miscellaneous/Fix security key (WebAuthn) support|fragile]] and has very few users. The MediaWiki Platform team is temporarily disabling adding new WebAuthn keys, to avoid interfering with the rollout of [[mw:MediaWiki Platform Team/SUL3|SUL3]] (single user login version 3). Existing keys are unaffected.</span> [https://phabricator.wikimedia.org/T378402] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:30|la tâche soumise|les {{formatnum:30}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:30||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * <span lang="en" dir="ltr" class="mw-content-ltr">For developers that use the [[wikitech:Data Platform/Data Lake/Edits/MediaWiki history dumps|MediaWiki History dumps]]: The Data Platform Engineering team has added a couple of new fields to these dumps, to support the [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|Temporary Accounts]] initiative. If you maintain software that reads those dumps, please review your code and the updated documentation, since the order of the fields in the row will change. There will also be one field rename: in the <bdi lang="zxx" dir="ltr"><code>mediawiki_user_history</code></bdi> dump, the <bdi lang="zxx" dir="ltr"><code>anonymous</code></bdi> field will be renamed to <bdi lang="zxx" dir="ltr"><code>is_anonymous</code></bdi>. The changes will take effect with the next release of the dumps in February.</span> [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/LKMFDS62TXGDN6L56F4ABXYLN7CSCQDI/] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/05|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W05"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 27 janvier 2025 à 23:14 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28149374 --> == Reminder: first part of the annual UCoC review closes soon == <div lang="en" dir="ltr" class="mw-content-ltr"> My apologies for writing in English. {{Int:Please-translate}}. This is a reminder that the first phase of the annual review period for the Universal Code of Conduct and Enforcement Guidelines will be closing soon. You can make suggestions for changes through [[d:Q614092|the end of day]], 3 February 2025. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]]. After review of the feedback, proposals for updated text will be published on Meta in March for another round of community review. Please share this information with other members in your community wherever else might be appropriate. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 3 février 2025 à 01:48 (CET) </div> <!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28198931 --> == <span lang="en" dir="ltr">Tech News: 2025-06</span> == <div lang="en" dir="ltr"> <section begin="technews-2025-W06"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/06|Translations]] are available. '''Updates for editors''' * Editors who use the "Special characters" editing-toolbar menu can now see the 32 special characters you have used most recently, across editing sessions on that wiki. This change should help make it easier to find the characters you use most often. The feature is in both the 2010 wikitext editor and VisualEditor. [https://phabricator.wikimedia.org/T110722] * Editors using the 2010 wikitext editor can now create sublists with correct indentation by selecting the line(s) you want to indent and then clicking the toolbar buttons.[https://phabricator.wikimedia.org/T380438] You can now also insert <code><nowiki><code></nowiki></code> tags using a new toolbar button.[https://phabricator.wikimedia.org/T383010] Thanks to user stjn for these improvements. * Help is needed to ensure the [[mw:Special:MyLanguage/Citoid/Enabling Citoid on your wiki|citation generator]] works properly on each wiki. ** (1) Administrators should update the local versions of the page <code dir=ltr>MediaWiki:Citoid-template-type-map.json</code> to include entries for <code dir=ltr>preprint</code>, <code dir=ltr>standard</code>, and <code dir=ltr>dataset</code>; Here are example diffs to replicate [https://en.wikipedia.org/w/index.php?title=MediaWiki%3ACitoid-template-type-map.json&diff=1189164774&oldid=1165783565 for 'preprint'] and [https://en.wikipedia.org/w/index.php?title=MediaWiki%3ACitoid-template-type-map.json&diff=1270832208&oldid=1270828390 for 'standard' and 'dataset']. ** (2.1) If the citoid map in the citation template used for these types of references is missing, [[mediawikiwiki:Citoid/Enabling Citoid on your wiki#Step 2.a: Create a 'citoid' maps value for each citation template|one will need to be added]]. (2.2) If the citoid map does exist, the TemplateData will need to be updated to include new field names. Here are example updates [https://en.wikipedia.org/w/index.php?title=Template%3ACitation%2Fdoc&diff=1270829051&oldid=1262470053 for 'preprint'] and [https://en.wikipedia.org/w/index.php?title=Template%3ACitation%2Fdoc&diff=1270831369&oldid=1270829480 for 'standard' and 'dataset']. The new fields that may need to be supported are <code dir=ltr>archiveID</code>, <code dir=ltr>identifier</code>, <code dir=ltr>repository</code>, <code dir=ltr>organization</code>, <code dir=ltr>repositoryLocation</code>, <code dir=ltr>committee</code>, and <code dir=ltr>versionNumber</code>. [https://phabricator.wikimedia.org/T383666] * One new wiki has been created: a {{int:project-localized-name-group-wikipedia/en}} in [[d:Q15637215|Central Kanuri]] ([[w:knc:|<code>w:knc:</code>]]) [https://phabricator.wikimedia.org/T385181] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:27}} community-submitted {{PLURAL:27|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the [[mediawikiwiki:Special:MyLanguage/Help:Extension:Wikisource/Wikimedia OCR|OCR (optical character recognition) tool]] used for Wikisource now supports a new language, Church Slavonic. [https://phabricator.wikimedia.org/T384782] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/06|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2025-W06"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 4 février 2025 à 01:08 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28203495 --> == <span lang="en" dir="ltr">Tech News: 2025-07</span> == <div lang="en" dir="ltr"> <section begin="technews-2025-W07"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/07|Translations]] are available. '''Weekly highlight''' * The Product and Technology Advisory Council (PTAC) has published [[m:Special:MyLanguage/Product and Technology Advisory Council/February 2025 draft PTAC recommendation for feedback|a draft of their recommendations]] for the Wikimedia Foundation's Product and Technology department. They have recommended focusing on [[m:Special:MyLanguage/Product and Technology Advisory Council/February 2025 draft PTAC recommendation for feedback/Mobile experiences|mobile experiences]], particularly contributions. They request community [[m:Talk:Product and Technology Advisory Council/February 2025 draft PTAC recommendation for feedback|feedback at the talk page]] by 21 February. '''Updates for editors''' * The "Special pages" portlet link will be moved from the "Toolbox" into the "Navigation" section of the main menu's sidebar by default. This change is because the Toolbox is intended for tools relating to the current page, not tools relating to the site, so the link will be more logically and consistently located. To modify this behavior and update CSS styling, administrators can follow the instructions at [[phab:T385346|T385346]]. [https://phabricator.wikimedia.org/T333211] * As part of this year's work around improving the ways readers discover content on the wikis, the Web team will be running an experiment with a small number of readers that displays some suggestions for related or interesting articles within the search bar. Please check out [[mw:Special:MyLanguage/Reading/Web/Content Discovery Experiments#Experiment 1: Display article recommendations in more prominent locations, search|the project page]] for more information. * [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Advanced item]] Template editors who use TemplateStyles can now customize output for users with specific accessibility needs by using accessibility related media queries (<code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion prefers-reduced-motion]</code>, <code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-transparency prefers-reduced-transparency]</code>, <code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast prefers-contrast]</code>, and <code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors forced-colors]</code>). Thanks to user Bawolff for these improvements. [https://phabricator.wikimedia.org/T384175] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:22}} community-submitted {{PLURAL:22|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the global blocks log will now be shown directly on the {{#special:CentralAuth}} page, similarly to global locks, to simplify the workflows for stewards. [https://phabricator.wikimedia.org/T377024] '''Updates for technical contributors''' * Wikidata [[d:Special:MyLanguage/Help:Default values for labels and aliases|now supports a special language as a "default for all languages"]] for labels and aliases. This is to avoid excessive duplication of the same information across many languages. If your Wikidata queries use labels, you may need to update them as some existing labels are getting removed. [https://phabricator.wikimedia.org/T312511] * The function <code dir="ltr">getDescription</code> was invoked on every Wiki page read and accounts for ~2.5% of a page's total load time. The calculated value will now be cached, reducing load on Wikimedia servers. [https://phabricator.wikimedia.org/T383660] * As part of the RESTBase deprecation [[mw:RESTBase/deprecation|effort]], the <code dir="ltr">/page/related</code> endpoint has been blocked as of February 6, 2025, and will be removed soon. This timeline was chosen to align with the deprecation schedules for older Android and iOS versions. The stable alternative is the "<code dir="ltr">morelike</code>" action API in MediaWiki, and [[gerrit:c/mediawiki/services/mobileapps/+/982154/13/pagelib/src/transform/FooterReadMore.js|a migration example]] is available. The MediaWiki Interfaces team [[phab:T376297|can be contacted]] for any questions. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/GFC2IJO7L4BWO3YTM7C5HF4MCCBE2RJ2/] '''In depth''' * The latest quarterly [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/January|Language and Internationalization newsletter]] is available. It includes: Updates about the "Contribute" menu; details on some of the newest language editions of Wikipedia; details on new languages supported by the MediaWiki interface; updates on the Community-defined lists feature; and more. * The latest [[mw:Extension:Chart/Project/Updates#January 2025: Better visibility into charts and tabular data usage|Chart Project newsletter]] is available. It includes updates on the progress towards bringing better visibility into global charts usage and support for categorizing pages in the Data namespace on Commons. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/07|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2025-W07"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 11 février 2025 à 01:11 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28231022 --> == Actualités techniques n° 2025-08 == <section begin="technews-2025-W08"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/08|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * Les communautés utilisant les outils de croissance peuvent désormais mettre en avant un évènement pour les nouveaux contributeurs sur la <code>{{#special:Homepage}}</code>. Cette fonctionnalité aidera les nouveaux venus à être informés des activités d'édition auxquels ils peuvent participer. Les administrateurs peuvent ajouter un nouvel évènement à mettre en avant sur <code>{{#special:CommunityConfiguration}}</code>. Pour en apprendre davantage sur cette nouvelle fonctionnalité, vous pouvez lire [[diffblog:2025/02/12/community-updates-module-connecting-newcomers-to-your-initiatives/|l'annonce sur Diff]], la [[mw:Special:MyLanguage/Help:Growth/Tools/Community updates module|documentation]] ou [[mw:Talk:Growth|contacter l'équipe Croissance]]. '''Actualités pour la contribution''' [[File:Page Frame Features on desktop.png|thumb|Mise en évidence des améliorations aux pages de discussion]] * À partir de la semaine prochaine, les pages de discussions des wikis suivants recevront [[diffblog:2024/05/02/making-talk-pages-better-for-everyone/|une nouvelle présentation]] : {{int:project-localized-name-eswiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-frwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-itwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-jawiki/fr}}. Ce changement a été largement testé en tant que fonctionnalité beta et il s'agit de la dernière étape des [[mw:Special:MyLanguage/Talk pages project/Feature summary|améliorations aux pages de discussion]]. [https://phabricator.wikimedia.org/T379102] * Vous pouvez désormais visualiser une page de redirection directement depuis ses pages d'action, comme la page d'historique. Auparavant, vous étiez automatiquement redirigé vers la page cible et deviez manuellement revenir à la page de redirection. Ce changement devrait aider les rédacteurs travaillant avec les redirections. Merci à stjn pour cette amélioration. [https://phabricator.wikimedia.org/T5324] * Quand une référence est utilisée de nombreuses fois, les wikis affichent actuellement des nombres comme 1.23 ou des marqueurs avec des lettres comme a, b, c dans la liste de références. Avant, quand le nombre de références était trop important et que toutes les lettres avaient été utilisées, un [[MediaWiki:Cite error references no backlink label|message d'erreur]] était affiché. Dans le cadre des travaux pour la [[phab:T383036|modernisation de la personnalisation des références]], ces erreurs ne seront plus affichées et des marqueurs numériques comme 1.27 seront utilisés par défaut après épuisement des marqueurs alphabétiques. * Les entrées de journal pour chaque changement aux groupes utilisateur d'un éditeur ont été clarifiés pour indiquer exactement ce qui a été modifié. Elles contenaient auparavant les deux listes des groupes avant et après le changement. Les traducteurs sont invités à [[phab:T369466|aider à traduire les messages système associés]]. Merci à Msz2001 pour ces améliorations. * Un nouveau filtre a été ajouté à [[{{#special:Nuke}}]] — outil permettant aux administrateurs de supprimer en masse des pages — pour permettre aux utilisateurs de filtrer les pages en fonction de leur taille en octets. Cela permet par exemple de supprimer uniquement des pages inférieures à une certaine taille. [https://phabricator.wikimedia.org/T378488] * Les non-administrateurs peuvent maintenant voir quelles pages peuvent être supprimées à l'aide de [[{{#special:Nuke}}]]. Merci à MolecularPilot pour cette amélioration et les précédentes. [https://phabricator.wikimedia.org/T376378] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:25|la tâche soumise|les {{formatnum:25}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:25||s}} la semaine dernière]]. Par exemple, un bug a été corrigé dans la configuration du format de fichier vidéo AV1, ce qui permet à ces fichiers d'être lus à nouveau. [https://phabricator.wikimedia.org/T382193] '''Actualités pour la contribution technique''' * Parsoid Read Views sera déployé sur la plupart des Wiktionnaires dans les prochaines semaines, à la suite de la transition avec succès des Wikivoyage à Parsoid l'année dernière. Pour davantage d'informations, voir la page du projet [[mw:Special:MyLanguage/Parsoid/Parser Unification|Parsoid/Parser Unification]]. [https://phabricator.wikimedia.org/T385923][https://phabricator.wikimedia.org/T371640] * Les développeurs d'outils sur wiki sont informés que <code dir=ltr>mw.Uri</code> est obsolète. Les outils nécessitant <code dir=ltr>mw.Uri</code> doivent déclarer explicitement <code dir=ltr>mediawiki.Uri</code> comme une dépendance de ResourceLoader, et devraient migrer vers l'API <code dir=ltr>URL</code> native du navigateur prochainement. [https://phabricator.wikimedia.org/T384515] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/08|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W08"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 17 février 2025 à 22:16 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28275610 --> == <span lang="en" dir="ltr"> Upcoming Language Community Meeting (Feb 28th, 14:00 UTC) and Newsletter</span> == <div lang="en" dir="ltr"> <section begin="message"/> Hello everyone! [[File:WP20Symbols WIKI INCUBATOR.svg|right|frameless|150x150px|alt=An image symbolising multiple languages]] We’re excited to announce that the next '''Language Community Meeting''' is happening soon, '''February 28th at 14:00 UTC'''! If you’d like to join, simply sign up on the '''[[mw:Wikimedia_Language_and_Product_Localization/Community_meetings#28_February_2025|wiki page]]'''. This is a participant-driven meeting where we share updates on language-related projects, discuss technical challenges in language wikis, and collaborate on solutions. In our last meeting, we covered topics like developing language keyboards, creating the Moore Wikipedia, and updates from the language support track at Wiki Indaba. '''Got a topic to share?''' Whether it’s a technical update from your project, a challenge you need help with, or a request for interpretation support, we’d love to hear from you! Feel free to '''reply to this message''' or add agenda items to the document '''[[etherpad:p/language-community-meeting-feb-2025|here]]'''. Also, we wanted to highlight that the sixth edition of the Language & Internationalization newsletter (January 2025) is available here: [[:mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/January|Wikimedia Language and Product Localization/Newsletter/2025/January]]. This newsletter provides updates from the October–December 2024 quarter on new feature development, improvements in various language-related technical projects and support efforts, details about community meetings, and ideas for contributing to projects. To stay updated, you can subscribe to the newsletter on its wiki page: [[:mw:Wikimedia Language and Product Localization/Newsletter|Wikimedia Language and Product Localization/Newsletter]]. We look forward to your ideas and participation at the language community meeting, see you there! <section end="message"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 22 février 2025 à 09:28 (CET) <!-- Message envoyé par User:SSethi (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28217779 --> == Actualités techniques n° 2025-09 == <section begin="technews-2025-W09"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/09|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * Les administrateurs peuvent désormais personnaliser la manière dont les catégories de [[m:Special:MyLanguage/User language|Babel]] sont créées en utilisant [[{{#special:CommunityConfiguration/Babel}}]]. Ils peuvent renommer les catégories de langues, choisir si elles doivent être créées automatiquement et ajuster d'autres paramètres. [https://phabricator.wikimedia.org/T374348] * Le portail <bdi lang="en" dir="ltr">[https://www.wikimedia.org/ wikimedia.org]</bdi> a été mis à jour pour moderniser et améliorer l'accessibilité de nos pages de portail. Il dispose désormais d'un meilleur support pour les mises en page mobiles, de meilleures formulations et liens et d'un support linguistique amélioré. De plus, tous les portails du projet Wikimedia, comme <bdi lang="en" dir="ltr">[https://wikibooks.org wikibooks.org]</bdi>, prennent maintenant en charge le mode sombre lorsqu'un lecteur utilise ce paramètre système. [https://phabricator.wikimedia.org/T373204][https://phabricator.wikimedia.org/T368221][https://meta.wikimedia.org/wiki/Project_portals] * Un nouveau wiki a été créé : un {{int:project-localized-name-group-wiktionary/fr}} en [[d:Q33965|Santali]] ([[wikt:sat:|<code>wikt:sat:</code>]]) [https://phabricator.wikimedia.org/T386619] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:30|la tâche soumise|les {{formatnum:30}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:30||s}} la semaine dernière]]. Par exemple, un bogue qui empêchait de cliquer sur les résultats de recherche de l'interface web sur certaines configurations mobiles avec Firefox a été corrigé. [https://phabricator.wikimedia.org/T381289] '''Rencontres et évènements''' * La prochaine rencontre de la communauté linguistique aura lieu le 28 février à [https://zonestamp.toolforge.org/1740751200 14:00 UTC]. La rencontre de cette semaine couvrira : les points importants et mises-à-jour techniques pour les langues samis, les contributions à translatewiki.net de la part de la communauté Bahasa Lampung en Indonésie et une FAQ technique. Si vous souhaitez participer, inscrivez-vous sur la [[mw:Wikimedia Language and Product Localization/Community meetings#28 February 2025|page wiki]]. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/09|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W09"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 25 février 2025 à 01:41 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28296129 --> == Actualités techniques n° 2025-10 == <section begin="technews-2025-W10"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/10|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * Les utilisateurs et utilisatrices connectés utilisant l’affichage mobile peuvent désormais modifier une page complète. Le lien « {{int:Minerva-page-actions-editfull}} » est accessible dans le menu « {{int:minerva-page-actions-overflow}} » de la barre d’outils. Ce lien était auparavant disponible uniquement lorsque le [[mw:Special:MyLanguage/Reading/Web/Advanced mobile contributions|mode avancé]] était activé. [https://phabricator.wikimedia.org/T387180] * Les admins d’interface peuvent désormais retirer les utilisations de la classe CSS « <code dir="ltr">mw-ref</code> » de leur <bdi lang="en" dir="ltr">[[MediaWiki:Common.css]]</bdi> local. Cette classe de l’extension <span lang="en">Cite</span> est obsolète. La liste des wikis l’utilisant peut être trouvée par [https://global-search.toolforge.org/?q=mw-ref%5B%5E-a-z%5D&regex=1&namespaces=8&title=.*css cette recherche globale] et dans [https://ace.wikipedia.org/w/index.php?title=MediaWiki:Common.css&oldid=145662#L-139--L-144 cet exemple]. D’autres informations sur les manières d’aider sont données sur la [[mw:Parsoid/Parser Unification/Cite CSS|page du projet de migration du CSS]]. Les appels de note (<code dir="ltr">[1]</code>) sont désormais rendus par [[mw:Special:MyLanguage/Parsoid|Parsoid]] ; le CSS obsolète n’est plus nécessaire. Le CSS pour les rétroliens « <code dir="ltr">mw:referencedBy</code> » doit rester en place pour le moment. Ce nettoyage ne devrait pas avoir d’effet visible pour les lecteurs et lectrices. Merci d’aider à retirer ce code avant le 30 mars, après quoi l’équipe de développement le fera pour vous. * Lorsque les contributeurs ajoutent un fichier (par exemple <code><nowiki>[[File:MediaWiki.png]]</nowiki></code>) sur une page protégée par une protection en cascade, le logiciel ne restreindra plus les modifications à la page de description du fichier, mais uniquement aux nouveaux téléchargements de fichiers. [https://phabricator.wikimedia.org/T24521] A l’inverse, la transclusion d’une page de description de fichier (par exemple <code><nowiki>{{:File:MediaWiki.png}}</nowiki></code>) dans une page protégée en cascade provoquera désormais une restriction des modifications à la page.[https://phabricator.wikimedia.org/T62109] * Remettre un fichier dans une version antérieure nécessitera désormais les mêmes autorisations que le téléchargement d'une nouvelle version du fichier. Le logiciel vérifie désormais la possession des droits « <i lang="en">reupload</i> » ou « <i lang="en">reuplod-own</i> » [https://phabricator.wikimedia.org/T304474], et respecte la protection en cascade. [https://phabricator.wikimedia.org/T140010] * Lorsque les admins listent des pages à supprimer avec l’outil Nuke, ils peuvent désormais également répertorier les pages de discussion associées et les redirections à supprimer, en plus des pages créées par la cible, plutôt que de devoir supprimer manuellement ces pages. [https://phabricator.wikimedia.org/T95797] * La mise à jour [[m:Special:MyLanguage/Tech/News/2025/03|mentionnée précédemment]] de la connexion utilisateur unifiée, qui prendra en compte les restrictions du navigateur sur les cookies inter-domaines en déplaçant la connexion et la création de compte vers un domaine central, sera déployée pour tous les utilisateurs et utilisatrices en mars et avril. L'équipe prévoit de l'activer pour toutes les nouvelles créations de compte sur les wikis du [[wikitech:Deployments/Train#Tuesday|groupe 0]] cette semaine. Consultez la [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3#Deployment|page du projet SUL3]] pour plus de détails et un calendrier mis à jour. * Depuis la semaine dernière, un bogue cause l'affichage de certaines icônes d'interface sous forme de carrés noirs jusqu'à ce que la page soit entièrement chargée. Cela sera corrigé cette semaine. [https://phabricator.wikimedia.org/T387351] * Un nouveau wiki a été créé : une {{int:project-localized-name-group-wikipedia/fr}} en [[d:Q2044560|sylheti]] ([[w:syl:|<code>w:syl:</code>]]) [https://phabricator.wikimedia.org/T386441] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]]. Par exemple, un bogue avec le chargement d'images dans de très anciennes versions du navigateur Firefox sur mobile a été corrigé. [https://phabricator.wikimedia.org/T386400] '''Actualités pour la contribution technique''' * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.19|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/10|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W10"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 4 mars 2025 à 03:30 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28334563 --> == Universal Code of Conduct annual review: proposed changes are available for comment == <div lang="en" dir="ltr" class="mw-content-ltr"> My apologies for writing in English. {{Int:Please-translate}}. I am writing to you to let you know that [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|proposed changes]] to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct (UCoC) Enforcement Guidelines]] and [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|Universal Code of Conduct Coordinating Committee (U4C) Charter]] are open for review. '''[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|You can provide feedback on suggested changes]]''' through the [[d:Q614092|end of day]] on Tuesday, 18 March 2025. This is the second step in the annual review process, the final step will be community voting on the proposed changes. [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find relevant links about the process on the UCoC annual review page on Meta]]. The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]]. Please share this information with other members in your community wherever else might be appropriate. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] 7 mars 2025 à 19:50 (CET) </div> <!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28307738 --> == Actualités techniques n° 2025-11 == <section begin="technews-2025-W11"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/11|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * Les contributeurs qui utilisent des gestionnaires de mots de passe sur plusieurs wikis peuvent remarquer des changements à l’avenir. La manière dont nos wikis fournissent des informations aux gestionnaires de mots de passe sur la réutilisation des mots de passe entre les domaines a été récemment mise à jour, de sorte que certains gestionnaires de mots de passe peuvent maintenant vous proposer des identifiants de connexion que vous avez sauvegardés pour un autre site Wikimedia. Certains gestionnaires de mots de passe l'ont déjà fait, et le font maintenant pour d'autres domaines de Wikimedia. Cela fait partie du projet [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3|SUL3]] qui vise à améliorer le fonctionnement de notre connexion unifiée et à la rendre compatible avec les changements en cours dans les navigateurs web que nous utilisons. [https://phabricator.wikimedia.org/T385520][https://phabricator.wikimedia.org/T384844] * L'équipe des applications Wikipédia invite les utilisateurs intéressés à contribuer à l'amélioration de l'utilisation de Wikipédia hors ligne ou en internet limité. Après les discussions de [[m:Afrika Baraza|Afrika Baraza]] et la dernière [[m:Special:MyLanguage/ESEAP Hub/Meetings|conférence ESEAP]], des défis clés comme la recherche, la modification et l'accès hors ligne sont explorés, avec des groupes de discussion à venir pour approfondir ces sujets. Toutes les langues sont les bienvenues et des interprètes seront disponibles. Vous souhaitez partager vos idées ? [[mw:Special:MyLanguage/Wikimedia Apps/Improving Wikipedia Mobile Apps for Offline & Limited Internet Use|Participez à la discussion]] ou envoyez un courriel à <bdi lang="en" dir="ltr">aramadan@wikimedia.org</bdi> ! * Tous les wikis seront en lecture seule pendant quelques minutes le 19 mars, à [https://zonestamp.toolforge.org/1742392800 14 h UTC]. De plus amples informations seront publiées dans les ''Actualités techniques'' et seront également publiées sur chaque wiki dans les semaines à venir. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.20|MediaWiki]] '''En détails''' * La dernière [[mw:Special:MyLanguage/Growth/Newsletters/33|infolettre trimestrielle du département Croissance]] est disponible. Elle présente : le lancement du module Actualités de la communauté, les dernières modifications de la configuration communautaire et le test à venir des suggestions d'articles pour les personnes contribuant pour la première fois. * Une ancienne API utilisée dans l'application Android Wikipedia sera supprimée à la fin du mois de mars. Il n'y a pas d'utilisation logicielle en cours, mais les utilisateurs de l'application dont la version date de plus de 6 mois au moment de la suppression (2025-03-31), n'auront plus accès à la fonction Suggested Edits, jusqu'à ce qu'ils mettent à jour leur application. Vous pouvez [[diffblog:2025/02/24/sunset-of-wikimedia-recommendation-api/|lire plus de détails sur ce changement]]. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/11|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W11"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 11 mars 2025 à 00:09 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28372257 --> == Votre wiki sera bientôt en lecture seule == <section begin="server-switch"/><div class="plainlinks"> [[:m:Special:MyLanguage/Tech/Server switch|Lire ce message dans une autre langue]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}] La [[foundation:|Fondation Wikimedia]] va basculer le trafic entre ses centres de données. Cela permettra de s’assurer que Wikipédia et les autres wikis de Wikimedia peuvent rester en ligne même après une catastrophe. Le trafic sera basculé le '''{{#time:j xg|2025-03-19|fr}}'''. La bascule débutera à '''[https://zonestamp.toolforge.org/{{#time:U|2025-03-19T14:00|en}} {{#time:H:i e|2025-03-19T14:00}}]'''. Malheureusement, en raison de certaines limites de [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]], toutes les modifications de pages devront être arrêtées durant le passage d’un centre de données à l’autre. Nous nous excusons pour ce dérangement, que nous nous efforçons de réduire pour le futur. Une bannière sera affichée sur tous les wikis 30 minutes avant le début de l’opération. Cette bannière restera visible jusqu’à la fin de l’opération. '''Pendant une courte période, vous pourrez lire les wikis mais pas les modifier.''' *Vous ne pourrez pas effectuer de modification pendant une durée pouvant aller jusqu’à une heure, le {{#time:l j xg Y|2025-03-19|fr}}. *Si vous essayez de faire une modification ou de sauvegarder pendant cette période, vous verrez un message d’erreur. Nous espérons qu’aucune modification ne sera perdue durant ce temps, mais nous ne pouvons le garantir. Si vous voyez un message d’erreur, merci de patienter jusqu’au retour à la normale. Vous pourrez alors enregistrer votre modification. Nous vous conseillons cependant de faire une copie de votre modification avant, au cas où. ''Autres conséquences :'' *Les tâches de fond seront ralenties et certaines pourraient être stoppées. Les liens rouges ne seront pas mis à jour aussi vite que d’habitude. Si vous créez un article qui est déjà lié depuis une autre page, le lien rouge pourrait rester rouge plus longtemps que d’habitude. Certains scripts ayant un long temps d’exécution devront être stoppés. * Le déploiement de code devrait se dérouler comme chaque semaine. Cependant, certains codes particuliers pourraient être gelés si l’opération le nécessitait. * [[mw:Special:MyLanguage/GitLab|GitLab]] sera indisponible durant environ 90 minutes. Ce projet pourra être reporté si nécessaire. Vous pouvez [[wikitech:Switch_Datacenter|consulter le calendrier sur wikitech.wikimedia.org]]. Tout changement sera annoncé dans le calendrier. '''Merci de partager ces informations avec votre communauté.'''</div><section end="server-switch"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 15 mars 2025 à 00:14 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=28307742 --> == <span lang="en" dir="ltr">Tech News: 2025-12</span> == <div lang="en" dir="ltr"> <section begin="technews-2025-W12"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/12|Translations]] are available. '''Weekly highlight''' * Twice a year, around the equinoxes, the Wikimedia Foundation's Site Reliability Engineering (SRE) team performs [[m:Special:MyLanguage/Tech/Server switch|a datacenter server switchover]], redirecting all traffic from one primary server to its backup. This provides reliability in case of a crisis, as we can always fall back on the other datacenter. [http://listen.hatnote.com/ Thanks to the Listen to Wikipedia] tool, you can hear the switchover take place: Before it begins, you'll hear the steady stream of edits; Then, as the system enters a brief read-only phase, the sound stops for a couple of minutes, before resuming after the switchover. You can [[diffblog:2025/03/12/hear-that-the-wikis-go-silent-twice-a-year/|read more about the background and details of this process on the Diff blog]]. If you want to keep an ear out for the next server switchover, listen to the wikis on [https://zonestamp.toolforge.org/1742392800 March 19 at 14:00 UTC]. '''Updates for editors''' * The [https://test.wikipedia.org/w/index.php?title=Special:ContentTranslation&filter-type=automatic&filter-id=previous-edits&active-list=suggestions&from=en&to=es improved Content Translation tool dashboard] is now available in [[phab:T387820|10 Wikipedias]] and will be available for all Wikipedias [[phab:T387821|soon]]. With [[mw:Special:MyLanguage/Content translation#Improved translation experience|the unified dashboard]], desktop users can now: Translate new sections of an article; Discover and access topic-based [https://ig.m.wikipedia.org/w/index.php?title=Special:ContentTranslation&active-list=suggestions&from=en&to=ig&filter-type=automatic&filter-id=previous-edits article suggestion filters] (initially available only for mobile device users); Discover and access the [[mw:Special:MyLanguage/Translation suggestions: Topic-based & Community-defined lists|Community-defined lists]] filter, also known as "Collections", from wiki-projects and campaigns. * On Wikimedia Commons, a [[c:Commons:WMF support for Commons/Upload Wizard Improvements#Improve category selection|new system to select the appropriate file categories]] has been introduced: if a category has one or more subcategories, users will be able to click on an arrow that will open the subcategories directly within the form, and choose the correct one. The parent category name will always be shown on top, and it will always be possible to come back to it. This should decrease the amount of work for volunteers in fixing/creating new categories. The change is also available on mobile. These changes are part of planned improvements to the UploadWizard. * The Community Tech team is seeking wikis to join a pilot for the [[m:Special:MyLanguage/Community Wishlist Survey 2023/Multiblocks|Multiblocks]] feature and a refreshed Special:Block page in late March. Multiblocks enables administrators to impose multiple different types of blocks on the same user at the same time. If you are an admin or steward and would like us to discuss joining the pilot with your community, please leave a message on the [[m:Talk:Community Wishlist Survey 2023/Multiblocks|project talk page]]. * Starting March 25, the Editing team will test a new feature for Edit Check at [[phab:T384372|12 Wikipedias]]: [[mw:Special:MyLanguage/Help:Edit check#Multi-check|Multi-Check]]. Half of the newcomers on these wikis will see all [[mw:Special:MyLanguage/Help:Edit check#ref|Reference Checks]] during their edit session, while the other half will continue seeing only one. The goal of this test is to see if users are confused or discouraged when shown multiple Reference Checks (when relevant) within a single editing session. At these wikis, the tags used on edits that show References Check will be simplified, as multiple tags could be shown within a single edit. Changes to the tags are documented [[phab:T373949|on Phabricator]]. [https://phabricator.wikimedia.org/T379131] * The [[m:Special:MyLanguage/Global reminder bot|Global reminder bot]], which is a service for notifying users that their temporary user-rights are about to expire, now supports using the localized name of the user-rights group in the message heading. Translators can see the [[m:Global reminder bot/Translation|listing of existing translations and documentation]] to check if their language needs updating or creation. * The [[Special:GlobalPreferences|GlobalPreferences]] gender setting, which is used for how the software should refer to you in interface messages, now works as expected by overriding the local defaults. [https://phabricator.wikimedia.org/T386584] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:26}} community-submitted {{PLURAL:26|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the Wikipedia App for Android had a bug fixed for when a user is browsing and searching in multiple languages. [https://phabricator.wikimedia.org/T379777] '''Updates for technical contributors''' * Later this week, the way that Codex styles are loaded will be changing. There is a small risk that this may result in unstyled interface message boxes on certain pages. User generated content (e.g. templates) is not impacted. Gadgets may be impacted. If you see any issues [[phab:T388847|please report them]]. See the linked task for details, screenshots, and documentation on how to fix any affected gadgets. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.44/wmf.21|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/12|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2025-W12"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 18 mars 2025 à 00:48 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28412594 --> == Actualités techniques n° 2025-13 == <section begin="technews-2025-W13"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/13|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * La Fondation Wikimédia souhaite recueillir vos commentaires sur les [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Product & Technology OKRs|ébauches des objectifs et des résultats-clés qui façonneront les priorités de la Fondation en matière de produits et de technologies]] pour la prochaine année fiscale (commençant en juillet). Les objectifs sont des domaines généraux et les résultats-clés permettent de mesurer leur réalisation. N'hésitez pas à partager vos commentaires sur la page de discussion, dans n'importe quelle langue, idéalement avant fin avril. '''Actualités pour la contribution''' * L'extension [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|CampaignEvents]] sera déployée sur plusieurs wikis (voir le [[m:Special:MyLanguage/CampaignEvents/Deployment status#Global Deployment Plan|plan de déploiement]] pour plus de détails) en avril 2025, et l'équipe a commencé le processus d'engagement des communautés sur les wikis identifiés. L'extension fournit des outils pour organiser, gérer et promouvoir des activités collaboratives (comme des événements, des edit-a-thons et des WikiProjects) sur les wikis. L'extension comporte trois outils : [[m:Special:MyLanguage/Event Center/Registration|Inscription à l'événement]], [[m:Special:MyLanguage/CampaignEvents/Collaboration list|Liste de collaboration]] et [[m:Special:MyLanguage/Campaigns/Foundation Product Team/Invitation list|Liste d'invitation]]. Elle est actuellement présente sur 13 Wikipédias, dont la Wikipédia en anglais, la Wikipédia en français et la Wikipédia en espagnol, ainsi que sur Wikidata. Les questions ou demandes peuvent être adressées sur la [[mw:Help talk:Extension:CampaignEvents|page de discussion de l'extension]] ou sur Phabricator (avec l'étiquette <bdi lang="en" dir="ltr" style="white-space: nowrap;">#campaigns-product-team</bdi>). * À partir de la semaine du 31 mars, les wikis pourront définir quels groupes d'utilisateurs peuvent voir les inscriptions privées dans [[m:Special:MyLanguage/Event Center/Registration|Inscription à l'événement]], dans le cadre de l'extension [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|CampaignEvents]]. Par défaut, les organisateurs d'événements et les administrateurs du wiki local pourront voir les inscriptions privées. Il s'agit d'un changement par rapport au réglage actuel qui permet seulement aux organisateurs de l'événement de voir les inscriptions privées. Les wikis peuvent modifier la configuration par défaut en demandant un changement de configuration dans Phabricator (et en ajoutant l'étiquette <bdi lang="en" dir="ltr" style="white-space: nowrap;">#campaigns-product-team</bdi>). Les participants aux événements passés peuvent annuler leur inscription à tout moment. * Les administrateurs des wikis qui disposent d'une barre latérale <bdi lang="en" dir="ltr">[[MediaWiki:Sidebar]]</bdi> personnalisée doivent vérifier si elle contient une entrée pour la liste {{int:specialpages}}. Si ce n'est pas le cas, ils doivent l'ajouter en utilisant <code dir=ltr style="white-space: nowrap;">* specialpages-url|specialpages</code>. Les wikis disposant d'une barre latérale par défaut verront le lien déplacé de la boîte à outils de la page vers le menu de la barre latérale en avril. [https://phabricator.wikimedia.org/T388927] * L'habillage Minerva (web mobile) combine les notifications d'avis et d'alertes dans l'icône de cloche ([[File:OOjs UI icon bell.svg|16px|link=|class=skin-invert]]). Il existait depuis longtemps un bogue qui faisait qu'une indication de nouvelles notifications n'était affichée que si vous aviez des alertes que vous n'avez pas vues. Ce problème est désormais résolu. À l'avenir, les utilisateurs de Minerva remarqueront un compteur au-dessus de l'icône de la cloche lorsque vous avez un ou plusieurs notifications et/ou alertes non vues. [https://phabricator.wikimedia.org/T344029] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * VisualEditor a introduit un [[mw:VisualEditor/Hooks|nouveau hook côté client]] pour les développeurs à utiliser lors de l'intégration avec le cycle de vie de la cible VisualEditor. Ce hook devrait remplacer les hooks existants liés au cycle de vie et être plus cohérent entre les différentes plateformes. De plus, le nouveau hook s'appliquera aux utilisations de VisualEditor en dehors de l'édition complète d'articles, permettant aux gadgets d'interagir avec l'éditeur dans DiscussionTools également. L'équipe d'édition a l'intention de déprécier et éventuellement de supprimer les hooks de l'ancien cycle de vie, donc tous les cas d'utilisation que ce nouveau hook ne couvre pas seraient intéressants pour l'équipe et peuvent être [[phab:T355555|partagés dans la tâche]]. * Les développeurs qui utilisent la bibliothèque JavaScript <code dir=ltr>mw.Api</code> peuvent désormais identifier l'outil qui l'utilise avec le paramètre <code dir=ltr>userAgent</code> : <code dir=ltr>var api = new mw.Api( { userAgent: 'GadgetNameHere/1.0.1' } );</code>. Si vous gérez un gadget ou un script utilisateur, veuillez définir un agent utilisateur, car cela facilite la maintenance de la bibliothèque et du serveur et permet de différencier le trafic légitime du trafic illégitime. [https://phabricator.wikimedia.org/T373874][https://foundation.wikimedia.org/wiki/Policy:Wikimedia_Foundation_User-Agent_Policy] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.22|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/13|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W13"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 24 mars 2025 à 23:42 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28443127 --> == Actualités techniques n° 2025-14 == <section begin="technews-2025-W14"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/14|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * L'équipe Contribution travaille sur une nouvelle [[mw:Special:MyLanguage/Edit Check|vérification des modifications]] : le [[mw:Special:MyLanguage/Edit check#26 March 2025|contrôle des éloges]]. L'objectif de cette vérification est d’identifier les termes non neutres saisis lors de la modification d’une page Wikipédia, afin d’informer l’auteur ou autrice que son texte devrait peut-être être modifié avant publication. Ce projet n’en est qu’à ses débuts ; l’équipe a besoin de l’avis des communautés. Dans [[phab:T389445|cette tâche Phabricator]], l’équipe rassemble les recommendations internes des wikis, les modèles utilisés pour étiqueter les articles non neutres et les termes (jargon et mots-clés) utilisés dans les résumés de modification pour les langues étudiées actuellement. Vous pouvez participer en modifiant le tableau sur Phabricator, en commentant la tâche ou en envoyant directement un message à [[m:user:Trizek (WMF)|Trizek (WMF)]]. * La [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3|connexion utilisateur unique]] (SUL) a été mise à jour sur tous les wikis afin de déplacer la connexion et la création de compte vers un domaine central. Cela rend la connexion des contributeurs compatible avec les restrictions des navigateurs sur les cookies inter-domaines, qui ont empêché les utilisateurs de certains navigateurs de rester connectés. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:35|la tâche soumise|les {{formatnum:35}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:35||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * À partir du 31 mars, l'équipe MediaWiki Interfaces va lancer une version limitée des spécifications OpenAPI générées et une expérience de bac à sable basée sur SwaggerUI pour [[mw:Special:MyLanguage/API:REST API|MediaWiki REST APIs]]. L'équipe invite les développeurs d'un groupe limité de communautés Wikipédia non anglophones (arabe, allemand, français, hébreu, interlingua, néerlandais, chinois) à consulter la documentation et à expérimenter le bac à sable dans leur langue de choix. En plus de ces projets Wikipédia spécifiques, le bac à sable et la spécification OpenAPI seront disponibles sur la [[testwiki:Special:RestSandbox|page spéciale test wiki REST Sandbox]] pour les développeurs dont l'anglais est la langue préférée. Pendant la période de prévisualisation, l'équipe MediaWiki Interfaces invite également les développeurs à [[mw:MediaWiki Interfaces Team/Feature Feedback/REST Sandbox|partager leur retour d'expérience]]. L'aperçu durera environ 2 semaines, après quoi le bac à sable et les spécifications OpenAPI seront mis à la disposition de tous les projets wiki. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.23|MediaWiki]] '''En détails''' * Parfois, un petit changement de code d'une ligne peut avoir une grande importance : dans ce cas, cela signifie que pour la première fois depuis des années, nous sommes en mesure de faire fonctionner toute la pile qui sert <bdi lang="en" dir="ltr">[http://maps.wikimedia.org/ maps.wikimedia.org]</bdi> - un hôte dédié à servir nos wikis et leurs besoins en cartes multilingues - à partir d'un seul centre de données, ce que nous testons à chaque fois que nous effectuons un [[m:Special:MyLanguage/Tech/Server switch|basculement de centre de données]]. C'est important car cela signifie que si l'un de nos centres de données est affecté par une catastrophe, nous serons toujours en mesure de servir le site. Ce changement est le résultat d'un [[phab:T216826|travail intensif]] de deux développeurs sur le portage du dernier composant de la pile de cartes sur [[w:fr:Kubernetes|kubernetes]], où nous pouvons allouer des ressources plus efficacement qu'auparavant, ce qui nous permet de supporter plus de trafic dans un seul centre de données. Ce travail a nécessité beaucoup d'étapes compliquées car ce logiciel et les bibliothèques logicielles qu'il utilise nécessitaient de nombreuses mises à jour attendues depuis longtemps. Ce type de travail rend l'infrastructure de Wikimedia plus durable. '''Rencontres et évènements''' * La [[mw:Special:MyLanguage/MediaWiki Users and Developers Workshop Spring 2025|Conférence des utilisateurs et développeurs de MediaWiki printemps 2025]] se déroulera à Sandusky, aux États-Unis, et en ligne, du 14 au 16 mai 2025. La conférence proposera des discussions autour de l'utilisation du logiciel MediaWiki par et au sein d'entreprises de différents secteurs, et inspirera et embarquera de nouveaux utilisateurs. L'inscription et l'enregistrement des présentations sont maintenant disponibles sur le site web de la conférence. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/14|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W14"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 1 avril 2025 à 02:05 (CEST) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28473566 --> == Final proposed modifications to the Universal Code of Conduct Enforcement Guidelines and U4C Charter now posted == <div lang="en" dir="ltr" class="mw-content-ltr"> The proposed modifications to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct Enforcement Guidelines]] and the U4C Charter [[m:Universal_Code_of_Conduct/Annual_review/2025/Proposed_Changes|are now on Meta-wiki for community notice]] in advance of the voting period. This final draft was developed from the previous two rounds of community review. Community members will be able to vote on these modifications starting on 17 April 2025. The vote will close on 1 May 2025, and results will be announced no later than 12 May 2025. The U4C election period, starting with a call for candidates, will open immediately following the announcement of the review results. More information will be posted on [[m:Special:MyLanguage//Universal_Code_of_Conduct/Coordinating_Committee/Election|the wiki page for the election]] soon. Please be advised that this process will require more messages to be sent here over the next two months. The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]]. Please share this message with members of your community so they can participate as well. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) 4 avril 2025 à 04:04 (CEST) </div> <!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 --> == Actualités techniques n° 2025-15 == <section begin="technews-2025-W15"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/15|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * Désormais, les [[m:Special:MyLanguage/Interface administrators|admins d’interface]] et [[m:Special:MyLanguage/Central notice administrators|admins des annonces centrales]] sont contraint techniquement d’activer l’[[m:Special:MyLanguage/Help:Two-factor authentication|authentification à deux facteurs]] avant de pouvoir utiliser leurs privilèges. À l’avenir, cela pourrait être étendu à d’autres groupes ayant des droits avancés. [https://phabricator.wikimedia.org/T150898] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:20|la tâche soumise|les {{formatnum:20}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:20||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * L’équipe Système design prépare la sortie de la nouvelle version majeur de Codex (v2.0.0) pour le 29 avril. Les contributeurices et développeurs et développeuses qui utilisent du CSS de Codex devraient consulter la [[mw:Codex/Release Timeline/2.0|documentation sur l’arrivée de la v2]], elle inclut un guidage pour les ruptures introduites dans cette version, par exemple pour <code dir=ltr style="white-space: nowrap;">font-size</code>, <code dir=ltr style="white-space: nowrap;">line-height</code> et <code dir=ltr style="white-space: nowrap;">size-icon</code>. * Les résultats de [[mw:Developer Satisfaction Survey/2025|l’enquête 2025 sur la satisfaction des développeurs et développeuses]] est désormais disponible. Merci à tous les participants ! Ces résultats aident Wikimedia à décider ce sur quoi orienter le travail et à évaluer le travail récent. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.24|MediaWiki]] '''Rencontres et évènements''' * Le [[mw:Special:MyLanguage/Wikimedia Hackathon 2025|Hackathon Wikimedia 2025]] aura lieu à Istanbul en Turquie, du 2 au 4 mai. Les inscriptions pour participer en présentiel ont lieu jusqu’au 13 avril. Avant de vous inscrire, sachez qu’il vous faudra peut-être un [https://www.mfa.gov.tr/turkish-representations.en.mfa visa] ou un [https://www.mfa.gov.tr/visa-information-for-foreigners.en.mfa e-visa] pour entrer dans le pays. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/15|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W15"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 7 avril 2025 à 20:52 (CEST) <!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28507470 --> == Wikidata and Sister Projects: An online community event == ''(Apologies for posting in English)'' Hello everyone, I am excited to share news of an upcoming online event called '''[[d:Event:Wikidata_and_Sister_Projects|Wikidata and Sister Projects]]''' celebrating the different ways Wikidata can be used to support or enhance with another Wikimedia project. The event takes place over 4 days between '''May 29 - June 1st, 2025'''. We would like to invite speakers to present at this community event, to hear success stories, challenges, showcase tools or projects you may be working on, where Wikidata has been involved in Wikipedia, Commons, WikiSource and all other WM projects. If you are interested in attending, please [[d:Special:RegisterForEvent/1291|register here]]. If you would like to speak at the event, please fill out this Session Proposal template on the [[d:Event_talk:Wikidata_and_Sister_Projects|event talk page]], where you can also ask any questions you may have. I hope to see you at the event, in the audience or as a speaker, - [[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 11 avril 2025 à 11:18 (CEST) <!-- Message envoyé par User:Danny Benjafield (WMDE)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Danny_Benjafield_(WMDE)/MassMessage_Send_List&oldid=28525705 --> == <span lang="en" dir="ltr">Tech News: 2025-16</span> == <div lang="en" dir="ltr"> <section begin="technews-2025-W16"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/16|Translations]] are available. '''Weekly highlight''' * Later this week, the default thumbnail size will be increased from 220px to 250px. This changes how pages are shown in all wikis and has been requested by some communities for many years, but wasn't previously possible due to technical limitations. [https://phabricator.wikimedia.org/T355914] * File thumbnails are now stored in discrete sizes. If a page specifies a thumbnail size that's not among the standard sizes (20, 40, 60, 120, 250, 330, 500, 960), then MediaWiki will pick the closest larger thumbnail size but will tell the browser to downscale it to the requested size. In these cases, nothing will change visually but users might load slightly larger images. If it doesn't matter which thumbnail size is used in a page, please pick one of the standard sizes to avoid the extra in-browser down-scaling step. [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images#Thumbnail_sizes][https://phabricator.wikimedia.org/T355914] '''Updates for editors''' * The Wikimedia Foundation are working on a system called [[m:Edge Uniques|Edge Uniques]] which will enable [[:w:en:A/B testing|A/B testing]], help protect against [[:w:en:Denial-of-service attack|Distributed denial-of-service attacks]] (DDoS attacks), and make it easier to understand how many visitors the Wikimedia sites have. This is so that they can more efficiently build tools which help readers, and make it easier for readers to find what they are looking for. * To improve security for users, a small percentage of logins will now require that the account owner input a one-time password [[mw:Special:MyLanguage/Help:Extension:EmailAuth|emailed to their account]]. It is recommended that you [[Special:Preferences#mw-prefsection-personal-email|check]] that the email address on your account is set correctly, and that it has been confirmed, and that you have an email set for this purpose. [https://phabricator.wikimedia.org/T390662] * "Are you interested in taking a short survey to improve tools used for reviewing or reverting edits on your Wiki?" This question will be [[phab:T389401|asked at 7 wikis starting next week]], on Recent Changes and Watchlist pages. The [[mw:Special:MyLanguage/Moderator Tools|Moderator Tools team]] wants to know more about activities that involve looking at new edits made to your Wikimedia project, and determining whether they adhere to your project's policies. * On April 15, the full Wikidata graph will no longer be supported on <bdi lang="zxx" dir="ltr">[https://query.wikidata.org/ query.wikidata.org]</bdi>. After this date, scholarly articles will be available through <bdi lang="zxx" dir="ltr" style="white-space:nowrap;">[https://query-scholarly.wikidata.org/ query-scholarly.wikidata.org]</bdi>, while the rest of the data hosted on Wikidata will be available through the <bdi lang="zxx" dir="ltr">[https://query.wikidata.org/ query.wikidata.org]</bdi> endpoint. This is part of the scheduled split of the Wikidata Graph, which was [[d:Special:MyLanguage/Wikidata:SPARQL query service/WDQS backend update/September 2024 scaling update|announced in September 2024]]. More information is [[d:Wikidata:SPARQL query service/WDQS graph split|available on Wikidata]]. * The latest quarterly [[m:Special:MyLanguage/Wikimedia Apps/Newsletter/First quarter of 2025|Wikimedia Apps Newsletter]] is now available. It covers updates, experiments, and improvements made to the Wikipedia mobile apps. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:30}} community-submitted {{PLURAL:30|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. '''Updates for technical contributors''' * The latest quarterly [[mw:Technical Community Newsletter/2025/April|Technical Community Newsletter]] is now available. This edition includes: an invitation for tool maintainers to attend the Toolforge UI Community Feedback Session on April 15th; recent community metrics; and recent technical blog posts. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.44/wmf.25|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/16|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2025-W16"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 15 avril 2025 à 02:24 (CEST) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28540654 --> == Vote now on the revised UCoC Enforcement Guidelines and U4C Charter == <div lang="en" dir="ltr" class="mw-content-ltr"> The voting period for the revisions to the Universal Code of Conduct Enforcement Guidelines ("UCoC EG") and the UCoC's Coordinating Committee Charter is open now through the end of 1 May (UTC) ([https://zonestamp.toolforge.org/1746162000 find in your time zone]). [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/2025/Voter_information|Read the information on how to participate and read over the proposal before voting]] on the UCoC page on Meta-wiki. The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review of the EG and Charter was planned and implemented by the U4C. Further information will be provided in the coming months about the review of the UCoC itself. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]]. Please share this message with members of your community so they can participate as well. In cooperation with the U4C -- [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) 17 avril 2025 à 02:34 (CEST) </div> <!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 --> == Actualités techniques n° 2025-17 == <section begin="technews-2025-W17"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/17|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * [[f:Special:MyLanguage/Wikifunctions:Main Page|Wikifunctions]] est désormais intégré à la [[w:dag:Solɔɣu|Wikipédia en dagbani]] depuis le 15 avril. C'est le premier projet qui pourra appeler des [[f:Special:MyLanguage/Wikifunctions:Introduction|fonctions de Wikifonctions]] et les intégrer dans des articles. Une fonction est quelque chose qui prend une ou plusieurs entrées et les transforme en un résultat souhaité, comme l'addition de deux nombres, la conversion de miles en mètres, le calcul du temps écoulé depuis un événement, ou la déclinaison d'un mot en une casse. Les Wikifonctions permettront aux utilisateurs de faire cela par un simple appel d'[[f:Special:MyLanguage/Wikifunctions:Catalogue|une fonction stable et globale]], plutôt que par l'intermédiaire d'un modèle local. [https://www.wikifunctions.org/wiki/Special:MyLanguage/Wikifunctions:Status_updates/2025-04-16] * Un nouveau type d'erreur ''lint'' a été créé : [[Special:LintErrors/empty-heading|{{int:linter-category-empty-heading}}]] ([[mw:Special:MyLanguage/Help:Lint errors/empty-heading|documentation]]). L'objectif de l'extension [[mw:Special:MyLanguage/Help:Extension:Linter|Linter]] est d'identifier les éléments de wikitexte qui doivent ou peuvent être corrigés dans les pages et de fournir des conseils sur les problèmes posés par ces éléments et sur la manière de les corriger. [https://phabricator.wikimedia.org/T368722] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:37|la tâche soumise|les {{formatnum:37}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:37||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * À la suite de sa publication sur ''HuggingFace'', l'ensemble de données « ''Structured Contents'' », développé par Wikimedia Enterprise, est [https://enterprise.wikimedia.com/blog/kaggle-dataset/ maintenant également disponible sur Kaggle]. Cette initiative bêta vise à rendre les données de Wikimedia plus lisibles par les machines pour les réutilisateurs de gros volumes. Cette version bêta est publiée à un emplacement déjà utilisé par les communautés de données ouvertes, afin de recueillir des commentaires qui permettront d'améliorer le produit en vue d'une future diffusion à plus grande échelle. Vous pouvez en savoir plus sur le projet ''[https://enterprise.wikimedia.com/blog/structured-contents-snapshot-api/#open-datasets Structured Contents]'', et sur la [https://enterprise.wikimedia.com/blog/structured-contents-wikipedia-infobox/ première version librement utilisable]. * Il n'y a pas de nouvelle version de MediaWiki cette semaine. '''Rencontres et évènements''' * Les équipes de rédaction et d'apprentissage automatique (''Editing and Machine Learning Teams'') invitent les bénévoles intéressés à une visioconférence pour discuter de la [[mw:Special:MyLanguage/Edit check/Peacock check|vérification « ''peacock'' »]] <small>(NdT : litt. « paon » mais également une expression idiomatique pour le langage vaniteux, prétentieux)</small>, qui est la dernière [[mw:Special:MyLanguage/Edit check|vérification de modification]] qui détectera le langage « trop promotionnel » ou « non neutre » pendant qu'un rédacteur est en train de taper. Les rédacteurs qui travaillent avec les nouveaux arrivants, ou qui aident à corriger ce type d'écriture, ou qui sont intéressés par la manière dont nous utilisons l'intelligence artificielle dans nos projets, sont invités à participer à cette réunion. La [[mw:Special:MyLanguage/Editing team/Community Conversations#Next Conversation|réunion aura lieu le 28 avril 2025]] à [https://zonestamp.toolforge.org/1745863200 18:00-19:00 UTC] et sera hébergée sur Zoom. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/17|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W17"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 21 avril 2025 à 23:00 (CEST) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28578245 --> == Actualités techniques n° 2025-18 == <section begin="technews-2025-W18"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/18|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * <span lang="en" dir="ltr" class="mw-content-ltr">Event organizers who host collaborative activities on [[m:Special:MyLanguage/CampaignEvents/Deployment status#Global Deployment Plan|multiple wikis]], including Bengali, Japanese, and Korean Wikipedias, will have access to the [[mw:Special:MyLanguage/Extension:CampaignEvents|CampaignEvents extension]] this week. Also, admins in the Wikipedia where the extension is enabled will automatically be granted the event organizer right soon. They won't have to manually grant themselves the right before they can manage events as [[phab:T386861|requested by a community]].</span> * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:19|la tâche soumise|les {{formatnum:19}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:19||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * <span lang="en" dir="ltr" class="mw-content-ltr">The release of the next major version of [[mw:Special:MyLanguage/Codex|Codex]], the design system for Wikimedia, is scheduled for 29 April 2025. Technical editors will have access to the release by the week of 5 May 2025. This update will include a number of [[mw:Special:MyLanguage/Codex/Release_Timeline/2.0#Breaking_changes|breaking changes]] and minor [[mw:Special:MyLanguage/Codex/Release_Timeline/2.0#Visual_changes|visual changes]]. Instructions on handling the breaking and visual changes are documented on [[mw:Special:MyLanguage/Codex/Release Timeline/2.0#|this page]]. Pre-release testing is reported in [[phab:T386298|T386298]], with post-release issues tracked in [[phab:T392379|T392379]] and [[phab:T392390|T392390]].</span> * <span lang="en" dir="ltr" class="mw-content-ltr">Users of [[wikitech:Special:MyLanguage/Help:Wiki_Replicas|Wiki Replicas]] will notice that the database views of <code dir="ltr">ipblocks</code>, <code dir="ltr">ipblocks_ipindex</code>, and <code dir="ltr">ipblocks_compat</code> are [[phab:T390767|now deprecated]]. Users can query the <code dir="ltr">[[mw:Special:MyLanguage/Manual:Block_table|block]]</code> and <code dir="ltr">[[mw:Special:MyLanguage/Manual:Block_target_table|block_target]]</code> new views that mirror the new tables in the production database instead. The deprecated views will be removed entirely from Wiki Replicas in June, 2025.</span> * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.27|MediaWiki]] '''En détails''' * <span lang="en" dir="ltr" class="mw-content-ltr">The latest quarterly [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/April|Language and Internationalization Newsletter]] is now available.</span> <span lang="en" dir="ltr" class="mw-content-ltr">This edition includes an overview of the improved [https://test.wikipedia.org/w/index.php?title=Special:ContentTranslation&campaign=contributionsmenu&to=es&filter-type=automatic&filter-id=previous-edits&active-list=suggestions&from=en#/ Content Translation Dashboard Tool], [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/April#Language Support for New and Existing Languages|support for new languages]], [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/April#Wiki Loves Ramadan Articles Made In Content Translation Mobile Workflow|highlights from the Wiki Loves Ramadan campaign]], [[m:Special:MyLanguage/Research:Languages Onboarding Experiment 2024 - Executive Summary|results from the Language Onboarding Experiment]], an analysis of topic diversity in articles, and information on upcoming community meetings and events.</span> '''Rencontres et évènements''' * <span lang="en" dir="ltr" class="mw-content-ltr">The [[Special:MyLanguage/Grants:Knowledge_Sharing/Connect/Calendar|Let's Connect Learning Clinic]] will take place on [https://zonestamp.toolforge.org/1745937000 April 29 at 14:30 UTC]. This edition will focus on "Understanding and Navigating Conflict in Wikimedia Projects". You can [[m:Special:MyLanguage/Event:Learning Clinic %E2%80%93 Understanding and Navigating Conflict in Wikimedia Projects (Part_1)|register now]] to attend.</span> * <span lang="en" dir="ltr" class="mw-content-ltr">The [[mw:Special:MyLanguage/Wikimedia Hackathon 2025|2025 Wikimedia Hackathon]], which brings the global technical community together to connect, brainstorm, and hack existing projects, will take place from May 2 to 4th, 2025, at Istanbul, Turkey.</span> '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/18|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W18"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 28 avril 2025 à 21:31 (CEST) <!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28585685 --> == Vote sur les modifications proposées aux lignes directrices de l'UCoC et à la charte de l'U4C == <section begin="announcement-content" /> La période de vote pour les révisions des directives d'application du Code de conduite universel et de la Charte U4C se termine le 1er mai 2025 à 23:59 UTC ([https://zonestamp.toolforge.org/1746162000 trouver dans votre fuseau horaire]). [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025/Voter information|Lisez les informations sur la façon de participer et lisez la proposition avant de voter]] sur la page UCoC de Méta-wiki. Le [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Comité de coordination du code de conduite universel (U4C)]] est un groupe mondial qui se consacre à la mise en œuvre équitable et cohérente de l'UCoC. Cet examen annuel a été planifié et mis en œuvre par l'U4C. Pour plus d'informations et pour connaître les responsabilités de l'U4C, vous pouvez [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|réviser la charte U4C]]. Veuillez partager ce message avec les membres de votre communauté dans votre langue, le cas échéant, afin qu'ils puissent également y participer. En coopération avec l'U4C -- <section end="announcement-content" /> <div lang="en" dir="ltr" class="mw-content-ltr"> [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 29 avril 2025 à 05:40 (CEST)</div> <!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 --> == Actualités techniques n° 2025-19 == <section begin="technews-2025-W19"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/19|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * La Wikimedia Foundation a partagé le dernier projet de mise à jour de son [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026|plan annuel]] pour l'année prochaine (juillet 2025-juin 2026). Cela comprend un [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026|résumé exécutif]] (également sur [[diffblog:2025/04/25/sharing-the-wikimedia-foundations-2025-2026-draft-annual-plan/|Diff]]), des détails sur les trois principaux [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Goals|objectifs]] ([[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Product & Technology OKRs|Infrastructure]], [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Goals/Volunteer Support|Soutien aux bénévoles]] et [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Goals/Effectiveness|Efficacité]]), [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Global Trends|tendances mondiales]], ainsi que le [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Budget Overview|budget]] et le [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Financial Model|modèle financier]]. Les réactions et les questions sont les bienvenues sur la [[m:Talk:Wikimedia Foundation Annual Plan/2025-2026|page de discussion]] jusqu'à la fin du mois de mai. '''Actualités pour la contribution''' * Pour les wikis qui ont l'extension [[m:Special:MyLanguage/CampaignEvents/Deployment status|CampaignEvents]] activée, deux nouvelles améliorations de fonctionnalités ont été publiées : ** Les administrateurs peuvent désormais choisir les espaces de noms autorisés pour [[m:Special:MyLanguage/Event Center/Registration|Inscription à un événement]] via [[mw:Special:MyLanguage/Community Configuration|Configuration de la communauté]] ([[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Registration/Permitted namespaces|documentation]]). Par défaut, l'enregistrement d'un événement est autorisé dans l'espace de noms Event, mais d'autres espaces de noms (tels que l'espace de noms du projet ou l'espace de noms WikiProject) peuvent désormais être ajoutés. Grâce à cette modification, les communautés telles que les WikiProjets peuvent désormais utiliser plus facilement l'enregistrement d'événements pour leurs activités collaboratives. ** Les éditeurs peuvent désormais [[mw:Special:MyLanguage/Transclusion|transclure]] la liste de collaboration sur une page wiki ([[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Collaboration list/Transclusion|documentation]]). La liste de collaboration est une liste automatisée d'événements et de wikiprojets sur les wikis, accessible via {{#special:AllEvents}} ([[w:en:Special:AllEvents|exemple]]). Désormais, la liste de collaboration peut être ajoutée à toutes sortes de pages wiki, telles que : une page principale wiki, une page WikiProjet, une page d'affiliation, une page d'événement, ou même une page d'utilisateur. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * Les développeurs qui utilisent la bibliothèque <code dir=ltr>moment</code> dans les gadgets et les scripts utilisateur doivent réviser leur code pour utiliser des alternatives comme la bibliothèque <code dir=ltr>Intl</code> ou la nouvelle bibliothèque <code dir=ltr>mediawiki.DateFormatter</code>. La bibliothèque <code dir=ltr>moment</code> a été dépréciée et commencera à enregistrer des messages dans la console du développeur. Vous pouvez voir une recherche globale pour les utilisations actuelles, et [[phab:T392532|posez des questions connexes dans cette tâche Phabricator]]. * Les développeurs qui maintiennent un outil qui interroge les tables de stockage de termes de Wikidata (<code dir=ltr style="white-space: nowrap;">wbt_*</code>) doivent mettre à jour leur code pour se connecter à une grappe de base de données séparée. Ces tables sont réparties dans une grappe de base de données distincte. Les outils qui interrogent ces tables via les répliques du wiki doivent être adaptés pour se connecter à la nouvelle grappe. [[wikitech:News/2025 Wikidata term store database split|La documentation et des liens connexes sont à votre disposition]]. [https://phabricator.wikimedia.org/T390954] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.28|MediaWiki]] '''En détails''' * La dernière [[mw:Special:MyLanguage/Extension:Chart/Project/Updates|lettre d’information du projet Chart]] est disponible. Il comprend des mises à jour sur la préparation de l'extension du déploiement à d'autres wikis dès cette semaine (à partir du 6 mai) et sur la mise à l'échelle au cours des semaines suivantes, ainsi que sur l'exploration du filtrage et de la transformation des données sources. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/19|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W19"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 6 mai 2025 à 02:14 (CEST) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28665011 --> == We will be enabling the new Charts extension on your wiki soon! == ''(Apologies for posting in English)'' Hi all! We have good news to share regarding the ongoing problem with graphs and charts affecting all wikis that use them. As you probably know, the [[:mw:Special:MyLanguage/Extension:Graph|old Graph extension]] was disabled in 2023 [[listarchive:list/wikitech-l@lists.wikimedia.org/thread/EWL4AGBEZEDMNNFTM4FRD4MHOU3CVESO/|due to security reasons]]. We’ve worked in these two years to find a solution that could replace the old extension, and provide a safer and better solution to users who wanted to showcase graphs and charts in their articles. We therefore developed the [[:mw:Special:MyLanguage/Extension:Chart|Charts extension]], which will be replacing the old Graph extension and potentially also the [[:mw:Extension:EasyTimeline|EasyTimeline extension]]. After successfully deploying the extension on Italian, Swedish, and Hebrew Wikipedia, as well as on MediaWiki.org, as part of a pilot phase, we are now happy to announce that we are moving forward with the next phase of deployment, which will also include your wiki. The deployment will happen in batches, and will start from '''May 6'''. Please, consult [[:mw:Special:MyLanguage/Extension:Chart/Project#Deployment Timeline|our page on MediaWiki.org]] to discover when the new Charts extension will be deployed on your wiki. You can also [[:mw:Special:MyLanguage/Extension:Chart|consult the documentation]] about the extension on MediaWiki.org. If you have questions, need clarifications, or just want to express your opinion about it, please refer to the [[:mw:Special:MyLanguage/Extension_talk:Chart/Project|project’s talk page on Mediawiki.org]], or ping me directly under this thread. If you encounter issues using Charts once it gets enabled on your wiki, please report it on the [[:mw:Extension_talk:Chart/Project|talk page]] or at [[phab:tag/charts|Phabricator]]. Thank you in advance! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 6 mai 2025 à 17:07 (CEST) <!-- Message envoyé par User:Sannita (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28663781 --> == Actualités techniques n° 2025-20 == <section begin="technews-2025-W20"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/20|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * Le lien [[m:Special:MyLanguage/Wikimedia URL Shortener|« Obtenir une URL raccourcie »]] dans la barre latérale inclut désormais un [[phab:T393309|code QR]]. Les utilisateurs des sites Wikimedia peuvent maintenant l’utiliser en le scannant ou en le téléchargeant pour partager et accéder rapidement au contenu partagé des sites Wikimedia, de manière pratique. '''Actualités pour la contribution''' * La Wikimedia Foundation travaille sur un système appelé [[m:Edge Uniques|« ''Edge Uniques'' »]], qui permettra de réaliser des [[w:en:A/B testing|tests A/B]], d’aider à se protéger contre les [[w:en:Denial-of-service attack|attaques par déni de service distribué]] (attaques DDoS), et de mieux comprendre combien de visiteurs les sites Wikimedia reçoivent. Cela vise à construire plus efficacement des outils utiles aux lecteurs, et de les aider à trouver ce qu'ils cherchent. Les Actualités techniques en ont [[m:Special:MyLanguage/Tech/News/2025/16|déjà parlé]]. Le déploiement sera progressif. Certains pourraient voir le cookie « ''Edge Uniques'' » à partir de la semaine du 19 mai. Vous pouvez en discuter sur la [[m:Talk:Edge Uniques|page de discussion]]. * À partir du 19 mai 2025, les organisateurs d’événements sur les wikis disposant de l’[[mw:Special:MyLanguage/Help:Extension:CampaignEvents|extension « CampaignEvents »]] pourront utiliser l’[[m:Special:MyLanguage/Event Center/Registration|inscription aux événements]] dans l’espace de noms du projet (par exemple, l’espace Wikipédia, l’espace Wikidata). Avec ce changement, les communautés n’ont plus besoin d’administrateurs pour utiliser cette fonctionnalité. Toutefois, les wikis qui ne souhaitent pas ce changement peuvent retirer et ajouter les espaces de noms autorisés sur [[Special:CommunityConfiguration/CampaignEvents]]. * Le projet Wikipédia dispose désormais d’un {{int:project-localized-name-group-wikipedia/fr}} en [[d:Q36720|nupe]] ([[w:nup:|<code>w:nup:</code>]]). Il s’agit d’une langue principalement parlée dans la région du centre-nord du Nigeria. Les locuteurs de cette langue sont invités à contribuer à la [[w:nup:Tatacin feregi|nouvelle Wikipédia]]. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * Les développeurs peuvent désormais accéder à la Wikipédia néerlandaise pré-analysée, parmi d’autres (anglais, allemand, français, espagnol, italien et portugais), via les [https://enterprise.wikimedia.com/docs/snapshot/#structured-contents-snapshot-bundle-info-beta instantanés « ''Structured Contents'' » (bêta)]. Le contenu comprend des résumés Wikipédia analysés, des descriptions, des images principales, des infoboxes, des sections d’articles et des références. * Le point de terminaison de l’API REST <code dir="ltr">/page/data-parsoid</code> n’est plus utilisé et sera obsolète. Il est [[phab:T393557|prévu qu’il soit désactivé]] le 7 juin 2025. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.1|MediaWiki]] '''En détails''' * Le [https://wikitech.wikimedia.org/wiki/News/2025_Cloud_VPS_VXLAN_IPv6_migration support IPv6] est un nouveau réseau virtuel Cloud qui améliore considérablement l’évolutivité, la sécurité et la préparation des plateformes Wikimedia pour l’avenir. Si vous êtes un contributeur technique curieux d’en savoir plus, consultez [https://techblog.wikimedia.org/2025/05/06/wikimedia-cloud-vps-ipv6-support/ ce billet de blogue] pour un aperçu détaillé de la transition vers l’IPv6. '''Rencontres et évènements''' * La deuxième édition de 2025 de [[m:Special:MyLanguage/Afrika Baraza|« Afrika Baraza »]], une plateforme virtuelle permettant aux Wikimédiens africains de se connecter, aura lieu le [https://zonestamp.toolforge.org/1747328400 15 mai à 17 h UTC]. Cette édition sera axée sur des discussions concernant la [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026|planification annuelle et les avancées de Wikimedia]]. * La [[m:Special:MyLanguage/MENA Connect Community Call|« ''MENA Connect Community Call'' »]], une réunion virtuelle permettant aux Wikimédiens de la [[w:fr:MENA|région Moyen-Orient et Afrique du Nord]] (MENA) de se rencontrer, aura lieu le [https://zonestamp.toolforge.org/1747501200 17 mai à 17 h UTC]. Vous pouvez [[m:Event:MENA Connect (Wiki_Diwan) APP Call|vous inscrire dès maintenant]] pour y assister. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/20|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W20"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 13 mai 2025 à 00:37 (CEST) <!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28714188 --> == Appel aux candidatures pour le Comité de Coordination du Code de Conduite Universel (U4C). == <section begin="announcement-content" /> Les résultats du vote sur les directives d'application et la charte du Comité de Coordination du Code de Conduite Universel (U4C) sont disponibles [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025#Results|sur Méta-wiki]]. Vous pouvez désormais [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025/Candidates|soumettre votre candidature pour siéger à l'U4C]] <s>à partir du 29 mai 2025 à 12:00 UTC</s>. Des informations sur [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025|l'éligibilité, le processus, et la chronologie sont disponibles sur Méta-wiki]]. Le vote sur les candidats sera ouvert le 1<sup>er</sup> juin 2025 et durera deux semaines, se finissant donc le 15 juin 2025 à 12:00 UTC. Si vous avez des questions, vous pouvez les poser sur [[m:Talk:Universal Code of Conduct/Coordinating Committee/Election/2025|la page de discussion pour l'élection]]. -- en coopération avec l'U4C, </div><section end="announcement-content" /> <bdi lang="en" dir="ltr">[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|discussion]])</bdi> 16 mai 2025 à 00:06 (CEST) <!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 --> :Rectification : Vous pouvez désormais [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025/Candidates|soumettre votre candidature pour siéger à l'U4C]] du 14 mai au 28 mai. :{{BlocCitation|You can submit your candidacy from May 14 until May 28, 2025}} :--&nbsp;◄&nbsp;[[Utilisateur:DavidL|'''D'''avid&nbsp;'''L''']]&nbsp;•&nbsp;[[Discussion Utilisateur:DavidL|discuter]]&nbsp;► 16 mai 2025 à 16:36 (CEST) == <span lang="en" dir="ltr">Tech News: 2025-21</span> == <div lang="en" dir="ltr"> <section begin="technews-2025-W21"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/21|Translations]] are available. '''Weekly highlight''' * The Editing Team and the Machine Learning Team are working on a new check for newcomers: [[mw:Edit check/Peacock check|Peacock check]]. Using a prediction model, this check will encourage editors to improve the tone of their edits, using artificial intelligence. We invite volunteers to review the first version of the Peacock language model for the following languages: Arabic, Spanish, Portuguese, English, and Japanese. Users from these wikis interested in reviewing this model are [[mw:Edit check/Peacock check/model test|invited to sign up at MediaWiki.org]]. The deadline to sign up is on May 23, which will be the start date of the test. '''Updates for editors''' * From May 20, 2025, [[m:Special:MyLanguage/Oversight policy|oversighters]] and [[m:Special:MyLanguage/Meta:CheckUsers|checkusers]] will need to have their accounts secured with two-factor authentication (2FA) to be able to use their advanced rights. All users who belong to these two groups and do not have 2FA enabled have been informed. In the future, this requirement may be extended to other users with advanced rights. [[m:Special:MyLanguage/Mandatory two-factor authentication for users with some extended rights|Learn more]]. * [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Wishlist item]] [[m:Special:MyLanguage/Community Wishlist Survey 2023/Multiblocks|Multiblocks]] will begin mass deployment by the end of the month: all non-Wikipedia projects plus Catalan Wikipedia will adopt Multiblocks in the week of May 26, while all other Wikipedias will adopt it in the week of June 2. Please [[m:Talk:Community Wishlist Survey 2023/Multiblocks|contact the team]] if you have concerns. Administrators can test the new user interface now on your own wiki by browsing to [{{fullurl:Special:Block|usecodex=1}} {{#special:Block}}?usecodex=1], and can test the full multiblocks functionality [[testwiki:Special:Block|on testwiki]]. Multiblocks is the feature that makes it possible for administrators to impose different types of blocks on the same user at the same time. See the [[mw:Special:MyLanguage/Help:Manage blocks|help page]] for more information. [https://phabricator.wikimedia.org/T377121] * Later this week, the [[{{#special:SpecialPages}}]] listing of almost all special pages will be updated with a new design. This page has been [[phab:T219543|redesigned]] to improve the user experience in a few ways, including: The ability to search for names and aliases of the special pages, sorting, more visible marking of restricted special pages, and a more mobile-friendly look. The new version can be [https://meta.wikimedia.beta.wmflabs.org/wiki/Special:SpecialPages previewed] at Beta Cluster now, and feedback shared in the task. [https://phabricator.wikimedia.org/T219543] * The [[mw:Special:MyLanguage/Extension:Chart|Chart extension]] is being enabled on more wikis. For a detailed list of when the extension will be enabled on your wiki, please read the [[mw:Special:MyLanguage/Extension:Chart/Project#Deployment Timeline|deployment timeline]]. * [[f:Special:MyLanguage/Wikifunctions:Main Page|Wikifunctions]] will be deployed on May 27 on five Wiktionaries: [[wikt:ha:|Hausa]], [[wikt:ig:|Igbo]], [[wikt:bn:|Bengali]], [[wikt:ml:|Malayalam]], and [[wikt:dv:|Dhivehi/Maldivian]]. This is the second batch of deployment planned for the project. After deployment, the projects will be able to call [[f:Special:MyLanguage/Wikifunctions:Introduction|functions from Wikifunctions]] and integrate them in their pages. A function is something that takes one or more inputs and transforms them into a desired output, such as adding up two numbers, converting miles into metres, calculating how much time has passed since an event, or declining a word into a case. Wikifunctions will allow users to do that through a simple call of [[f:Special:MyLanguage/Wikifunctions:Catalogue|a stable and global function]], rather than via a local template. * Later this week, the Wikimedia Foundation will publish a hub for [[diffblog:2024/07/09/on-the-value-of-experimentation/|experiments]]. This is to showcase and get user feedback on product experiments. The experiments help the Wikimedia movement [[diffblog:2023/07/13/exploring-paths-for-the-future-of-free-knowledge-new-wikipedia-chatgpt-plugin-leveraging-rich-media-social-apps-and-other-experiments/|understand new users]], how they interact with the internet and how it could affect the Wikimedia movement. Some examples are [[m:Special:MyLanguage/Future Audiences/Generated Video|generated video]], the [[m:Special:MyLanguage/Future Audiences/Roblox game|Wikipedia Roblox speedrun game]] and [[m:Special:MyLanguage/Future Audiences/Discord bot|the Discord bot]]. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:29}} community-submitted {{PLURAL:29|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, there was a bug with creating an account using the API, which has now been fixed. [https://phabricator.wikimedia.org/T390751] '''Updates for technical contributors''' * Gadgets and user scripts that interact with [[{{#special:Block}}]] may need to be updated to work with the new [[mw:Special:MyLanguage/Help:Manage blocks|manage blocks interface]]. Please review the [[mw:Help:Manage blocks/Developers|developer guide]] for more information. If you need help or are unable to adapt your script to the new interface, please let the team know on the [[mw:Help talk:Manage blocks/Developers|talk page]]. [https://phabricator.wikimedia.org/T377121] * The <code dir=ltr>mw.title</code> object allows you to get information about a specific wiki page in the [[w:en:Wikipedia:Lua|Lua]] programming language. Starting this week, a new property will be added to the object, named <code dir=ltr>isDisambiguationPage</code>. This property allows you to check if a page is a disambiguation page, without the need to write a custom function. [https://phabricator.wikimedia.org/T71441] * [[File:Octicons-tools.svg|15px|link=|class=skin-invert|Advanced item]] User script developers can use a [[toolforge:gitlab-content|new reverse proxy tool]] to load javascript and css from [[gitlab:|gitlab.wikimedia.org]] with <code dir=ltr>mw.loader.load</code>. The tool's author hopes this will enable collaborative development workflows for user scripts including linting, unit tests, code generation, and code review on <bdi lang="zxx" dir="ltr">gitlab.wikimedia.org</bdi> without a separate copy-and-paste step to publish scripts to a Wikimedia wiki for integration and acceptance testing. See [[wikitech:Tool:Gitlab-content|Tool:Gitlab-content on Wikitech]] for more information. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.45/wmf.2|MediaWiki]] '''Meetings and events''' * The 12th edition of [[m:Special:MyLanguage/Wiki Workshop 2025|Wiki Workshop 2025]], a forum that brings together researchers that explore all aspects of Wikimedia projects, will be held virtually on 21-22 May. Researchers can [https://pretix.eu/wikimedia/wikiworkshop2025/ register now]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/21|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2025-W21"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 20 mai 2025 à 01:12 (CEST) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28724712 --> == RfC ongoing regarding Abstract Wikipedia (and your project) == <div lang="en" dir="ltr" class="mw-content-ltr"> ''(Apologies for posting in English, if this is not your first language)'' Hello all! We opened a discussion on Meta about a very delicate issue for the development of [[:m:Special:MyLanguage/Abstract Wikipedia|Abstract Wikipedia]]: where to store the abstract content that will be developed through functions from Wikifunctions and data from Wikidata. Since some of the hypothesis involve your project, we wanted to hear your thoughts too. We want to make the decision process clear: we do not yet know which option we want to use, which is why we are consulting here. We will take the arguments from the Wikimedia communities into account, and we want to consult with the different communities and hear arguments that will help us with the decision. The decision will be made and communicated after the consultation period by the Foundation. You can read the various hypothesis and have your say at [[:m:Abstract Wikipedia/Location of Abstract Content|Abstract Wikipedia/Location of Abstract Content]]. Thank you in advance! -- [[User:Sannita (WMF)|Sannita (WMF)]] ([[User talk:Sannita (WMF)|<span class="signature-talk">{{int:Talkpagelinktext}}</span>]]) 22 mai 2025 à 17:26 (CEST) </div> <!-- Message envoyé par User:Sannita (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28768453 --> == Actualités techniques n° 2025-22 == <section begin="technews-2025-W22"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/22|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * Une discussion communautaire à l’échelle du mouvement est désormais ouverte sur Meta au sujet d’un point très délicat pour le développement de [[m:Special:MyLanguage/Abstract Wikipedia|« Abstract Wikipedia »]] : où stocker le contenu abstrait qui sera développé à partir des fonctions de Wikifunctions et des données de Wikidata. La discussion est ouverte jusqu’au 12 juin sur [[m:Special:MyLanguage/Abstract Wikipedia/Location of Abstract Content|Abstract Wikipedia/Location of Abstract Content]], et tous les avis sont les bienvenus. La décision sera prise et communiquée par la Fondation à l’issue de la période de consultation. '''Actualités pour la contribution''' * Depuis la semaine dernière, sur tous les wikis excepté [[phab:T388604|les vingt plus grands]], les utilisateurs de l’éditeur visuel mobile disposent de [[phab:T385851|nouveaux outils dans la barre de menu]], accessibles via le nouveau bouton de la barre d’outils <code>+</code>. Pour commencer, le nouveau menu proposera des options pour ajouter : des références, des hiéroglyphes et des blocs de code. Le déploiement sur les autres wikis est [[phab:T388605|prévu]] pour le mois de juin. * [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] La fonction d’analyse <code dir=ltr>[[mw:Special:MyLanguage/Help:Extension:ParserFunctions##ifexist|#ifexist]]</code> ne créera plus de lien vers sa page cible. Cela améliorera l’utilité de [[{{#special:WantedPages}}]], qui ne listera à terme que les pages réellement ciblées par un lien rouge. Ce changement se fera progressivement à mesure que les pages sources seront mises à jour. [https://phabricator.wikimedia.org/T14019] * Cette semaine, l’équipe des outils de modération va lancer [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|un nouveau filtre dans les modifications récentes]], en commençant par la Wikipédia en indonésien. Ce nouveau filtre met en évidence les modifications susceptibles d’être annulées. L’objectif est d’aider les patrouilleurs des modifications récentes à repérer les contributions potentiellement problématiques. D’autres wikis bénéficieront de ce filtre à l’avenir. * Lorsqu’ils cliquent sur une barre de recherche vide, les utilisateurs non connectés verront des suggestions d’articles à lire. Cette fonctionnalité sera disponible à la fois sur ordinateur et sur mobile. Les lecteurs des Wikipédias en catalan, hébreu et italien ainsi que de certains projets frères recevront ce changement entre le 21 mai et la mi-juin. Les lecteurs des autres wikis le recevront plus tard. L’objectif est d’encourager les utilisateurs à lire davantage les wikis. [[mw:Special:MyLanguage/Reading/Web/Content Discovery Experiments/Search Suggestions|En savoir plus]]. * Certains utilisateurs de l’application Wikipédia sur Android peuvent utiliser une nouvelle fonctionnalité destinée aux lecteurs : [[mw:Special:MyLanguage/Wikimedia Apps/Team/Android/TrivaGame|« WikiGames »]], un jeu quotidien de quiz basé sur des événements historiques réels. Le déploiement a commencé sous forme de test A/B, disponible pour 50 % des utilisateurs dans les langues suivantes : anglais, français, portugais, russe, espagnol, arabe, chinois et turc. * L’[[mw:Special:MyLanguage/Extension:Newsletter|extension « Newsletter »]] disponible sur MediaWiki.org permet la création de [[mw:Special:Newsletters|divers bulletins d’information]] à destination des utilisateurs de tous les wikis. L’extension peut désormais publier de nouveaux numéros sous forme de liens vers des sections sur une page existante, au lieu de nécessiter une nouvelle page pour chaque numéro. [https://phabricator.wikimedia.org/T393844] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:32|la tâche soumise|les {{formatnum:32}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:32||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * Les champs <code dir=ltr>[[mw:Special:MyLanguage/Manual:Ipblocks table|ipblocks]]</code>, précédemment déclarés obsolètes, seront supprimés début juin dans les [[wikitech:Help:Wiki Replicas|Wiki Replicas]]. Il est recommandé aux utilisateurs d’interroger les nouveaux champs <code dir=ltr>[[mw:Special:MyLanguage/Manual:Block table|block]]</code> et <code dir=ltr>[[mw:Special:MyLanguage/Manual:Block target table|block_target]]</code> à la place. * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.3|MediaWiki]] '''Rencontres et évènements''' * [[d:Special:MyLanguage/Event:Wikidata and Sister Projects|« Wikidata et les projets frères »]] est un événement en ligne de plusieurs jours qui portera sur l’intégration de Wikidata à Wikipédia et aux autres projets Wikimedia. L’événement se déroulera du 29 mai au 1<sup>er</sup> juin. Vous pouvez [[d:Special:MyLanguage/Event:Wikidata and Sister Projects#Sessions|consulter le programme]] et [[d:Special:RegisterForEvent/1291|vous inscrire]]. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/22|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W22"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 26 mai 2025 à 22:04 (CEST) <!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28788673 --> == Sélection 2025 du conseil d'administration de la fondation Wikimédia et appel à questions == <section begin="announcement-content" /> :''[[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Announcement/Selection announcement|{{int:interlanguage-link-mul}}]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation elections/2025/Announcement/Selection announcement}}&language=&action=page&filter= {{int:please-translate}}]'' Chers tous et toutes, Cette année, le mandat de deux administrateurs sélectionnés par la communauté et les affiliés au conseil d'administration de la Fondation Wikimédia prendra fin [1]. Le conseil invite l'ensemble du mouvement à participer au processus de sélection de cette année et à voter pour pourvoir ces sièges. Le Comité des élections supervisera ce processus avec le soutien du personnel de la Fondation [2]. Le Comité de gouvernance, composé de membres du Conseil d'administration non-candidats au processus de sélection des membres du Conseil d'administration 2025 sélectionnés par la communauté et les affiliés (Raju Narisetti, Shani Evenstein Sigalov, Lorenzo Losa, Kathy Collins, Victoria Doronina et Esra'a Al Shafei) [3], est chargé de superviser le processus de sélection des membres du Conseil d'administration 2025 et de tenir le Conseil d'administration au courant de la situation. Pour plus de détails sur les rôles de la commission des élections, du conseil d'administration et du personnel, cliquez ici [4]. En voici les dates clés : * 22 mai - 5 juin : Annonce ( la présente communication) et période d'appel à questions. [6] * 17 juin - 1er juillet 2025 : Appel à candidatures * Juillet 2025 : Si nécessaire, les affiliés votent pour présélectionner les candidats si 10 d'entre eux ou plus se présentent [5]. * Août 2025 : Période de la campagne * Août - septembre 2025 : Période de vote communautaire de deux semaines * Octobre - novembre 2025 : Vérification des antécédents des candidats sélectionnés * Réunion du conseil d'administration en décembre 2025 : Installation des nouveaux membres du conseil d'administration Pour en savoir plus sur le processus de sélection de 2025 - y compris le calendrier détaillé, le processus de candidature, les règles de la campagne et les critères d'éligibilité des électeurs -, veuillez consulter cette page Meta-wiki. [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025|[link]]]. '''Appel à questions''' Lors de chaque processus de sélection, la communauté a la possibilité de soumettre des questions auxquelles les candidats au conseil d'administration devront répondre. Le comité électoral sélectionne les questions à partir de la liste établie par la communauté pour que les candidats y répondent. Les candidats doivent répondre à toutes les questions posées dans le dossier de candidature pour être éligibles, faute de quoi leur candidature sera rejetée. Cette année, le comité électoral sélectionnera 5 questions auxquelles les candidats devront répondre. Les questions sélectionnées peuvent être une combinaison de celles qui ont été soumises par la communauté, si elles sont similaires ou liées. [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025/Questions_for_candidates|[link]]] '''Bénévoles des élections''' Une autre façon de participer au processus de sélection de 2025 est de devenir bénévole des élections. Les bénévoles électoraux constituent un pont entre le comité électoral et leur communauté respective. Ils veillent à ce que leur communauté soit représentée et les incitent à voter. Pour en savoir plus sur le programme et les modalités d'adhésion, consultez cette page Meta-wiki. [[m:Wikimedia_Foundation_elections/2025/Election_volunteers|[link]]]. Je vous remercie ! [1] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2022/Results [2] https://foundation.wikimedia.org/wiki/Committee:Elections_Committee_Charter [3] https://foundation.wikimedia.org/wiki/Resolution:Committee_Membership,_December_2024 [4] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections_committee/Roles [5] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/FAQ [6] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/Questions_for_candidates Bien à vous, Victoria Doronina Liaison du conseil d'administration avec le comité des élections Comité de gouvernance<section end="announcement-content" /> [[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 28 mai 2025 à 05:07 (CEST) <!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 --> == Actualités techniques n° 2025-23 == <section begin="technews-2025-W23"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/23|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * L'[[mw:Special:MyLanguage/Extension:Chart|extension Chart]] est maintenant disponible sur tous les wikis Wikimedia. Les éditeurs peuvent utiliser cette nouvelle extension pour créer des visualisations de données interactives comme des diagrammes à barres, à lignes, avec des zones, et circulaires. Chart a été créée pour remplacer la plupart des utilisations de l'ancienne [[mw:Special:MyLanguage/Extension:Graph|extension Graph]]. '''Actualités pour la contribution''' * Il est maintenant plus simple de configurer les citations automatiques pour votre wiki dans le [[mw:Special:MyLanguage/Citoid/Enabling Citoid on your wiki|générateur de citations]] de l'éditeur visuel. Les administrateurs peuvent maintenant définir un modèle par défaut en utilisant la clé <code dir=ltr>_default</code> dans la page locale <bdi lang="en" dir="ltr">[[MediaWiki:Citoid-template-type-map.json]]</bdi> ([[mw:Special:Diff/6969653/7646386|exemple de modification]]). Définir ce réglage par défaut permettra aussi de pérenniser vos configurations existantes lorsque de [[phab:T347823|nouveaux types d'objets]] seront ajoutés à l'avenir. Vous pouvez toujours définir des modèles pour des types d'objets individuels et ils seront prioritaires par rapport au modèle par défaut. [https://phabricator.wikimedia.org/T384709] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:20|la tâche soumise|les {{formatnum:20}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:20||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * À partir de la semaine du 2 juin, les robots qui utilisent <code dir=ltr>action=login</code> ou <code dir=ltr>action=clientlogin</code> pour s'authentifier auront un taux d'échec plus fréquent. Cela est dû à des protections plus fortes contre les connexions suspectes. Les robots qui utilisent des [[mw:Special:MyLanguage/Manual:Bot passwords|mots de passe de robots]] ou une authentification sans connexion telle que [[mw:Special:MyLanguage/OAuth/Owner-only consumers|OAuth]] ne seront pas affectés. Si votre bot n'utilise aucun des deux, vous devriez le mettre à jour ; utiliser <code dir=ltr>action=login</code> sans un mot de passe de robot a été rendu désuet [[listarchive:list/wikitech-l@lists.wikimedia.org/message/3EEMN7VQX5G7WMQI5K2GP5JC2336DPTD/|en 2016]]. Pour la plupart des robots, cela nécessite seulement de changer quel mot de passe ce dernier utilise. [https://phabricator.wikimedia.org/T395205] * À partir de cette semaine, les wikis Wikimedia permettront des fonctionnalités ES2017 dans le code JavaScript pour le code officiel, les gadgets et les scripts utilisateurs. La fonctionnalité la plus visible d'ES2017 est la syntaxe <bdi lang="zxx" dir="ltr"><code>async</code>/<code>await</code></bdi>, ce qui permet un code plus facile à lire. Jusqu'à cette semaine, la plateforme ne permettait que jusqu'à ES2016, et quelques mois plus tôt, jusqu'à ES2015. [https://phabricator.wikimedia.org/T381537] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.4|MediaWiki]] '''Rencontres et évènements''' * Les demandes de bourse d'études pour participer à la [[m:Special:MyLanguage/GLAM Wiki 2025|conférence GLAM 2025]] sont maintenant ouvertes. La conférence aura lieu du 30 octobre au 1er novembre, à Lisbonne, au Portugal. Les contributeurs GLAM qui n'ont pas les moyens de financer leur participation peuvent [[m:Special:MyLanguage/GLAM Wiki 2025/Scholarships|faire une demande ici]]. La date limite de candidature est le 7 juin. '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/23|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W23"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 3 juin 2025 à 01:54 (CEST) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28819186 --> == Actualités techniques n° 2025-24 == <section begin="technews-2025-W24"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/24|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * L’[[mw:Special:MyLanguage/Trust and Safety Product|équipe produits Confiance et sûreté]] finalise les travaux nécessaires au déploiement des [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] sur les grandes Wikipédias plus tard ce mois-ci. L’équipe a collaboré avec les stewards et d’autres utilisateurs disposant de droits étendus afin d’anticiper et de traiter de nombreux cas d’usage qui pourraient se présenter sur les wikis de grande taille, afin que les membres des communautés puissent continuer à modérer et à patrouiller efficacement les comptes temporaires. Il s’agira de la deuxième des trois phases de déploiement ; la dernière aura lieu en septembre au plus tôt. Pour plus d’informations sur les développements récents du projet, [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Updates|voir cette mise à jour]]. Si vous avez des commentaires ou des questions, écrivez sur la [[mw:Talk:Trust and Safety Product/Temporary Accounts|page de discussion]] et [[m:Event:CEE Catch up Nr. 10 (June 2025)|rejoignez un « CEE Catch Up »]] ce mardi. '''Actualités pour la contribution''' * [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] La fonctionnalité [[mw:Special:MyLanguage/Help:Watchlist expiry|« expiration de la liste de suivi »]] permet aux contributeurs de suivre des pages pendant une durée limitée. Une fois ce délai écoulé, la page est automatiquement retirée de votre liste de suivi. À partir de cette semaine, vous pouvez définir une préférence pour la durée par défaut pendant laquelle vous souhaitez suivre les pages. Les [[Special:Preferences#mw-prefsection-watchlist-pageswatchlist|préférences]] permettent également de définir différentes durées par défaut selon que vous modifiez une page existante, que vous en créez une nouvelle ou que vous utilisez l’annulation rapide (''rollback''). [https://phabricator.wikimedia.org/T265716] [[File:Talk pages default look (April 2023).jpg|thumb|alt=Capture d'écran des améliorations visuelles apportées aux pages de discussion|Exemple d'une page de discussion avec les améliorations, en français.]] * L’apparence des pages de discussion va changer sur la quasi-totalité des Wikipédias ([[m:Special:MyLanguage/Tech/News/2024/19|certaines]] ont déjà reçu ce nouveau design, [[phab:T379264|quelques-unes]] le recevront plus tard). Vous pouvez lire les détails concernant ces changements [[diffblog:2024/05/02/making-talk-pages-better-for-everyone/|sur ''Diff'']]. Il est possible de désactiver ces modifications [[Special:Preferences#mw-prefsection-editing-discussion|dans les préférences utilisateur]] (« {{int:discussiontools-preference-visualenhancements}} »). [https://phabricator.wikimedia.org/T319146][https://phabricator.wikimedia.org/T392121] * Les utilisateurs disposant de certains droits étendus (y compris les administrateurs, bureaucrates, vérificateurs, masqueurs et stewards) peuvent désormais voir les adresses IP de tous les comptes temporaires [[phab:T358853|révélées automatiquement]] pendant des périodes limitées, lorsqu’ils doivent lutter contre du vandalisme rapide impliquant des changements fréquents de compte. Cette fonctionnalité a été demandée par les stewards. [https://phabricator.wikimedia.org/T386492] * Cette semaine, les équipes des outils de modération et d’apprentissage automatique poursuivent le déploiement [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|d’un nouveau filtre dans les Modifications récentes]], en l’étendant à plusieurs autres Wikipédias. Ce filtre utilise le modèle « Revert Risk », développé par l’équipe de recherche, pour mettre en évidence les modifications susceptibles d’être annulées et aider les patrouilleurs à repérer les contributions potentiellement problématiques. La fonctionnalité sera déployée sur les Wikipédias suivantes : {{int:project-localized-name-afwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-bewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-bnwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-cywiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-hawwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-iswiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-kkwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-simplewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-trwiki/fr}}. Le déploiement se poursuivra dans les semaines à venir pour inclure [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|le reste des Wikipédias concernées par ce projet]]. [https://phabricator.wikimedia.org/T391964] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * Les éditeurs de filtres anti-abus actifs sur Meta-Wiki et les grandes Wikipédias sont priés de mettre à jour les filtres pour les rendre compatibles avec les comptes temporaires. Un lien vers les instructions ainsi que vers les listes privées des filtres à vérifier est [[phab:T369611|disponible sur Phabricator]]. * Les modules Lua ont désormais accès au nom de l’image miniature associée à une page, et sur [https://gerrit.wikimedia.org/g/operations/mediawiki-config/+/2e4ab14aa15bb95568f9c07dd777065901eb2126/wmf-config/InitialiseSettings.php#10849 certains wikis], aux informations d’évaluation des WikiProjets. Cela est possible grâce à deux nouvelles propriétés des objets [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#added-by-extensions|mw.title]], nommées <code dir=ltr>pageImage</code> et <code dir=ltr>pageAssessments</code>. [https://phabricator.wikimedia.org/T131911][https://phabricator.wikimedia.org/T380122] * [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.5|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2025/24|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2025-W24"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 10 juin 2025 à 03:16 (CEST) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28846858 --> == Vote now in the 2025 U4C Election == <div lang="en" dir="ltr" class="mw-content-ltr"> Apologies for writing in English. {{Int:Please-translate}} Eligible voters are asked to participate in the 2025 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2025|2025 Election information page]]. The vote closes on 17 June 2025 at [https://zonestamp.toolforge.org/1750161600 12:00 UTC]. Please vote if your account is eligible. Results will be available by 1 July 2025. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 14 juin 2025 à 01:00 (CEST) </div> <!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28848819 --> d1hj7bu74og38kwfnph37ka4fvzyy3z Observations fondamentales : Observatoire des politiques sur les savoirs ouverts, 2017-2020/Introduction 0 82409 744720 743704 2025-06-13T22:19:12Z LodestarChariot2 120009 Mises à jour du texte 744720 wikitext text/x-wiki <div align="center"><strong>Caroline Winter, avec Alyssa Arbuckle, Tanja Niemann, Lynne Siemens, Brittany Amell, et Ray Siemens</strong><ref>Cette introduction révise et met à jour les documents publiés à l’origine dans les publications Open Scholarship Press Collection (Winter et al. 2023) et Curated Volume (Winter et al. 2024) sur la politique citée ci-dessous.</ref></div> Alors que le mouvement d’Érudition ouverte a pris de l’ampleur ces dernières années, la politique s’est imposée comme une question clé. En particulier, les questions relatives à la politique d’érudition ouverte sont les suivantes : Comment et dans quelle mesure la politique fait-elle progresser l’érudition ouverte ? Quel est l’effet de la politique sur les individus et leur travail ? Comment la politique affecte-t-elle les pratiques d’érudition ouverte ? Ce volume reflète les premières années, 2017-2020, de l’Observatoire des politiques de l’érudition ouverte ([https://ospolicyobservatory.uvic.ca/ Open Scholarship Policy Observatory], OSPO). Il s’agit d’un centre d’informations et de ressources liées à tous les aspects de l’érudition ouverte et qui comprend une collection de politiques documentées ainsi qu’une analyse de politiques. Depuis sa création en 2017, l’OSPO a été coordonné par l’Institut canadien du savoir social ([https://c-ski.ca/ Canadian Social Knowledge Institute], C-SKI), basé dans le Laboratoire des cultures textuelles électroniques ([https://etcl.uvic.ca/ Electronic Textual Cultures Lab], ETCL) de l’Université de Victoria, en tant qu’initiative du [https://inke.ca/activity-clusters/#policy Pôle politique] du Partenariat [https://inke.ca/ Implementing New Knowledge Environments] (INKE), de 2020 à ce jour, co-facilité par Tanja Niemann (Érudit) et Lynne Siemens (UVic). L’OSPO suit et reflète les développements politiques liés à l’érudition ouverte au Canada et au-delà, en analysant les changements politiques et leur pertinence pour les chercheurs, les professionnels de l’information, les bibliothécaires, les professeurs et les décideurs politiques. L’OSPO a été créé en reconnaissance de l’élaboration d’un nombre croissant de politiques et de mandats en matière de libre accès, ainsi que de la confusion qui règne quant aux différentes voies vers le libre accès et à l’approche la plus efficace (Milligan et al. 2019). L’OSPO suit l’évolution des politiques nationales et internationales et fait part de ses conclusions à la communauté, dans le but de renforcer la compréhension et la communauté autour de la politique de l’érudition ouverte au Canada et à l’étranger. Par exemple, de plus en plus de gouvernements nationaux, d’organismes de financement et d’institutions élaborent des politiques relatives au libre accès aux données de recherche et aux publications, qui se recoupent et s’opposent parfois de manière complexe. D’autres formes et types de politiques qui constituent l’écosystème de la recherche au sens large ont également une incidence sur la politique de bourses d’études ouvertes, telles que les politiques des éditeurs en matière de conservation des droits, les normes internationales en matière d’infrastructure numérique et les lignes directrices des établissements en matière d’évaluation, de titularisation et de promotion. Compte tenu du rôle joué par les politiques dans la manière dont les chercheurs travaillent et dans la façon dont ils partagent leurs travaux, il est essentiel de comprendre le paysage politique – à la fois ses fondements et ses tendances émergentes – pour faire progresser la recherche ouverte. L’OSPO s’aligne sur les publications de l’Open Scholarship Press sur la politique. L’une d’entre elles est la collection [[b:en:Open Scholarship Press Collections: Policy | Open Scholarship Press Collections: Policy]] (Winter et al. 2023), qui passe en revue la littérature actuelle sur la politique d’érudition ouverte, offrant un aperçu du domaine de l’analyse et de la critique de la politique. L’objectif est de dessiner les contours de ce domaine et d’identifier les voies critiques principales, tout en reconnaissant qu’il ne s’agit qu’un aperçu et qu’il ne peut donc pas couvrir l’ensemble du domaine en détail. L’autre est l’[[b:en:Open Scholarship Press Curated Volumes: Policy |Open Scholarship Press Curated Volumes: Policy]] (Winter et al. 2024) qui rassemble une sélection de ressources clés pour un engagement plus poussé. La plupart sont des politiques et des principes fondamentaux, mais des exemples d’aperçus théoriques, d’études de recherche et d’analyses critiques sont également inclus afin de fournir un large aperçu du paysage politique de l’érudition ouverte. Les deux volumes sont accompagnés d’aperçus analytiques qui reflètent le domaine et ses orientations importantes. ==L'Érudition ouverte et ses points focaux== L’érudition ouverte est un terme générique qui fait référence à la fois aux pratiques d’érudition ouverte, telles que l’évaluation ouverte par les pairs, et à leurs résultats, tels que les publications en libre accès. Bien que l’érudition ouverte soit parfois qualifiée de «&#8239;science ouverte&#8239;», elle n’est pas spécifique à une discipline, mais englobe toutes les disciplines et une variété de pratiques et de principes interdépendants. Ce qui a commencé il y a plus de 20 ans à petite échelle et au niveau local est devenu un mouvement mondial, mais qui progresse de manière inégale et, à bien des égards, incertaine (Tennant et al. 2019). Étant donné que l’érudition ouverte englobe tant de choses, il est difficile de la définir. George Veletsianos (2016) la décrit comme «&#8239;la diffusion large et étendue de l’érudition par une variété de moyens interconnectés (par exemple, la technologie, les licences) visant à élargir les connaissances et à réduire les obstacles à l’accès aux connaissances et à l’information&#8239;» (16 ; voir également Veletsianos et Kimmons 2012). Dans «&#8239;Fondements de l’élaboration d’une stratégie d’érudition ouverte&#8239;» (Foundations for Open Scholarship Strategy Development), Jonathan Tennant et al. (2019) la définissent comme «&#8239;le processus, la communication et la réutilisation de la recherche telle qu’elle est pratiquée dans toute discipline de recherche savante, ainsi que son inclusion et son rôle dans la société au sens large&#8239;» (sec. 3). Cette définition reflète la complexité du phénomène lui-même : comme le soulignent Tennant et al. (2019), il n’existe pas de définition, de cadre, de politique ou de déclaration unique qui rende compte du mouvement dans son ensemble : il s’agit d’un complexe de personnes, d’organisations, d’idées, de valeurs, de pratiques et de résultats. Ils décrivent constructivement l’érudition ouverte comme un «&#8239;objet frontière&#8239;», que Samuel Moore (2017) définit comme «&#8239;un concept qui a une compréhension spécifique dans une communauté de pratique locale, mais qui est suffisamment rigide pour maintenir sa définition à travers les communautés également&#8239;» (par. 5). Le fait de comprendre qu’il s’agit d’un objet frontière permet des définitions et des conceptions flexibles de ce qui constitue l’érudition ouverte, tout en conservant une compréhension commune suffisante pour que le terme ait un sens. Comme le souligne le document «&#8239;Fondements de l’élaboration d’une stratégie d’érudition ouverte (Foundations for Open Scholarship Strategy Development)&#8239;», un document stratégique fondamental rédigé par une équipe internationale de praticiens et de parties prenantes de l’Érudition ouverte, l’érudition est fondée sur l’idéal de «&#8239;faire progresser nos connaissances collectives au profit de toute l’humanité&#8239;», mais cet idéal est soumis à des tensions en raison des valeurs et des structures complexes et souvent concurrentes au sein desquelles l’érudition est réalisée, telles que la concurrence pour les emplois et les opportunités de publication (Tennant et al. 2019, sec. 1). L’érudition ouverte cherche à «&#8239;réaligner les pratiques de recherche modernes sur cet idéal&#8239;» afin que l’ouverture devienne la norme (sec. 1). Bien que le mouvement soit diversifié, ses partisans partagent la conviction que «&#8239;l’adoption accrue des pratiques d’érudition ouverte (et plus généralement des pratiques simplement ouvertes) est généralement une bonne chose&#8239;» (sec. 4). L’érudition ouverte peut être comprise comme comprenant deux catégories principales, que nous pourrions également considérer comme des axes : «&#8239;connaissances et pratiques&#8239;» et «&#8239;principes et valeurs&#8239;» (Tennant et al. 2019, sec. 4.1.3). Le long de l’axe «&#8239;connaissances et pratiques&#8239;» se trouvent les composantes de l’érudition ouverte, telles que le libre accès et les données ouvertes ; le long de l’axe «&#8239;principes et valeurs&#8239;» se trouvent des idées telles que «&#8239;la participation, l’égalité, la transparence, la justice cognitive, la collaboration, le partage, l’équité et l’inclusivité.&#8239;» Outre la structure catégorielle, Tennant et al. (2019) s’appuient sur cinq écoles de pensée que Benedikt Fecher et Sascha Friesike (2013) ont proposées pour comprendre la science ouverte : <blockquote> l’''école de l’infrastructure'' (qui s’intéresse à l’architecture technologique), l’''école publique'' (qui s’intéresse à l’accessibilité de la création de connaissances), l’''école de la mesure'' (qui s’intéresse à la mesure alternative d’impact), l’''école démocratique'' (qui s’intéresse à l’accès à la connaissance) et l’''école pragmatique'' (qui s’intéresse à la recherche collaborative). (Résumé) </blockquote> Comme on peut s’y attendre d’un mouvement aussi complexe, il existe des tensions en son sein, dont beaucoup sont liées aux différences géographiques et disciplinaires ainsi qu’aux besoins et priorités variables des groupes de ses parties prenantes, qui comprennent les chercheurs et les institutions ainsi que les organismes de financement, les éditeurs et autres groupes industriels, et les décideurs politiques (Tennant et al. 2019). Certaines des tensions les plus fortes sont liées aux licences et aux pratiques d’octroi de licences, aux différents modèles de – et aux voies vers le libre accès, à la responsabilité et au contrôle de l’infrastructure, et au «&#8239;rôle des mandats politiques dans la promotion de l’ouverture&#8239;» (sec. 4.2.3). Cela dit, Tennant et al. (2019) identifient la politique comme l’une des forces du mouvement, notant qu’«&#8239;il reste important que l’impératif et l’agenda de l’Érudition ouverte restent reconnus aux plus hauts niveaux politiques&#8239;» (sec. 6). Cependant, des problèmes se posent lorsque les politiques descendantes, telles que celles dictées par les organismes de financement internationaux et nationaux, ne sont pas accompagnées de ressources (par exemple, une infrastructure appropriée) pour assurer leur mise en œuvre réussie. Les politiques ascendantes, telles que celles élaborées par les bibliothèques institutionnelles ou même les particuliers, ont tendance à être facultatives et à ne pas être appliquées ou exécutoires (Tennant et al. 2019). La recherche ouverte est facilitée et rendue possible par les technologies numériques, mais l’ouverture n’est pas intrinsèquement numérique, et ces technologies numériques ne sont pas nécessairement ouvertes. Notant que les technologies numériques ont permis aux revues savantes d’avoir une plus grande portée grâce au libre accès, par exemple, John Maxwell (2015) affirme que la communication savante dans son ensemble doit s’éloigner des modèles fondés sur le paradigme de la production imprimée et adopter un «&#8239;modèle d’édition basé sur le web&#8239;» (4). Le passage à ce «&#8239;paradigme de réseau&#8239;» (4) nous permet de reconsidérer non seulement les formes de publication savante – en imaginant, par exemple, à quoi ressemble un article ou une monographie – mais aussi ce que signifie publier. Le paradigme de l’imprimé est fondé sur l’idée de rendre quelque chose public en l’imprimant ; maintenant que le défi n’est plus de savoir comment rendre les œuvres publiques mais comment les rendre pertinentes et trouvables, la publication dans le cadre d’un paradigme de réseau se réfère «&#8239;non pas à la production de livres mais à la production d’un public pour lequel ces livres ont un sens&#8239;» (Stadler 2010). Pour créer ce public, l’érudition (les pratiques savantes et leurs résultats) doit être sociale (Maxwell 2015). Le terme «&#8239;politique&#8239;» est déjà large, et il est appliqué ici également de manière large pour englober non seulement les déclarations officielles de politique internationale, nationale et institutionnelle, mais aussi les politiques officielles et informelles sur les questions et les sujets qui constituent l’érudition ouverte (par exemple, libre accès, données ouvertes) et les questions adjacentes (par exemple, droit d’auteur ; évaluation, titularisation et promotion). Les politiques ne suffisent toutefois pas à elles seules à susciter le changement : comme le soulignent MacCallum et al. (2020), «&#8239;la politique d’accès libre est un outil qui doit être associé à des ressources supplémentaires afin d’en accroître l’impact&#8239;», telles que la formation des bibliothécaires et des praticiens de la communication savante (9). En outre, un changement culturel est nécessaire pour que l’érudition ouverte devienne le mode d’érudition par défaut ; cela implique de dissiper les mythes persistants sur la nature du libre accès et de l’érudition ouverte, tels que le mythe selon lequel les revues en libre accès ne sont pas évaluées par les pairs et sont largement prédatrices. Il s’agira également de modifier la façon dont nous évaluons l’érudition en passant d’un modèle d’exclusivité, dans lequel la publication dans les revues les plus exclusives et basées sur l’abonnement confère la plus grande valeur érudite, à un modèle d’inclusion ouverte, ce que Kathleen Fitzpatrick (2019) appelle la «&#8239;pensée généreuse&#8239;» (voir également MacCallum et al. 2020). En décrivant la portée du domaine, les auteurs de cette analyse reconnaissent que la politique joue un rôle dans de nombreux domaines de l’écosystème savant et qu’elle n’est pas toujours nommée ou comprise comme une politique. ==Thèmes émergents, champ d’application== Bien que la politique fasse partie d’un ensemble de domaines, de disciplines et de groupes d’intervenants, notre travail se concentre sur le rôle de la politique dans l’écosystème de la communication savante. Un thème important de ce travail est le rôle vital joué par les bibliothèques, les bibliothécaires, les praticiens de la communication savante et, plus généralement, le domaine des bibliothèques et des sciences de l’information (BSI) dans le mouvement de l’érudition ouverte. Comme indiqué dans ''Faire progresser l’ouverture : le point de vue des praticiens de la communication savante'' (''Advancing Open : Views from Scholarly Communications Practitioners'') (MacCallum 2020), un rapport de l’Association des bibliothèques de recherche du Canada (CARL-ABRC), l’ouverture est une valeur fondamentale des bibliothèques et des sciences de l’information, et les professionnels de l’information ont joué et continuent de jouer un rôle déterminant dans l’avancement du libre accès et de l’érudition ouverte, à la fois en les soutenant localement, par exemple par le biais de dépôts institutionnels, et en soutenant des initiatives d’infrastructure à grande échelle. Le travail de plaidoyer effectué par les bibliothécaires et les professionnels de l’information, tel que les négociations avec les éditeurs, a également fait progresser la recherche ouverte. Un autre thème important est que, bien que l’écosystème de la communication savante soit d’envergure mondiale, il est hétérogène, avec des variations significatives entre les environnements locaux. Cela signifie, entre autres, que les solutions qui fonctionnent bien dans un contexte ne fonctionnent pas nécessairement dans un autre. MacCallum et al. (2020), par exemple, notent que bien que le Canada soit aligné en principe sur le Plan S, une initiative européenne d’accès libre, son engagement envers l’érudition libre est plus récent et il n’a pas encore développé l’infrastructure (financière, technique ou culturelle) nécessaire à la mise en œuvre d’une transformation aussi radicale. Un troisième thème connexe émerge, à savoir l’existence de tensions importantes au sein du mouvement. Il s’agit, par exemple, de savoir s’il est préférable d’adopter une approche descendante ou ascendante de l’érudition ouverte, si un changement révolutionnaire ou évolutif a le plus de chances de réussir, et quel rôle l’édition commerciale devrait jouer dans l’écosystème transformé, s’il y en a un. Un autre thème qui émerge est que l’érudition ouverte est intrinsèquement sociale et collaborative. MacCallum et al. (2020) soulignent, par exemple, que la seule façon de faire progresser l’érudition ouverte au Canada est de travailler en collaboration et de manière transparente avec tous les intervenants, y compris les chercheurs, les praticiens de la communication savante, les professionnels de l’information et les organisations nationales telles que les organismes de financement et les associations savantes. Enfin, les responsabilités qui accompagnent les données numériques et les référentiels – tous deux souvent considérés comme des aspects essentiels de la recherche ouverte – constituent un autre fil conducteur de la collection. Par exemple, les principes FAIR (Findable, Accessible, Interoperable, and Reusable – Repérable, accessible, interopérable et réutilisable) font surface, bien que Lin et al. (2020) affirment que la préservation des données à long terme nécessite des dépôts dignes de confiance, et que cette fiabilité doit également être démontrée. Pour démontrer la fiabilité, Lin et al. (2020) proposent les principes TRUST, ce qui signifie Transparency, Responsibility, User focus, Sustainability, and Technology (Transparence, Responsabilité, Orientation vers l’utilisateur, Durabilité et Technologie) (voir aussi [[Observations fondamentales : Observatoire des politiques sur les savoirs ouverts, 2017-2020/Les principes TRUST pour les dépôts numériques|Winter 2020b]]). La gestion des données est également au cœur de ces préoccupations et fait partie intégrante de la politique des trois organismes sur la gestion des données de recherche (examinée par Winter, 2020a), qui s’inspire des principes FAIR et du modèle OCAP (Ownership, Control, Access, and Possession – Propriété, contrôle, accès et possession) pour la gouvernance des données indigènes. La politique de gestion des données de recherche, qui a été introduite en 2021, comporte trois exigences fondamentales : 1) Les établissements admissibles à l’administration des fonds des trois organismes doivent créer une stratégie institutionnelle de gestion de données de recherche d’ici le 1er mars 2023 ; 2) Les propositions de subventions soumises aux organismes doivent refléter les meilleures pratiques de gestion des données de recherche et, dans le cas de certaines possibilités de financement, des plans de gestion des données doivent être soumis aux organismes subventionnaires ; 3) Les bénéficiaires de subventions doivent déposer toutes les données de recherche numériques dans un dépôt numérique, ainsi que toutes les métadonnées et tous les codes qui appuient directement les conclusions de la recherche dans toutes les publications ou préimpressions qui découlent de la recherche financée par les organismes (Gouvernement du Canada, 2021). Ces thèmes apparaissent dans les observations de l’OSPO au cours de la période 2017-2020, et dans celles qui se poursuivent jusqu’à aujourd’hui - développées plus en détail dans le recueil de Winter et al. 2023, qui recense la littérature actuelle sur les politiques d’érudition ouverte, dans ses sections consacrées aux politiques et cadres politiques fondamentaux, à l’érudition ouverte et au mouvement d’érudition ouverte (par le biais de l’accès libre, des données ouvertes, de l’éducation ouverte, de la connaissance ouverte et du code source ouvert), à la communication savante (son paysage, les bibliothèques et la communication savante ouverte, les modèles de publication et les pratiques d’abonnement, et les monographies ouvertes), à l’infrastructure (y compris l’infrastructure de recherche numérique, les modèles de financement et les infrastructures, la bibliométrie, la propriété intellectuelle et le droit d’auteur, la gestion de l’identité, l’accès à l’information et les droits d’auteur), bibliométrie, propriété intellectuelle et droit d’auteur, gestion de l’identité, données ouvertes liées et gestion des données de recherche), collaboration et engagement communautaire (via la mobilisation et la traduction des connaissances, l’engagement communautaire, l’érudition publique, le crowdsourcing et la science citoyenne) et, enfin, élaboration, mise en œuvre et analyse des politiques (à travers les pratiques d’érudition ouverte, la politique institutionnelle et sa mise en œuvre, la politique nationale et internationale et sa mise en œuvre, et la justice sociale). Outre le fait que les observations de l’OSPO constituent le fondement de ce travail d’analyse, elles servent également de base aux Volumes conservés (Curated Volumes) (Winter et al. 2024), qui reflètent les lectures essentielles dans ces domaines, et à [[b:en:Open Scholarship Press Curated Volumes: Policy/Introduction | leur aperçu analytique détaillé]] (Winter 2024). Les observations présentées dans ce volume ont été rédigées par Sarah Milligan, Kim Silk, Alyssa Arbuckle et Caroline Winter. Tanja Niemann et Lynne Siemens ont apporté leur expertise et leur leadership dans le domaine politique, tandis que Alyssa Arbuckle et Ray Siemens ont joué le rôle d’éditeurs superviseurs, tout en initiant la conception de la recherche dans l’ensemble du partenariat INKE. Les réviseurs des travaux publiés ici sont, entre autres, Janneke Adema, Clare Appavoo, Jonathan Bengtson, Lisa Goddard, Gary Hall, Janet Halliwell, Rachel Hendery, Matt Huculak, Inba Kehoe, Les Kneebone, John Maxwell, Kate Shuttleworth et Claire Warwick. Tim Sobie a dirigé le développement technique et la production. La traduction française de cette introduction et des observations de l’OSPO, dirigée par Olga Ziminova, sera bientôt disponible. ==Notes== {{reflist}} ==Ouvrages cités== *Fecher, Benedikt, and Sascha Friesike. 2013. «&#8239;Open Science: One Term, Five Schools of Thought&#8239;». In ''Opening Science: The Evolving Guide on How the Internet Is Changing Research, Collaboration and Scholarly Publishing'', edited by Sönke Bartling and Sascha Friesike, 17–47. Cham, Switzerland: Springer. https://doi.org/10.1007/978-3-319-00026-8_2 *Gouvernement du Canada, Innovation. 2021. ''Tri-Agency Research Data Management Policy''. Innovation, Sciences et Développement économique Canada. https://science.gc.ca/site/science/en/interagency-research-funding/policies-and-guidelines/research-data-management/tri-agency-research-data-management-policy. *Fitzpatrick, Kathleen. 2019. ''Generous Thinking: A Radical Approach to Saving the University''. Baltimore: Johns Hopkins University Press. *MacCallum, Lindsey, Ann Barrett, Leah Vanderjagt, Amy Buckland, et Canadian Association of Research Libraries Open Repositories Working Group’s Task Group on Community Building and Engagement. 2020. «&#8239;Advancing Open: Views from Scholarly Communications Practitioners&#8239;». https://www.carl-abrc.ca/wp-content/uploads/2020/04/ORWG_report3_Advancing_open_EN.pdf *Maxwell, John W. 2015. «&#8239;Beyond Open Access to Open Publication and Open Scholarship&#8239;». ''Scholarly and Research Communication'' 6 (3). https://doi.org/10.22230/src.2015v6n3a202 *Milligan, Sarah, Kimberly Silk, Alyssa Arbuckle, et Ray Siemens. 2019. «&#8239;The Initial Impact of the Open Scholarship Policy Observatory&#8239;». ''KULA: Knowledge Creation, Dissemination, and Preservation Studies'' 3 (février): 16. https://doi.org/10.5334/kula.43 *Moore, Samuel A. 2017. «&#8239;A Genealogy of Open Access: Negotiations between Openness and Access to Research&#8239;». ''Revue Française Des Sciences de l’information et de La Communication'' 11 (1). https://doi.org/10.4000/rfsic.3220 *Stadler, Matthew. 2010. ''What is publication?'' Présentation principale à la conférence des écrivains de la Maison Richard Hugo «&#8239;Finding Your Audience in the 21st Century&#8239;». Seattle, Washington, USA., 11 septembre. <nowiki>https://vimeo.com/14888791</nowiki>. Cité dans Maxwell, John. 2015. «&#8239;Beyond Open Access to Open Publication and Open Scholarship.&#8239;» ''Scholarly and Research Communication'' 6 (3). *Tennant, Jonathan, Jennifer Elizabeth Beamer, Jeroen Bosman, Björn Brembs, Neo Christopher Chung, Gail Clement, Tom Crick, et al. 2019. «&#8239;Foundations for Open Scholarship Strategy Development&#8239;». ''Open Scholarship Press''. https://openscholarshippress.pubpub.org/pub/camlwswq/release/1. *Veletsianos, George. 2016. ''Social Media in Academia: Networked Scholars''. New York et London: Routledge. https://doi.org/10.4324/9781315742298 *Veletsianos, George, et Royce Kimmons. 2012. «&#8239;Assumptions and Challenges of Open Scholarship&#8239;». ''The International Review of Research in Open and Distributed Learning'' 13 (4): 166–89. https://doi.org/10.19173/irrodl.v13i4.1313 *Winter, Caroline. 2020a. «&#8239;Tri-Agency Research Data Management Policy&#8239;». ''Open Scholarship Policy Observatory''. https://doi.org/10.25547/HKY0-FP69. *Winter, Caroline. 2020b. «&#8239;The TRUST Principles for Digital Repositories&#8239;». ''Open Scholarship Policy Observatory''. https://doi.org/10.25547/B0WM-TK54. *Winter, Caroline. 2024. «&#8239;Introduction—Open Scholarship Policy in Focus&#8239;». Dans ''Open Scholarship Press Curated Volumes: Policy''. https://en.wikibooks.org/wiki/Open_Scholarship_Press_Curated_Volumes:_Policy/Introduction *Winter, Caroline, Alyssa Arbuckle, Jesse Thomas Kern, Vitor Yano, Anna Honcharova, Tyler Fontenot, Graham Jensen, Alan Colín-Arce, Ray Siemens, Tanja Niemann, Lynne Simens, and the INKE and ETCL Research Groups, editors. 2023. ''Open Scholarship Press Collections: Policy'', Open Scholarship Press. https://en.wikibooks.org/wiki/Open_Scholarship_Press_Collections:_Policy *Winter, Caroline, Alyssa Arbuckle, Jesse Thomas Kern, Vitor Yano, Anna Honcharova, Tyler Fontenot, Graham Jensen, Alan Colín-Arce, Ray Siemens, Tanja Niemann, Lynne Simens, and the INKE and ETCL Research Groups, editors. 2024. ''Open Scholarship Press Curated Volumes: Policy'', Open Scholarship Press. https://en.wikibooks.org/wiki/Open_Scholarship_Press_Collections:_Policy [[Catégorie:Observations fondamentales : Observatoire des politiques sur les savoirs ouverts, 2017-2020 (livre)]] ml4sjclfj0gs4nnagj1zkyhmsl2xfj6 Fonctionnement d'un ordinateur/L'accélération matérielle de la virtualisation 0 82429 744661 744493 2025-06-13T12:57:45Z Mewtow 31375 /* La virtualisation des interruptions */ 744661 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté par le 286. Pour que le mode protégé ne soit pas relégué aux oubliettes, le 286 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> soiruaiu5fhbrzq3tvzldikmrth9nsx 744662 744661 2025-06-13T13:05:35Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744662 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté par le 286. Pour que le mode protégé ne soit pas relégué aux oubliettes, le 286 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 286 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> dltumsz6q8msomjthkx4z0o9j10erb4 744663 744662 2025-06-13T13:12:20Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744663 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté par le 286. Pour que le mode protégé ne soit pas relégué aux oubliettes, le 286 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 286 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> fz87qepa7vm9aof8qexxyhzmkotd911 744664 744663 2025-06-13T13:16:49Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744664 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté par le 286. Pour que le mode protégé ne soit pas relégué aux oubliettes, le 286 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. En mode virtuel 8086, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 286 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> qsb7nqp5ivhl321ja5n0te3aylfofqm 744665 744664 2025-06-13T13:45:15Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744665 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté par le 286. Pour que le mode protégé ne soit pas relégué aux oubliettes, le 286 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode virtuel 8086, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours, qui font que le 286 et le 8086 ne fonctionnent pas exactement pareil. Sur le 286, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le 286 en mode réel permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 286 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> opi1pe9c76d8xirgpivobsww3fu9mj4 744666 744665 2025-06-13T13:51:18Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744666 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode virtuel 8086, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> ckxwsdnm6yi8y064zviyb19361yycpn 744667 744666 2025-06-13T13:58:14Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744667 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode virtuel 8086, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode 8086 virtuel, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Elle introduisait des optimisations quant au traitement des interruptions, dont certaines étaient utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> mzbqms3asnofs2ab708710dt1jkqtby 744668 744667 2025-06-13T13:59:52Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744668 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. ===Le fonctionnement du mode virtuel 8086 de base=== Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode virtuel 8086, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode 8086 virtuel, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Elle introduisait des optimisations quant au traitement des interruptions, dont certaines étaient utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> j822vpxqzo6vucnd070fjubeesw7hb1 744669 744668 2025-06-13T14:04:04Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744669 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode virtuel et le mode réel du 8086, idem avec le mode réel du 286. Mais rien de foncièrement bloquant, la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Le fonctionnement du mode virtuel 8086 de base=== Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode virtuel 8086, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode 8086 virtuel, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Elle introduisait des optimisations quant au traitement des interruptions, dont certaines étaient utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> er4r5pk42qwuwapqotpqdxaiit1mu6p 744672 744669 2025-06-13T16:58:19Z Mewtow 31375 /* Les Virtual-8086 mode extensions */ 744672 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode virtuel et le mode réel du 8086, idem avec le mode réel du 286. Mais rien de foncièrement bloquant, la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Le fonctionnement du mode virtuel 8086 de base=== Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode virtuel 8086, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode 8086 virtuel, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier l'''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode protégé, ce bit est accesible seulement en mode noyau, toute instruction modifiant ce bit devant passer par l'OS par l'hyperviseur. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser l'''interrupt flag'', avec une copie de l'''interrupt flag'' par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> 97r9s3mwpbt5gby4qr1qaux284y5gnq 744673 744672 2025-06-13T16:59:37Z Mewtow 31375 /* Les Virtual-8086 mode extensions */ 744673 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode virtuel et le mode réel du 8086, idem avec le mode réel du 286. Mais rien de foncièrement bloquant, la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Le fonctionnement du mode virtuel 8086 de base=== Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode virtuel 8086, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode 8086 virtuel, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode protégé, ce bit est accesible seulement en mode noyau. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> tst4lh783s82d9ct5x9u1bmtb6qh29e 744674 744673 2025-06-13T17:01:53Z Mewtow 31375 /* Les Virtual-8086 mode extensions */ 744674 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode virtuel et le mode réel du 8086, idem avec le mode réel du 286. Mais rien de foncièrement bloquant, la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Le fonctionnement du mode virtuel 8086 de base=== Utiliser le mode virtuel 8086 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un hyperviseur V86. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode virtuel 8086, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Le processeur est configuré en mode 8086 virtuel, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode 8086 virtuel, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> 80ix6yskg6sdlqnjfsqp7ogzemhnn7g 744675 744674 2025-06-13T17:04:53Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744675 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', qui permet aux applications en mode réel de profiter des protections permises par le mode protégé, ainsi que d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Il s'agit en fait d'une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Les différents programmes en mode réel s'exécutent dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Le fonctionnement du mode virtuel 8086 de base=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> 92nujdborny50xghl4voxfqe94a7gtn 744679 744675 2025-06-13T17:25:09Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744679 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel, alors que l'OS s'exécute en mode protégé. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. Les différents programmes en mode réel s'exécutent dans une machine virtuelle dédiée appelée la '''VM V86'''. L'OS devait être programmé pour utiliser le mode protégé et ce n'est qu'à partir de Windows 3.0 que ce fut le cas. Le DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Et beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Le fonctionnement du mode virtuel 8086 de base=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> tup43hlw9rm44zwmwdtj8evq804xg57 744680 744679 2025-06-13T17:27:44Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744680 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. L'OS devait être programmé pour utiliser le mode protégé et ce n'est qu'à partir de Windows 3.0 que ce fut le cas. Le système d'exploitation DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Windows implémentait un hyperviseur V86 de type 2, à savoir que c'était un logiciel qui s'exécutait sur un OS sous-jacent, ici [[File:Hyperviseur.svg|centre|vignette|upright=2|Hyperviseur]] La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Le fonctionnement du mode virtuel 8086 de base=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> iilwwutjlti10mpai3yajxcu58a573m 744681 744680 2025-06-13T17:28:43Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744681 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===La virtualisation du DOS=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. L'OS devait être programmé pour utiliser le mode protégé et ce n'est qu'à partir de Windows 3.0 que ce fut le cas. Le système d'exploitation DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Windows implémentait un hyperviseur V86 de type 2, à savoir que c'était un logiciel qui s'exécutait sur un OS sous-jacent, ici [[File:Hyperviseur.svg|centre|vignette|upright=2|Hyperviseur]] ===Le fonctionnement du mode virtuel 8086 de base=== En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> 7iz476i97kknre0sv43mgcrr1swula8 744682 744681 2025-06-13T17:29:00Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ 744682 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. ===La virtualisation du DOS=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. Le système d'exploitation, s'il existe, est soit pris en charge dans l’hyperviseur, soit exécuté en mode réel. Dans le cas du DOS, il était exécuté en mode réel, vu que c'était un OS en mode réel uniquement. L'OS devait être programmé pour utiliser le mode protégé et ce n'est qu'à partir de Windows 3.0 que ce fut le cas. Le système d'exploitation DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Windows implémentait un hyperviseur V86 de type 2, à savoir que c'était un logiciel qui s'exécutait sur un OS sous-jacent, ici [[File:Hyperviseur.svg|centre|vignette|upright=2|Hyperviseur]] ===Le fonctionnement du mode virtuel 8086 de base=== En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> rruz42cnwf9mjvobip0w3gdcykau639 744683 744682 2025-06-13T17:30:16Z Mewtow 31375 /* La virtualisation du DOS */ 744683 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. ===La virtualisation du DOS=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. L'hyperviseur est forcément exécuté ne mode protégé. Le système d'exploitation DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Windows implémentait un hyperviseur V86 de type 2, à savoir que c'était un logiciel qui s'exécutait sur un OS sous-jacent, ici [[File:Hyperviseur.svg|centre|vignette|upright=2|Hyperviseur]] ===Le fonctionnement du mode virtuel 8086 de base=== En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> 8597yxund2j1e9x3fpmj6yuh6gxsyrg 744684 744683 2025-06-13T17:31:05Z Mewtow 31375 /* La virtualisation du DOS */ 744684 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. ===La virtualisation du DOS et la mémoire étendue=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. L'hyperviseur est forcément exécuté ne mode protégé. Le système d'exploitation DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Windows implémentait un hyperviseur V86 de type 2, à savoir que c'était un logiciel qui s'exécutait sur un OS sous-jacent, ici [[File:Hyperviseur.svg|centre|vignette|upright=2|Hyperviseur]] ===Le fonctionnement du mode virtuel 8086 de base=== En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> ntjx72m47et4msj7hda5as35hl1nsvo 744687 744684 2025-06-13T17:44:16Z Mewtow 31375 /* La virtualisation du DOS et la mémoire étendue */ 744687 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. ===La virtualisation du DOS et la mémoire étendue=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. L'hyperviseur est forcément exécuté ne mode protégé. Le système d'exploitation DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Windows implémentait un hyperviseur V86 de type 2, à savoir que c'était un logiciel qui s'exécutait sur un OS sous-jacent, ici [[File:Hyperviseur.svg|centre|vignette|upright=2|Hyperviseur]] Les applications DOS dans une VM V86 ne peuvent pas adresser plus d'un mébioctet de mémoire. L'ordinateur peut cependant avoir plus de mémoire RAM, notamment pour gérer l'hyperviseur V86. Diverses techniques permettaient aux applications DOS d'utiliser la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, qui lui-même communiquait avec l'hyperviseur V86. L'intermédiaire est appelé le ''Extended Memory Manager'' (EMM), et il est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les applications DOS ne pouvaient pas adresser la mémoire étendue, mais pouvaient échanger des données avec l'EMM. Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. L'intermédiaire ''Extended Memory Manager'' s'occupe d’échanger des données entre mémoire conventionnelle et mémoire étendue. Pour cela, il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre la mémoire étendue et l'''expanded memory'' qu'on a vu dans le chapitre sur l'espace d'adressage. Pour rappel, l'''expanded memory'' est un système de commutation de banque, qui autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute. Elle fonctionne sans mode protégé, sans virtualisation, sans mode V86. La mémoire étendue ne gère pas de commutation de banque et demande que la RAM en plus soit installée dans l'ordinateur, pas sur une carte d'extension. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques cela. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. L'hyperviseur V86 pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le fonctionnement du mode virtuel 8086 de base=== En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> ow5myuimrktxe61qak9j3gh8ra0fqjg 744688 744687 2025-06-13T17:44:45Z Mewtow 31375 /* La virtualisation du DOS et la mémoire étendue */ 744688 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. ===La virtualisation du DOS et la mémoire étendue=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. L'hyperviseur est forcément exécuté ne mode protégé. Le système d'exploitation DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Windows implémentait un hyperviseur V86 de type 2, à savoir que c'était un logiciel qui s'exécutait sur un OS sous-jacent, ici [[File:Hyperviseur.svg|centre|vignette|upright=2|Hyperviseur]] Les applications DOS dans une VM V86 ne peuvent pas adresser plus d'un mébioctet de mémoire. L'ordinateur peut cependant avoir plus de mémoire RAM, notamment pour gérer l'hyperviseur V86. Diverses techniques permettaient aux applications DOS d'utiliser la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, qui lui-même communiquait avec l'hyperviseur V86. L'intermédiaire est appelé le ''Extended Memory Manager'' (EMM), et il est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les applications DOS ne pouvaient pas adresser la mémoire étendue, mais pouvaient échanger des données avec l'EMM. Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. L'intermédiaire ''Extended Memory Manager'' s'occupe d’échanger des données entre mémoire conventionnelle et mémoire étendue. Pour cela, il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory''. Pour rappel, l'''expanded memory'' est un système de commutation de banque, qui autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute. Elle fonctionne sans mode protégé, sans virtualisation, sans mode V86. La mémoire étendue ne gère pas de commutation de banque et demande que la RAM en plus soit installée dans l'ordinateur, pas sur une carte d'extension. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques cela. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. L'hyperviseur V86 pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le fonctionnement du mode virtuel 8086 de base=== En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> kv32gegqgjzsobx80l76fbz6vvfydfv 744696 744688 2025-06-13T18:03:44Z Mewtow 31375 /* Les Virtual-8086 mode extensions */ 744696 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est est lié à ce mode. ===La virtualisation du DOS et la mémoire étendue=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accesible en mode protégé, celui-ci permettant d'adresser plus de RAM. L'hyperviseur est forcément exécuté ne mode protégé. Le système d'exploitation DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Windows implémentait un hyperviseur V86 de type 2, à savoir que c'était un logiciel qui s'exécutait sur un OS sous-jacent, ici [[File:Hyperviseur.svg|centre|vignette|upright=2|Hyperviseur]] Les applications DOS dans une VM V86 ne peuvent pas adresser plus d'un mébioctet de mémoire. L'ordinateur peut cependant avoir plus de mémoire RAM, notamment pour gérer l'hyperviseur V86. Diverses techniques permettaient aux applications DOS d'utiliser la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, qui lui-même communiquait avec l'hyperviseur V86. L'intermédiaire est appelé le ''Extended Memory Manager'' (EMM), et il est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les applications DOS ne pouvaient pas adresser la mémoire étendue, mais pouvaient échanger des données avec l'EMM. Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. L'intermédiaire ''Extended Memory Manager'' s'occupe d’échanger des données entre mémoire conventionnelle et mémoire étendue. Pour cela, il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory''. Pour rappel, l'''expanded memory'' est un système de commutation de banque, qui autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute. Elle fonctionne sans mode protégé, sans virtualisation, sans mode V86. La mémoire étendue ne gère pas de commutation de banque et demande que la RAM en plus soit installée dans l'ordinateur, pas sur une carte d'extension. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques cela. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. L'hyperviseur V86 pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le fonctionnement du mode virtuel 8086 de base=== En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Et bien les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentré sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86, et surtout sans déclencher une exception matérielle gérée par l'hyperviseur. Bien qu'elles aient été introduite sur les processeurs Pentium, et même sur certains processeurs 486 sortis après le Pentium, elle n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> rnv2s1432egiyr19vw891nb4y76dses 744719 744696 2025-06-13T19:18:11Z Mewtow 31375 /* Annexe : le mode virtuel 8086 des premiers CPU Intel */ orthotypo 744719 wikitext text/x-wiki La virtualisation est l'ensemble des techniques qui permettent de faire tourner plusieurs systèmes d'exploitation en même temps. Le terme est polysémique, mais c'est la définition que nous allons utiliser pour ce qui nous intéresse. La virtualisation demande d'utiliser un logiciel dit '''hyperviseur''', qui permet de faire tourner plusieurs OS en même temps. Les hyperviseurs sont en quelque sorte situés sous le système d'exploitation. On peut les voir comme une sorte de sous-système d'exploitation, de système d'exploitation pour les systèmes d'exploitation. A ce propos, les OS virtualisés sont appelés des ''OS invités'', alors que l'hyperviseur est parfois appelé l'''OS hôte''. [[File:Diagramme ArchiHyperviseur.png|centre|vignette|upright=2|Différence entre système d'exploitation et hyperviseur.]] Les processeurs modernes intègrent des techniques pour accélérer la virtualisation. Les techniques en question sont assez variées, allant d'un niveau de privilège en plus des modes noyau/utilisateur à des modifications de la mémoire virtuelle, en passant à des modifications liées aux interruptions matérielles. Mais pour comprendre tout cela, il va falloir faire quelques explications sur la virtualisation elle-même. ==La virtualisation : généralités== Pour faire tourner plusieurs OS en même temps, l'hyperviseur recourt à de nombreux stratagèmes. Il doit partager le processeur, la RAM et les entrées-sorties entre plusieurs OS. Le partage de la RAM demande concrètement des modifications assez légères de la mémoire virtuelle, qu'on verra en temps voulu. Le partage du processeur est assez simple : les OS s'exécutent à tour de rôle sur le processeur, chacun pendant un temps défini, fixe. Une fois leur temps d'exécution passé, ils laissent la main à l'OS suivant. C'est l’hyperviseur qui s'occupe de tout cela, grâce à une interruption commandée à un ''timer''. Ce système de partage est une forme de '''multiplexage'''. A ce propos, il s'agit de la même solution que les OS utilisent pour faire tourner plusieurs programmes en même temps sur un processeur/cœur unique. La gestion des entrées-sorties demande d'utiliser des techniques d''''émulation''', plus complexes à expliquer. Un hyperviseur peut parfaitement simuler du matériel qui n'est pas installé sur l'ordinateur. Par exemple, il peut faire croire à un OS qu'une carte réseau obsolète, datant d'il y a 20 ans, est installée sur l'ordinateur, alors que ce n'est pas le cas. Les commandes envoyées par l'OS à cette carte réseau fictive sont en réalité traitées par une vraie carte réseau par l’hyperviseur. Pour cela, l’hyperviseur intercepte les commandes envoyées aux entrées-sorties, et les traduit en commandes compatibles avec les entrées-sorties réellement installées sur l'ordinateur. ===Les machines virtuelles=== L'exemple avec la carte réseau est un cas particulier, l'hyperviseur faisant beaucoup de choses dans le genre. L'hyperviseur peut faire croire à l'ordinateur qu'il a plus ou moins de RAM que ce qui est réellement installé, par exemple. L'hyperviseur implémente ce qu'on appelle des '''machines virtuelles'''. Il s'agit d'une sorte de faux matériel, simulé par un logiciel. Un logiciel qui s’exécute dans une machine virtuelle aura l'impression de s’exécuter sur un matériel et/ou un O.S différent du matériel sur lequel il est en train de s’exécuter. : Dans ce qui suit, nous parlerons de V.M (virtual machine), pour parler des machines virtuelles. [[File:VM-monitor-french.png|centre|vignette|upright=2|Machines virtuelles avec la virtualisation.]] Avec la virtualisation, plusieurs machines virtuelles sont gérées par l'hyperviseur, chacune étant réservée à un système d'exploitation. D'ailleurs, hyperviseurs sont parfois appelés des ''Virtual Machine Manager''. Nous utiliserons d'ailleurs l'abréviation VMM dans les schémas qui suivent. Il existe deux types d'hyperviseurs, qui sont nommés type 1 et type 2. Le premier type s'exécute directement sur le matériel, alors que le second est un logiciel qui s’exécute sur un OS normal. Pour ce qui nous concerne, la distinction n'est pas très importante. [[File:Ansatz der Systemvirtualisierung zur Schaffung virtueller Betriebsumgebungen.png|centre|vignette|upright=2.5|Comparaison des différentes techniques de virtualisation : sans virtualisation à gauche, virtualisation de type 1 au milieu, de type 2 à droite.]] La virtualisation est une des utilisations possibles, mais il y en a d'autres. La plus intéressante est celle des émulateurs. Ces derniers sont des logiciels qui permettent de simuler le fonctionnement d'anciens ordinateurs ou consoles de jeux. L'émulateur crée une machine virtuelle qui est réservée à un programme, à savoir le jeu à émuler. Il y a une différence de taille entre un émulateur et un hyperviseur. L'émulation émule une machine virtuelle totalement différente, alors que la virtualisation doit émuler les entrées-sorties mais pas le processeur. Avec un hyperviseur, le système d'exploitation s'exécute sur le processeur lui-même. Le code de l'OS est compatible avec le processeur de la machine, dans le sens où il est compilé pour le jeu d'instruction du processeur de la machine réelle. Les instructions de l'OS s'exécutent directement. Par contre, un émulateur exécute un jeu qui est programmé pour une machine dont le processeur est totalement différent. Le jeu d'instruction de la machine virtuelle et celui du vrai processeur n'est pas le même. L'émulation implique donc de traduire les instructions à exécuter dans la V.M par des instructions exécutables par le processeur. Ce n'est pas le cas avec la virtualisation, le jeu d'instruction étant le même. ===La méthode ''trap and emulate'' basique=== Pour être considéré comme un logiciel de virtualisation, un logiciel doit remplir trois critères : * L'équivalence : l'O.S virtualisé et les applications qui s’exécutent doivent se comporter comme s'ils étaient exécutés sur le matériel de base, sans virtualisation. * Le contrôle des ressources : tout accès au matériel par l'O.S virtualisé doit être intercepté par la machine virtuelle et intégralement pris en charge par l'hyperviseur. * L'efficacité : La grande partie des instructions machines doit s’exécuter directement sur le processeur, afin de garder des performances correctes. Ce critère n'est pas respecté par les émulateurs matériels, qui doivent simuler le jeu d'instruction du processeur émulé. Remplir ces trois critères est possible sous certaines conditions, établies par les théorèmes de Popek et Goldberg. Ces théorèmes se basent sur des hypothèses précises. De fait, la portée de ces théorèmes est limitée, notamment pour le critère de performance. Ils partent notamment du principe que l'ordinateur utilise la segmentation pour la mémoire virtuelle, et non la pagination. Il part aussi du principe que les interruptions ont un cout assez faible, qu'elles sont assez rares. Mais laissons ces détails de côté, le cœur de ces théorèmes repose sur une hypothèse simple : la présence de différents types d'instructions machines. Pour rappel, il faut distinguer les instructions privilégiées de celles qui ne le sont pas. Les instructions privilégiées ne peuvent s'exécuter que en mode noyau, les programmes en mode utilisateur ne peuvent pas les exécuter. Parmi les instructions privilégiées on peut distinguer un sous-groupe appelé les '''instructions systèmes'''. Le premier type regroupe les '''instructions d'accès aux entrées-sorties''', aussi appelées instructions sensibles à la configuration. Le second type est celui des '''instructions de configuration du processeur''', qui agissent sur les registres de contrôle du processeur, aussi appelées instructions sensibles au comportement. Elles servent notamment à gérer la mémoire virtuelle, mais pas que. La théorie de Popek et Goldberg dit qu'il est possible de virtualiser un O.S à une condition : que les instructions systèmes soient toutes des instructions privilégiées, c’est-à-dire exécutables seulement en mode noyau. Virtualiser un O.S demande simplement de le démarrer en mode utilisateur. Quand l'O.S fait un accès au matériel, il le fait via une instruction privilégiée. Vu que l'OS est en mode utilisateur, cela déclenche une exception matérielle, qui émule l'instruction privilégiée. L'hyperviseur n'est ni plus ni moins qu'un ensemble de routines d'interruptions, chaque routine simulant le fonctionnement du matériel émulé. Par exemple, un accès au disque dur sera émulé par une routine d'interruption, qui utilisera les appels systèmes fournit par l'OS pour accéder au disque dur réellement présent dans l'ordinateur. Cette méthode est souvent appelée la méthode ''trap and emulate''. [[File:Virtualisation avec la méthode trap-and-emulate.png|centre|vignette|upright=2.0|Virtualisation avec la méthode trap-and-emulate]] La méthode ''trap and emulate'' ne fonctionne que si certaines contraintes sont respectées. Un premier problème est que beaucoup de jeux d'instructions anciens ne respectent pas la règle "les instructions systèmes sont toutes privilégiées". Par exemple, ce n'est pas le cas sur les processeurs x86 32 bits. Sur ces CPU, les instructions qui manipulent les drapeaux d'interruption ne sont pas toutes des instructions privilégiées, idem pour les instructions qui manipulent les registres de segmentation, celles liées aux ''call gates'', etc. A cause de cela, il est impossible d'utiliser la méthode du ''trap and emulate''. La seule solution qui ne requiert pas de techniques matérielles est de traduire à la volée les instructions systèmes problématiques en appels systèmes équivalents, grâce à des techniques de '''réécriture de code'''. Enfin, certaines instructions dites '''sensibles au contexte''' ont un comportement différent entre le mode noyau et le mode utilisateur. En présence de telles instructions, la méthode ''trap and emulate'' ne fonctionne tout simplement pas. Grâce à ces instructions, le système d’exploitation ou un programme applicatif peut savoir s'il s'exécute en mode utilisateur ou noyau, ou hyperviseur, ou autre. La virtualisation impose l'usage de la mémoire virtuelle, sans quoi plusieurs OS ne peuvent pas se partager la même mémoire physique. De plus, il ne faut pas que la mémoire physique, non-virtuelle, puisse être adressée directement. Et cette contrainte est violée, par exemple sur les architectures MIPS qui exposent des portions de la mémoire physique dans certaines zones fixées à l'avance de la mémoire virtuelle. L'OS est compilé pour utiliser ces zones de mémoire pour accéder aux entrées-sorties mappées en mémoire, entre autres. En théorie, on peut passer outre le problème en marquant ces zones de mémoire comme inaccessibles, toute lecture/écriture à ces adresses déclenche alors une exception traitée par l'hyperviseur. Mais le cout en performance est alors trop important. Quelques hyperviseurs ont été conçus pour les architectures MIPS, dont le projet de recherche DISCO, mais ils ne fonctionnaient qu'avec des systèmes d'exploitation recompilés, de manière à passer outre ce problème. Les OS étaient recompilés afin de ne pas utiliser les zones mémoire problématiques. De plus, les OS étaient modifiés pour améliorer les performances en virtualisation. Les OS disposaient notamment d'appels systèmes spéciaux, appelés des ''hypercalls'', qui exécutaient des routines de l'hyperviseur directement. Les appels systèmes faisant appel à des instructions systèmes étaient ainsi remplacés par des appels système appelant directement l'hyperviseur. Le fait de modifier l'OS pour qu'il communique avec un hyperviseur, dont il a connaissance de l'existence, s'appelle la '''para-virtualisation'''. [[File:Virtualization - Para vs Full.png|centre|vignette|upright=2.5|Virtualization - Para vs Full]] ==La virtualisation du processeur== La virtualisation demande de partager le matériel entre plusieurs machines virtuelles. Précisément, il faut partager : le processeur, la mémoire RAM, les entrées-sorties. Les trois sont gérés différemment. Par exemple, la virtualisation des entrées-sorties est gérée par l’hyperviseur, parfois aidé par le ''chipset'' de la carte mère. Virtualiser des entrées-sorties demande d'émuler du matériel inexistant, mais aussi de dupliquer des entrées-sorties de manière à ce le matériel existe dans chaque VM. Partager la mémoire RAM entre plusieurs VM est assez simple avec la mémoire virtuelle, bien que cela demande quelques adaptations. Maintenant, voyons ce qu'il en est pour le processeur. ===Le niveau de privilège hyperviseur=== Sur certains CPU modernes, il existe un niveau de privilège appelé le '''niveau de privilège hyperviseur''' qui est utilisé pour les techniques de virtualisation. Le niveau de privilège hyperviseur est réservé à l’hyperviseur et il a des droits d'accès spécifiques. Il n'est cependant pas toujours activé. Par exemple, si aucun hyperviseur n'est installé sur la machine, le processeur dispose seulement des niveaux de privilège noyau et utilisateur, le mode noyau n'ayant alors aucune limitation précise. Mais quand le niveau de privilège hyperviseur est activé, une partie des manipulations est bloquée en mode noyau et n'est possible qu'en mode hyperviseur. Le fonctionnement se base sur la différence entre instruction privilégiée et instruction système. Les instructions privilégiées peuvent s'exécuter en niveau noyau, alors que les instructions systèmes ne peuvent s'exécuter qu'en niveau hyperviseur. L'idée est que quand le noyau d'un OS exécute une instruction système, une exception matérielle est levée. L'exception bascule en mode hyperviseur et laisse la main à une routine de l'hyperviseur. L'hyperviseur fait alors des manipulations précise pour que l'instruction système donne le même résultat que si elle avait été exécutée par l'ordinateur simulé par la machine virtuelle. [[File:Virtualisation avec un mode hyperviseur.png|centre|vignette|upright=2|Virtualisation avec un mode hyperviseur.]] Il est ainsi possible d'émuler des entrées-sorties avec un cout en performance assez léger. Précisément, ce mode hyperviseur améliore les performances de la méthode du ''trap-and-emulate''. La méthode ''trap-and-emulate'' basique exécute une exception matérielle pour toute instruction privilégiée, qu'elle soit une instruction système ou non. Mais avec le niveau de privilège hyperviseur, seules les instructions systèmes déclenchent une exception, pas les instructions privilégiées non-système. Les performances sont donc un peu meilleures, pour un résultat identique. Après tout, les entrées-sorties et la configuration du processeur suffisent à émuler une machine virtuelle, les autres instructions noyau ne le sont pas. Sur les processeurs ARM, il est possible de configurer quelles instructions sont détournées vers le mode hyperviseur et celles qui restent en mode noyau. En clair, on peut configurer quelles sont les instructions systèmes et celles qui sont simplement privilégiées. Et il en est de même pour les interruptions : on peut configurer si elles exécutent la routine de l'OS normal en mode noyau, ou si elles déclenchent une exception matérielle qui redirige vers une routine de l’hyperviseur. En l'absence d'hyperviseur, toutes les interruptions redirigent vers la routine de l'OS normale, vers le mode noyau. Il faut noter que le mode hyperviseur n'est compatible qu'avec les hyperviseurs de type 1, à savoir ceux qui s'exécutent directement sur le matériel. Par contre, elle n'est pas compatible avec les hyperviseurs de type 2, qui sont des logiciels qui s'exécutent comme tout autre logiciel, au-dessus d'un système d'exploitation sous-jacent. ===L'Intel VT-X et l'AMD-V=== Les processeurs ARM de version v8 et plus incorporent un mode hyperviseur, mais pas les processeurs x86. À la place, ils incorporent des technologies alternatives nommées Intel VT-X ou l'AMD-V. Les deux ajoutent de nouvelles instructions pour gérer l'entrée et la sortie d'un mode réservé à l’hyperviseur. Mais ce mode réservé à l'hyperviseur n'est pas un niveau de privilège comme l'est le mode hyperviseur. L'Intel VT-X et l'AMD-V dupliquent le processeur en deux modes de fonctionnement : un mode racine pour l'hyperviseur, un mode non-racine pour l'OS et les applications. Fait important : les niveaux de privilège sont dupliqués eux aussi ! Par exemple, il y a un mode noyau racine et un mode noyau non-racine, idem pour le mode utilisateur, idem pour le mode système (pour le BIOS/UEFI). De même, les modes réel, protégé, v8086 ou autres, sont eux aussi dupliqués en un exemplaire racine et un exemplaire non-racine. L'avantage est que les systèmes d'exploitation virtualisés s'exécutent bel et bien en mode noyau natif, l'hyperviseur a à sa disposition un mode noyau séparé. D'ailleurs, les deux modes ont des registres d'interruption différents. Le mode racine et le mode non-racine ont chacun leurs espaces d'adressage séparés de 64 bit, avec leur propre table des pages. Et cela demande des adaptations au niveau de la TLB. La transition entre mode racine et non-racine se fait lorsque le processeur exécute une instruction système ou lors de certaines interruptions. Au minimum, toute exécution d'une instruction système fait commuter le processeur mode racine et lance l'exécution des routines de l’hyperviseur adéquates. Les interruptions matérielles et exceptions font aussi passer le CPU en mode racine, afin que l’hyperviseur puisse gérer le matériel. De plus, afin de gérer le partage de la mémoire entre OS, certains défauts de page déclenchent l'entrée en mode racine. Les ''hypercalls'' de la para-virtualisation sont supportés grâce à aux instructions ''vmcall'' et ''vmresume'' qui permettent respectivement d'appeler une routine de l’hyperviseur ou d'en sortir. La transition demande de sauvegarder/restaurer les registres du processeur, comme avec les interruptions. Mais cette sauvegarde est réalisée automatiquement par le processeur, elle n'est pas faite par les routines de l'hyperviseur. L’implémentation de cette sauvegarde/restauration se fait surtout via le microcode du processeur, car elle demande beaucoup d'étapes. Elle est en conséquence très lente. Le processeur sauvegarde l'état de chaque machine virtuelle en mémoire RAM, dans une structure de données appelée la ''Virtual Machine Control Structure'' (VMCS). Elle mémorise surtout les registres du processeur à l'instant t. Lorsque le processeur démarre l'exécution d'une VM sur le processeur, cette VMCS est recopiée dans les registres pour rétablir la VM à l'endroit où elle s'était arrêtée. Lorsque la VM est interrompue et doit laisser sa place à l'hyperviseur, les registres et l'état du processeur sont sauvegardés dans la VMCS adéquate. ==La virtualisation de la mémoire : mémoire virtuelle et MMU== Avec la virtualisation, les différentes machines virtuelles, les différents OS doivent se partager la mémoire physique, en plus d'être isolés les uns des autres. L'idée est d'utiliser la mémoire virtuelle pour cela. L'espace d'adressage physique vu par chaque OS est en réalité un espace d'adressage fictif, qui ne correspond pas à la mémoire physique. Les adresses physiques manipulées par l'OS sont en réalité des adresses intermédiaires entre les adresses physiques liées à la RAM, et les adresses virtuelles vues par les processus. Pour les distinguer, nous parlerons d'adresses physiques de l'hôte pour parler des adresses de la RAM, et des adresses physiques invitées pour parler des adresses manipulées par les OS virtualisés. Sans accélération matérielle, la traduction des adresses physiques invitées en adresses hôte est réalisée par une seconde table des pages, appelée la ''shadow page table'', ce qui donnerait '''table des pages cachée''' en français. La table des pages cachée est prise en charge par l'hyperviseur. Toute modification de la table des pages cachée est réalisée par l'hyperviseur, les OS ne savent même pas qu'elle existe. [[File:Shadowpagetables.png|centre|vignette|upright=2|Table des pages cachée.]] ===La MMU et la virtualisation : les tables des pages emboitées=== Une autre solution demande un support matériel des tables des pages emboitées, à savoir qu'il y a un arbre de table des pages, chaque consultation de la première table des pages renvoie vers une seconde, qui renvoie vers une troisième, et ainsi de suite jusqu'à tomber sur la table des pages finale qui renvoie l'adresse physique réelle. L'idée est l'utiliser une seule table des pages, mais d'ajouter un ou deux niveaux supplémentaires. Pour l'exemple, prenons le cas des processeurs x86. Sans virtualisation, l'OS utilise une table des pages de 4 niveaux. Avec, la table des pages a un niveau en plus, qui sont ajoutés à la fin de la dernière table des pages normale. Les niveaux ajoutés s'occupent de la traduction des adresses physiques invitées en adresses physiques hôte. On parle alors de '''table des pages étendues''' pour désigner ce nouveau format de table des pages conçu pour la virtualisation. Il faut que le processeur soit modifié de manière à parcourir automatiquement les niveaux ajoutés, ce qui demande quelques modifications de la TLB et du ''page table walker''. Les modifications en question ne font que modifier le format normal de la table des pages, et sont donc assez triviales. Elles ont été implémentées sur les processeurs AMD et Intel. AMD a introduit les tables des pages étendues sur ses processeurs Opteron, destinés aux serveurs, avec sa technologie ''Rapid Virtualization Indexing''. Intel, quant à lui, a introduit la technologie sur les processeurs i3, i5 et i7, sous le nom ''Extended Page Tables''. Les processeurs ARM ne sont pas en reste avec la technologie ''Stage-2 page-tables'', qui est utilisée en mode hyperviseur. ===La virtualisation de l'IO-MMU=== Si la MMU du processeur est modifiée pour gérer des tables des pages étendues, il en est de même pour les IO-MMU des périphériques et contrôleurs DMA.Les périphériques doivent idéalement intégrer une IO-MMU pour faciliter la virtualisation. La raison est globalement la même que pour le partage de la mémoire. Les pilotes de périphériques utilisent des adresses qui sont des adresses physiques sans virtualisation, mais qui deviennent des adresses virtuelles avec. Quand le pilote de périphérique configure un contrôleur DMA, pour transférer des données de la RAM vers un périphérique, il utilisera des adresses virtuelles qu'il croit physique pour adresser les données en RAM. Pour éviter tout problème, le contrôleur DMA doit traduire les adresses qu'il reçoit en adresses physiques. Pour cela, il y a besoin d'une IO-MMU intégrée au contrôleur DMA, qui est configurée par l'hyperviseur. Toute IO-MMU a sa propre table des pages et l'hyperviseur configure les table des pages pour chaque périphérique. Ainsi, le pilote de périphérique manipule des adresses virtuelles, qui sont traduites en adresses physiques directement par le matériel lui-même, sans intervention logicielle. Pour gérer la virtualisation, on fait la même chose qu'avec une table des pages emboitée habituelle : on l'étend en ajoutant des niveaux. L'IO-MMU peut fonctionner dans un mode normal, sans virtualisation, où les adresses virtuelles reçues du ''driver'' sont traduite avec une table des pages normale, non-emboitée. Mais elle a aussi un mode virtualisation qui utilise des tables de pages étendues. ==La virtualisation des entrées-sorties== Virtualiser les entrées-sorties est simple sur le principe. Un OS communique avec le matériel soit via des ports IO, soit avec des entrées-sorties mappées en mémoire. Le périphérique répond avec des interruptions ou via des transferts DMA. Virtualiser les périphériques demande alors d'émuler les ports IO, les entrées-sorties mappées en mémoire, le DMA et les interruptions. ===La virtualisation logicielle des interruptions=== Émuler les ports IO est assez simple, vu que l'OS lit ou écrit dedans grâce à des instructions IO spécialisées. Vu que ce sont des instructions système, la méthode ''trap and emulate'' suffit. Pour les entrées-sorties mappées en mémoire, l'hyperviseur a juste à marquer les adresses mémoires concernées comme étant réservées/non-allouées/autre. Tout accès à ces adresses lèvera une exception matérielle d'accès mémoire interdit, que l’hyperviseur intercepte et gère via ''trap and emulate''. L'émulation du DMA est triviale, vu que l'hyperviseur a accès direct à celui-ci, sans compter que l'usage d'une IO-MMU résout beaucoup de problèmes. La gestion des interruptions matérielles, les fameuses IRQ, est quant à elle plus complexe. Les interruptions matérielles ne sont pas à prendre en compte pour toutes les machines virtuelles. Par exemple, si une machine virtuelle n'a pas de carte graphique, pas besoin qu'elle prenne en compte les interruptions provenant de la carte graphique. La gestion des interruptions matérielles n'est pas la même si l'ordinateur grée des cartes virtuelles ou s'il se débrouille avec une carte physique unique. Lors d'une interruption matérielle, le processeur exécute la routine adéquate de l'hyperviseur. Celle-ci enregistre qu'il y a eu une IRQ et fait quelques traitements préliminaires. Ensuite, elle laisse la main au système d'exploitation concerné, qui exécute alors sa routine d'interruption. Une fois la routine de l'OS terminée, l'OS dit au contrôleur d'interruption qu'il a terminé son travail. Mais cela demande d'interagir avec le contrôleur d'interruption, ce qui déclenche une exception qui appelle l'hyperviseur. L'hyperviseur signale au contrôleur d'interruption que l'interruption matérielle a été traitée. Il rend alors définitivement la main au système d'exploitation. Le processus complet demande donc plusieurs changements entre mode hyperviseur et OS, ce qui est assez couteux en performances. Vu que le matériel simulé varie d'une machine virtuelle à l'autre, chaque machine virtuelle a son propre vecteur d'interruption. Par exemple, si une machine virtuelle n'a pas de carte graphique son vecteur d'interruption ne pointera pas vers les routines d'interruption d'un quelconque GPU. L'hyperviseur gère les différents vecteurs d'interruption de chaque VM et traduit les interruptions reçues en interruptions destinées aux VM/OS. Si la méthode ''trap and emulate'' fonctionne, ses performances ne sont cependant pas forcément au rendez-vous. Tous les matériels ne se prêtent pas tous bien à la virtualisation, surtout les périphériques anciens. Pour éliminer une partie de ces problèmes, il existe différentes techniques, accélérées en matériel ou non. Elles permettent aux machines virtuelles de communiquer directement avec les périphériques, sans passer par l'hyperviseur. ===La virtualisation des périphériques avec l'affectation directe=== Virtualiser les entrées-sorties avec de bonnes performances est plus complexe. En pratique, cela demande une intervention du matériel. Le ''chipset'' de la carte mère, les différents contrôleurs d'interruption et bien d'autres circuits doivent être modifiés. Diverses techniques permettent de faciliter le partage des entrées-sorties entre machines virtuelles. La première est l''''affectation directe''', qui alloue un périphérique à une machine virtuelle et pas aux autres. Par exemple, il est possible d'assigner la carte graphique à une machine virtuelle tournant sur Windows, mais les autres machines virtuelles ne verront même pas la carte graphique. Même l'hyperviseur n'a pas accès directement à ce matériel. L'affectation directe est très utile sur les serveurs, qui disposent souvent de plusieurs cartes réseaux et peuvent en assigner une à chaque machine virtuelle. Mais dans la plupart des cas, elle ne marche pas. De plus, sur les périphériques sans IO-MMU, elle ouvre la porte à des attaques DMA, où une machine virtuelle accède à la mémoire physique de la machine en configurant le contrôleur DMA de son périphérique assigné. L'affectation directe est certes limitée, mais elle se marie bien avec certaines de virtualisation matérielles, intégrées dans de nombreux périphériques. Il existe des périphériques qui sont capables de se virtualiser tout seuls, à savoir qu'ils peuvent se dédoubler en plusieurs '''périphériques virtuels'''. Par exemple, prenons une carte réseau avec cette propriété. Il n'y a qu'une seule carte réseau dans l'ordinateur, mais elle peut donner l'illusion qu'il y en a 8-16 d'installés dans l'ordinateur. Il faut alors faire la différence entre la carte réseau physique et les 8-16 cartes réseau virtuelles. L'idée est d'utiliser l'affectation directe, chaque machine virtuelle/OS ayant une carte réseau virtuelle d'affectée, avec affectation directe. [[File:Virtualisation matérielle des périphériques.png|centre|vignette|upright=2|Virtualisation matérielle des périphériques]] Pour les périphériques PCI-Express, le fait de se dupliquer en plusieurs périphériques virtuels est permis par la technologie '''''Single-root input/output virtualization''''', abrévié en SRIOV. Elle est beaucoup, utilisée sur les cartes réseaux, pour plusieurs raisons. Déjà, ce sont des périphériques beaucoup utilisés sur les serveurs, qui utilisent beaucoup la virtualisation. Dupliquer des cartes réseaux et utiliser l'affectation directe rend la configuration des serveurs bien plus simple. De plus, la plupart des cartes réseaux sont sous-utilisées, même par les serveurs. Une carte réseau est souvent utilisée à environ 10% de ses capacités par une VM unique, ce qui fait qu'utiliser 10 cartes réseaux virtuelles permet d'utiliser les capacités de la carte réseau à 100%. Il est possible de faire une analogie entre les processeurs multithreadés et les périphériques virtuels. Un processeur multithreadé est dupliqué en plusieurs processeurs virtuels, un périphérique virtualisé est dupliqué en plusieurs périphériques virtuels. L'implémentation des deux techniques est similaire sur le principe, mais les détails varient selon qu'on parle d'une carte réseau, d'une carte graphique, d'une carte son, etc. Pour gérer plusieurs périphériques virtuels, le périphérique physique contient plusieurs copies de ses registres de commande/données, plusieurs files de commandes, etc. De plus, le périphérique physique contient divers circuits d'arbitrage, qui gèrent comment le matériel est utilisé. Ils donnent accès à tour de rôle à chaque VM aux ressources non-dupliquées. [[File:Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles.png|centre|vignette|upright=2|Implémentation d'une carte réseau gérant plusieurs cartes réseaux virtuelles]] Dans le cas le plus simple, le matériel traite les commandes provenant des différentes VM dans l'ordre d'arrivée, une par une, il n'y a pas d'arbitrage pour éviter qu'une VM monopolise le matériel. Plus évolué, le matériel peut faire de l'affectation au tour par tour, en traitant chaque VM dans l'ordre durant un certain temps. Le matériel peut aussi utiliser des algorithmes d'ordonnancement/répartition plus complexes. Par exemple, les cartes graphiques modernes utilisent des algorithmes de répartition/ordonnancement accélérés en matériel, implémentés dans le GPU lui-même. ===La virtualisation des interruptions=== La gestion des interruptions matérielles peut aussi être accélérée en matériel, en complément des techniques de périphériques virtuels vues plus haut. Par exemple, il est possible de gérer des ''exitless interrupts'', qui ne passent pas du tout par l'hyperviseur. Mais cela demande d'utiliser l'affectation directe, en complément de l'usage de périphériques virtuels. Tout périphérique virtuel émet des interruptions distinctes des autres périphérique virtuel. Pour distinguer les interruptions provenant de cartes virtuelles de celles provenant de cartes physiques, on les désigne sous le terme d''''interruptions virtuelles'''. Une interruption virtuelle est destinée à une seule machine virtuelle : celle à laquelle est assignée la carte virtuelle. Les autres machines virtuelles ne reçoivent pas ces interruptions. Les interruptions virtuelles ne sont pas traitées par l'hyperviseur, seulement par l'OS de la machine virtuelle assignée. Une subtilité a lieu sur les processeurs à plusieurs cœurs. Il est possible d'assigner un cœur à chaque machine virtuelle, possiblement plusieurs. Par exemple, un processeur octo-coeur peut exécuter 8 machines virtuelles simultanément. Avec l'affectation directe ou à tour de rôle, l'interruption matérielle est donc destinée à une machine virtuelle, donc à un cœur. L'IRQ doit donc être redirigée vers un cœur bien précis et ne pas être envoyée aux autres. Les contrôleurs d'interruption modernes déterminent à quelles machines virtuelles sont destinées telle ou telle interruption, et peuvent leur envoyer directement, sans passer par l'hyperviseur. Grâce à cela, l'affectation directe à tour de rôle ont de bonnes performances. En plus de ce support des interruptions virtuelles, le contrôleur d'interruption peut aussi être virtualisé, à savoir être dupliqué en plusieurs '''contrôleurs d'interruption virtuels'''. Sur les systèmes à processeur x86, le contrôleur d'interruption virtualisé est l'APIC (''Advanced Programmable Interrupt Controller''). Diverses technologies de vAPIC, aussi dites d'APIC virtualisé, permettent à chaque machine virtuelle d'avoir une copie virtuelle de l'APIC. Pour ce faire, tous les registres de l'APIC sont dupliqués en autant d'exemplaires que d'APIC virtuels supportés. Il existe un équivalent sur les processeurs ARM, où le contrôleur d'interruption est nommé le ''Generic Interrupt Controller'' (GIC) et peut aussi être virtualisé. La virtualisation de l'APIC permet d'éviter d'avoir à passer par l'hyperviseur pour gérer les interruptions. Par exemple, quand un OS veut prévenir qu'il a fini de traiter une interruption, il doit communiquer avec le contrôleur d'interruption. Sans virtualisation du contrôleur d'interruption, cela demande de passer par l'intermédiaire de l'hyperviseur. Mais s'il est virtualisé, l'OS peut communiquer directement avec le contrôleur d'interruption virtuel qui lui est associé, sans que l'hyperviseur n'ait à faire quoique ce soit. De plus, la virtualisation du contrôleur d'interruption permet de gérer des interruptions inter-processeurs dites postées, qui ne font pas appel à l'hyperviseur, ainsi que des interruptions virtuelles émises par les IO-MMU. Sur les plateformes ARM, les ''timers'' sont aussi virtualisés. ==Annexe : le mode virtuel 8086 des premiers CPU Intel== Les premiers processeurs x86 étaient rudimentaires. Le 8086 utilisait une forme très simple de segmentation, sans aucune forme de protection mémoire, ni même de mémoire virtuelle, dont le but était d'adresser plus de 64 kibioctets de mémoire avec un processeur 16 bits. Sur le processeur 286, la segmentation s'améliora et ajouta un support complet de la mémoire virtuelle et de la protection mémoire. L'espace d'adressage en mode protégé est passé de 24 bits sur le CPU 286, à 32 bits sur le 386 et les CPU suivants. Pour bien faire la différence, la segmentation du 8086 fut appelée le mode réel, et la nouvelle forme de segmentation fut appelée le mode protégé. Les programmes conçus pour le mode réel ne pouvaient pas s'exécuter en mode protégé. En clair, tous les programmes conçus pour le 8086 devaient fonctionner en mode réel, qui était supporté sur le 286 et les processeurs suivant. Pour corriger les problèmes observés sur le 286, le 386 a ajouté un '''mode 8086 virtuel''', une technique de virtualisation qui permet à des programmes de s'exécuter en mode réel dans une machine virtuelle dédiée appelée la '''VM V86'''. Notez que nous utiliserons l'abréviation V86 pour parler du mode virtuel 8086, ainsi que de tout ce qui est lié à ce mode. ===La virtualisation du DOS et la mémoire étendue=== Utiliser le mode V86 demande d'avoir un programme 8086 à lancer, mais aussi d'utiliser un '''hyperviseur V86'''. L’hyperviseur V86 est un véritable hyperviseur, qui s’exécute en mode noyau, exécute des routines d'interruption, gère les exceptions matérielles, etc. Il réside en mémoire dans une zone non-adressable en mode réel, mais accessible en mode protégé, celui-ci permettant d'adresser plus de RAM. L'hyperviseur est forcément exécuté en mode protégé. Le système d'exploitation DOS s'exécutait en mode réel, ce qui fait qu'il pouvait être émulé par le mode V86. Il était ainsi possible de lancer une ou plusieurs sessions DOS à partir d'un système d'exploitation multitâche comme Windows. Windows. Beaucoup de personnes nées avant les années 2000 ont sans doute profité de cette possibilité pour lancer des applications DOS sous Windows. Les applications étaient en réalité lancées dans une machine virtuelle grâce au mode V86. Windows implémentait un hyperviseur V86 de type 2, à savoir que c'était un logiciel qui s'exécutait sur un OS sous-jacent, ici [[File:Hyperviseur.svg|centre|vignette|upright=2|Hyperviseur]] Les applications DOS dans une VM V86 ne peuvent pas adresser plus d'un mébioctet de mémoire. L'ordinateur peut cependant avoir plus de mémoire RAM, notamment pour gérer l'hyperviseur V86. Diverses techniques permettaient aux applications DOS d'utiliser la mémoire au-delà du mébioctet, appelée la '''mémoire étendue'''. Les logiciels DOS accédaient à la mémoire étendue en passant par un intermédiaire logiciel, qui lui-même communiquait avec l'hyperviseur V86. L'intermédiaire est appelé le ''Extended Memory Manager'' (EMM), et il est concrètement implémenté par un driver sur DOS (HIMEM.SYS). Les applications DOS ne pouvaient pas adresser la mémoire étendue, mais pouvaient échanger des données avec l'EMM. Les logiciels peuvent ainsi déplacer des données dans la mémoire étendue pour les garder au chaud, puis les rapatrier dans la mémoire conventionnelle quand ils en avaient besoin. L'intermédiaire ''Extended Memory Manager'' s'occupe d’échanger des données entre mémoire conventionnelle et mémoire étendue. Pour cela, il switche entre mode réel et protégé à la demande, quand il doit lire ou écrire en mémoire étendue. Il ne faut pas confondre mémoire étendue et ''expanded memory''. Pour rappel, l'''expanded memory'' est un système de commutation de banque, qui autorise un va-et-vient entre une carte d'extension et une page de 64 kibioctets mappé en mémoire haute. Elle fonctionne sans mode protégé, sans virtualisation, sans mode V86. La mémoire étendue ne gère pas de commutation de banque et demande que la RAM en plus soit installée dans l'ordinateur, pas sur une carte d'extension. Par contre, il est possible d'émuler l'''expanded memory'' sans carte d'extension, en utilisant la mémoire étendue. Quelques ''chipsets'' de carte mère intégraient des techniques cela. Une émulation logicielle était aussi possible. L'émulation logicielle se basait sur une réécriture de l'interruption 67h utilisée pour adresser la technologie ''expanded memory''. L'hyperviseur V86 pouvait s'en charger, il avait juste à réécrire son allocateur de mémoire pour gérer cette interruption et quelques autres détails. ===Le fonctionnement du mode virtuel 8086 de base=== En mode V86, la segmentation du mode protégé est désactivée, seule la segmentation du mode réel est utilisée. Il y a quelques subtilités liées à la ligne A20 du bus d'adresse, déjà abordées auparavant dans ce cours. Sur les CPU 286 et ultérieurs, le processeur peut adresser 1 mébioctet (2^20 adresses), plus 64 kibioctets qui ne sont pas adressables sur le 8086. Le tout permet donc d'adresser les adresses allant de 0 à 0x010FFEFH. Et ces adresses sont utilisées pour les programmes en mode réel. Les adresses au-delà de l'adresse 0x010FFEFH sont typiquement le lieu de résidence de l’hyperviseur en RAM. Par contre, la pagination peut être activée par l’hyperviseur, afin d’exécuter plusieurs logiciels en mode réel simultanément. La mémoire virtuelle par pagination peut aussi être utile si l'ordinateur a peu de mémoire RAM, pas assez pour faire tourner le logiciel : l'espace d'adressage vu par le logiciel est un espace virtuel de grande taille, ce qui permet de lancer le logiciel, au prix de performances dégradées. Enfin, la gestion des entrées-sorties mappées en mémoire est aussi simplifiée. De plus, cela permettait d'adresser plus de mémoire RAM grâce aux adresses plus longues du mode protégé. Le processeur est configuré en mode V86, le bit VM du registre d'état spécifique est mis à 1. Le processeur utilise ce bit lorsqu'une instruction utilise les registres de segments, afin de savoir comment calculer les adresses, le calcul n'étant pas le même en mode réel et en mode protégé. Quelques instructions machines dépendent aussi de la valeur de ce bit, mais cela est traité au décodage de l'instruction. Il faut noter que dans le mode 8086 virtuel, les programmes peuvent utiliser les registres ajoutés sur le 386 et ultérieurs. Par exemple, le 8086 n'a que 4 registres de segment, alors que le 286 en a 6. Les programmes en mode 8086 virtuel peuvent utiliser les deux registres de segment supplémentaires. Il en est de même pour d'autres registres ajoutés par le 286, comme des registres de contrôle, des registres de debug, et quelques autres. Il en est de même pour les instructions ajoutées par le 286, le 386 et ultérieur, qui sont exécutables en mode virtuel 8086. Et elles sont nombreuses. La compatibilité n'était pas parfaite, il y avait quelques petites différences entre ce mode V86 et le mode réel du 8086, idem avec le mode réel du 286. Mais la grande majorité des applications n'avait aucun problème. Les problèmes étaient concentrés sur quelques instructions précises, notamment celles avec un préfixe LOCK. ===Les ''Virtual-8086 mode extensions''=== A partir du processeur Pentium, les processeurs x86 ont introduit des optimisations du mode V86, afin de rendre la virtualisation plus rapide. L'ensemble de ces optimisations est regroupé sous le terme de '''''Virtual-8086 mode extensions''''', abrévié en VME. Les optimisations du VMA étaient, pour certaines, utiles au-delà de la virtualisation et étaient activables indépendamment du reste du VME. Le VME introduisait des optimisations quant au traitement des interruptions, à savoir la gestion des interruptions virtuelles. De plus, le VME modifie la gestion de l'''interrupt flag'' du registre d'état. Pour rappel, ce bit permet d'activer ou de désactiver les interruptions masquables. Modifier le bit ''interrupt flag'' permettait de désactiver les interruptions masquables ou au contraire de les activer. Il se trouve que ce bit était accesible par les programmes exécutés en mode réel, qui pouvaient en faire ce qu'ils voulaient. Le mode réel n'étant pas prévu pour la multi-programmation, ce n'était pas un problème. Mais en mode V86, toute modification de ce bit se répercute sur les autres VM en mode V86. Pour éviter les problèmes, le VME a ajouté de quoi virtualiser cet ''interrupt flag'', avec une copie par machine virtuelle V86. Chaque programme modifiait sa propre copie de l'''interrupt flag'' sans altérer celle des autres programmes exécutés en mode V86, et surtout sans déclencher une exception matérielle gérée par l'hyperviseur. Bien qu'elles aient été introduites sur les processeurs Pentium, elles n'ont réellement été rendues publiques qu'après la sortie des processeurs de microarchitecture P6. Avant d'être rendue publique, la documentation du VME était une annexe de la documentation officielle, la fameuse annexe H. Elle était mentionnée dans la documentation officielle, mais était indisponible au grand public, seules quelques entreprises sous NDA y avait accès. <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les sections critiques et le modèle mémoire | prevText=Les sections critiques et le modèle mémoire | next=Le matériel réseau | nextText=Le matériel réseau }} </noinclude> qnag9hjn34lm9dskj3ehbtn7pubzpc8