Что такое разделяемая память linux

ЋЎй п (б®ў¬Ґбв­® ЁбЇ®«м§гҐ¬ п) Ї ¬пвм

Ћ¤Ё­ Ё§ б ¬ле Їа®бвле ¬Ґв®¤®ў ¬Ґ¦Їа®жҐбб®ў®Ј® ў§ Ё¬®¤Ґ©бвўЁп — ЁбЇ®«м§®ў вм ®Ўйго Ї ¬пвм. ЋЎй п Ї ¬пвм Ї®§ў®«пҐв ¤ўг¬ Ё«Ё Ў®«ҐҐ Їа®жҐбб ¬ ®Ўа й вмбп Є ®¤­®© Ё в®© ¦Ґ ®Ў« бвЁ Ї ¬пвЁ, Є Є Ўг¤в® ®­Ё ўбҐ ўл§лў «Ё malloc Ё Ё¬ Ўл«Ё ў®§ўа 饭л гЄ § ⥫Ё ­ ®¤­г Ё вг ¦Ґ дЁ§ЁзҐбЄго Ї ¬пвм. Љ®Ј¤ ®¤Ё­ Їа®жҐбб Ё§¬Ґ­пҐв Ї ¬пвм, ўбҐ ¤агЈЁҐ Їа®жҐббл «ўЁ¤пв» ¬®¤ЁдЁЄ жЁо.

Ѓлбв஥ «®Є «м­®Ґ ў§ Ё¬®¤Ґ©бвўЁҐ

ЋЎй п Ї ¬пвм — б ¬ п Ўлбва п д®а¬ ¬Ґ¦Їа®жҐбб®ў®Ј® ў§ Ё¬®¤Ґ©бвўЁп, Ї®в®¬г зв® ўбҐ Їа®жҐббл б®ў¬Ґбв­® ЁбЇ®«м§гов ®¤­г Ёвг ¦Ґ з бвм Ї ¬пвЁ. „®бвгЇ Є нв®© ®ЎйҐ© Ї ¬пвЁ ®бгйҐбвў«пҐвбп б в®© ¦Ґ бЄ®а®бвмо, зв® Ё ЇаЁ ®Ўа 饭ЁЁ Є ­Ґб®ў¬Ґбв­® ЁбЇ®«м§гҐ¬®© Ї ¬пвЁ, Ё нв® ­Ґ вॡгҐв бЁб⥬­®Ј® ўл§®ў Ё«Ё ўе®¤ ў п¤а®. ќв® в Є¦Ґ ­Ґ вॡгҐв Ё§«Ёи­ҐЈ® Є®ЇЁа®ў ­Ёп ¤ ­­ле.

Џ®бЄ®«мЄг п¤а® ­Ґ бЁ­еа®­Ё§ЁагҐв ¤®бвгЇл Є б®ў¬Ґбв­® ЁбЇ®«м§гҐ¬®© Ї ¬пвЁ, ўл ¤®«¦­л б ¬Ё ®ЎҐбЇҐзЁвм бЁ­еа®­Ё§ жЁо. Ќ ЇаЁ¬Ґа, Їа®жҐбб ­Ґ ¤®«¦Ґ­ зЁв вм Ё§ Ї ¬пвЁ, Ї®Є ¤ ­­лҐ ­Ґ § ЇЁб ­л вг¤ , Ё ¤ў Їа®жҐбб ­Ґ ¤®«¦­л ­ ЇЁб вм Ї® ®¤­®¬г Ё ⮬㠦Ґ ¤аҐбг Ї ¬пвЁ ў ®¤­® Ё в® ¦Ґ ўаҐ¬п. ЋЎй п бва ⥣Ёп Ё§ЎҐ¦ ­Ёп гб«®ўЁ© Ј®­ЄЁ б®бв®Ёв ў ⮬, зв®Ўл ЁбЇ®«м§®ў вм ᥬ д®ал.

Њ®¤Ґ«м Ї ¬пвЁ

—в®Ўл ЁбЇ®«м§®ў вм ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ, ®¤Ё­ Їа®жҐбб ¤®«¦Ґ­ ўл¤Ґ«Ёвм ᥣ¬Ґ­в. ’®Ј¤ Є ¦¤л© Їа®жҐбб, ¦Ґ« ойЁ© ®Ўа й вмбп Є ᥣ¬Ґ­вг ¤®«¦Ґ­ Ї®¤Є«озЁвм ᥣ¬Ґ­в. Џ®б«Ґ ®Є®­з ­Ёп ҐЈ® ЁбЇ®«м§®ў ­Ёп ᥣ¬Ґ­в , Є ¦¤л© Їа®жҐбб ®вЄ«оз Ґв ᥣ¬Ґ­в. ‚ ­ҐЄ®в®ал© ¬®¬Ґ­в, ®¤Ё­ Їа®жҐбб ¤®«¦Ґ­ ®бў®Ў®¤Ёвм ᥣ¬Ґ­в.

Џ®­Ё¬ ­ЁҐ ¬®¤Ґ«Ё Ї ¬пвЁ Linux Ї®¬®Ј Ґв ®Ўкпб­Ёвм Їа®жҐбб ўл¤Ґ«Ґ­Ёп Ё Ї®¤Є«о祭Ёп. Џ®¤ Linux , ўЁавг «м­ п Ї ¬пвм Є ¦¤®Ј® Їа®жҐбб а §ЎЁв ­ бва ­Ёжл. Љ ¦¤л© Їа®жҐбб Ї®¤¤Ґа¦Ёў Ґв ®в®Ўа ¦Ґ­ЁҐ ҐЈ® ¤аҐб®ў Ї ¬пвЁ ­ нвЁ бва ­Ёжл ўЁавг «м­®© Ї ¬пвЁ, Є®в®алҐ б®¤Ґа¦ в д ЄвЁзҐбЄЁҐ ¤ ­­лҐ. € е®вп Є ¦¤л© Їа®жҐбб Ё¬ҐҐв б®Ўб⢥­­лҐ ¤аҐб , ®в®Ўа ¦Ґ­Ёп ¬­®ЈЁе Їа®жҐбб®ў ¬®Јгв гЄ §лў вм ­ ®¤­г Ё вг ¦Ґ бва ­Ёжг, а §аҐи п б®ў¬Ґбв­®Ґ ЁбЇ®«м§®ў ­ЁҐ Ї ¬пвЁ.

‚뤥«Ґ­ЁҐ ­®ў®Ј® ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ ЇаЁў®¤Ёв Є б®§¤ ­Ёо бва ­Ёжл ўЁавг «м­®© Ї ¬пвЁ. Џ®бЄ®«мЄг ўбҐ Їа®жҐббл ¦Ґ« ов ®Ўа вЁвмбп Є ®¤­®¬г Ё ⮬㠦Ґ ®ЎйҐ¬г ᥣ¬Ґ­вг, в® в®«мЄ® ®¤Ё­ Їа®жҐбб ¤®«¦Ґ­ ўл¤Ґ«Ёвм ­®ўл© ®ЎйЁ© ᥣ¬Ґ­в. ‚뤥«Ґ­ЁҐ бгйҐбвўго饣® ᥣ¬Ґ­в ­Ґ б®§¤ Ґв ­®ўле бва ­Ёж, ў®§ўа й Ґв Ё¤Ґ­вЁдЁЄ в®а ¤«п бгйҐбвўгойЁе. —в®Ўл а §аҐиЁвм Їа®жҐббг ЁбЇ®«м§®ў вм ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ, Їа®жҐбб Ї®¤Є«оз Ґв ᥣ¬Ґ­в, Є®в®ал© ¤®Ў ў«пҐв ®в®Ўа ¦Ґ­ЁҐ ҐЈ® ўЁавг «м­®© Ї ¬пвЁ ­ ®ЎйҐ¤®бвгЇ­лҐ бва ­Ёжл ᥣ¬Ґ­в . Љ®Ј¤ а Ў®в б ᥣ¬Ґ­в®¬ § ўҐа襭 , нвЁ ®в®Ўа ¦Ґ­Ёп г¤ «повбп. Љ®Ј¤ ­Ё ®¤­Ё Ё§ Їа®жҐбб®ў ­Ґ е®зҐв ®Ўа й вмбп Є ᥣ¬Ґ­в ¬ ®ЎйҐ© Ї ¬пвЁ, Є Є®©-в® ®¤Ё­ Їа®жҐбб ¤®«¦Ґ­ ®бў®Ў®¤Ёвм бва ­Ёжл ўЁавг «м­®© Ї ¬пвЁ. ‚ᥠᥣ¬Ґ­вл ®ЎйҐ© Ї ¬пвЁ ўл¤Ґ«повбп Ї®бва ­Ёз­® Ё ®ЄагЈ«повбп ¤® а §¬Ґа бва ­Ёжл бЁб⥬л, Є®в®ал© пў«пҐвбп зЁб«®¬ Ў ©в®ў ў бва ­ЁжҐ Ї ¬пвЁ. Ќ бЁб⥬ е Linux , а §¬Ґа бва ­Ёжл а ўҐ­ 4 ЉЃ, ­® ўл ¤®«¦­л Ї®«гзЁвм нв® §­ 祭ЁҐ, ўл§лў п дг­ЄжЁо getpagesize .

‚뤥«Ґ­ЁҐ

Џа®жҐбб ўл¤Ґ«пҐв ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ, ЁбЇ®«м§гп shmget (» SHared Memory GET «). …Ј® ЇҐаўл© Ї а ¬Ґва — 楫®зЁб«Ґ­­л© Є«оз, Є®в®ал© ®ЇаҐ¤Ґ«пҐв, Є Є®© ᥣ¬Ґ­в б®§¤ вм. ЌҐбўп§ ­­лҐ Їа®жҐббл ¬®Јгв ®Ўа й вмбп Є ®¤­®¬г Ё ⮬㠦Ґ ᥣ¬Ґ­вг, ЁбЇ®«м§гп ®¤­® Ё в® ¦Ґ Є«о祢®Ґ §­ 祭ЁҐ. Љ ᮦ «Ґ­Ёо, ¤агЈЁҐ Їа®жҐббл, ў®§¬®¦­®, в Є¦Ґ ўлЎа «Ё в®в ¦Ґ б ¬л© Є«оз, зв® ¬®¦Ґв ЇаЁўҐбвЁ Є Є®­д«ЁЄвг. €бЇ®«м§гп бЇҐжЁ «м­го Є®­бв ­вг IPC_PRIVATE Є Є Є«о祢®Ґ §­ 祭ЁҐ, Ј а ­вЁагҐвбп, зв® б®§¤ бвбп б®ўҐа襭­® ­®ўл© ᥣ¬Ґ­в Ї ¬пвЁ.

…Ј® ўв®а®© Ї а ¬Ґва ®ЇаҐ¤Ґ«пҐв зЁб«® Ў ©в®ў ў ᥣ¬Ґ­вҐ. Џ®бЄ®«мЄг ᥣ¬Ґ­вл ўл¤Ґ«повбп Ї®бва ­Ёз­®, зЁб«® д ЄвЁзҐбЄЁ ўл¤Ґ«Ґ­­ле Ў ©в ®ЄагЈ«пҐвбп ¤® а §¬Ґа бва ­Ёжл.

’аҐвЁ© Ї а ¬Ґва — Ї®а §ап¤­®Ґ ¤ў®Ёз­®Ґ Ё«Ё §­ 祭Ё© д« ¦Є , Є®в®алҐ ®ЇаҐ¤Ґ«пов ®ЇжЁЁ Є shmget . ‡­ 祭Ёп д« ¦Є ўЄ«оз ов в ЄЁҐ Ї а ¬Ґвал:

  • IPC_CREAT — нв®в д« ¦®Є гЄ §лў Ґв, зв® ¤®«¦Ґ­ Ўлвм б®§¤ ­ ­®ўл© ᥣ¬Ґ­в. ќв® а §аҐи Ґв б®§¤ ў вм ­®ўл© ᥣ¬Ґ­в, ®ЇаҐ¤Ґ«пп Є«оз.
  • IPC_EXCL — нв®в д« ¦®Є, Є®в®ал© ўбҐЈ¤ ЁбЇ®«м§гҐвбп б IPC_CREAT ,§ бв ў«пҐв shmget ў®§ўа й вм ®иЁЎЄг, Ґб«Ё ᥣ¬Ґ­в­л© Є«оз ®ЇаҐ¤Ґ«Ґ­, Є Є 㦥 бгйҐбвўгойЁ©. ќв® ЁбЇ®«м§гҐвбп ¤«п ўл¤Ґ«Ґ­Ёп «нЄбЄ«о§Ёў­®Ј®» ᥣ¬Ґ­в . …б«Ё нв®в д« ¦®Є ­Ґ ¤ Ґвбп, Ё Є«оз бгйҐбвўго饣® ᥣ¬Ґ­в ЁбЇ®«м§гҐвбп, shmget ў®§ўа й Ґв бгйҐбвўгойЁ© ᥣ¬Ґ­в ў¬Ґбв® в®Ј®, зв®Ўл б®§¤ вм ­®ўл©.
  • Mode flags — нв® §­ 祭ЁҐ Ё§ 9 ЎЁв®ў, гЄ §лў ойЁе Їа ў , ЇаҐ¤®бв ў«Ґ­­лҐ ў« ¤Ґ«мжг, ЈагЇЇҐ, Ё ¬Ёаг(®бв «м­л¬), ­ гЇа ў«Ґ­ЁҐ ¤®бвгЇ®¬ Є ᥣ¬Ґ­вг. ЃЁвл ўлЇ®«­Ґ­Ёп ЁЈ­®аЁаговбп. Џа®бв®© бЇ®б®Ў ®ЇаҐ¤Ґ«Ёвм Їа ў б®бв®Ёв ў ⮬, зв®Ўл ЁбЇ®«м§®ў вм Є®­бв ­вл, ®ЇаҐ¤Ґ«Ґ­­лҐ ў Ё ®ЇЁб ­лҐ ў а §¤Ґ«Ґ 2 stat man-бва ­Ёж . Ќ ЇаЁ¬Ґа, S_IRUSR Ё S_IWUSR ®ЇаҐ¤Ґ«пов Їа ў ­ з⥭ЁҐ Ё § ЇЁбм ¤«п ў« ¤Ґ«мж ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ, S_IROTH Ё S_IWOTH ®ЇаҐ¤Ґ«пов Їа ў ­ з⥭ЁҐ Ё § ЇЁбм ¤«п ¤агЈЁе.
Читайте также:  Как запускать приложения линукс минт

Ќ ЇаЁ¬Ґа, нв®в ўл§®ў shmget б®§¤ Ґв ­®ўл© ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ (Ё«Ё ®Ўа й Ґвбп Є бгйҐбвўго饬г, Ґб«Ё shm_key 㦥 ЁбЇ®«м§гҐвбп), б Їа ў ¬Ё ­ зЁвҐ­ЁҐ Ё § ЇЁбм ў« ¤Ґ«м楬, ­® ­Ґ ¤агЈЁ¬Ё Ї®«м§®ў ⥫ﬨ.

…б«Ё ўл§®ў гᯥ襭, shmget ў®§ўа й Ґв Ё¤Ґ­вЁдЁЄ в®а ᥣ¬Ґ­в . …б«Ё ᥣ¬Ґ­в ®ЎйҐ© Ї ¬п⨠㦥 бгйҐбвўгҐв, в® Їа ў ­ ¤®бвгЇ Їа®ўҐаҐ­л, Ё Їа®ўҐаЄ Ј а ­вЁагҐв, з⮠ᥣ¬Ґ­в ­Ґ ®в¬ҐзҐ­ ¤«п г¤ «Ґ­Ёп.

Џ®¤Є«о祭ЁҐ Ё ®вЄ«о祭ЁҐ

—в®Ўл ᤥ« вм ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ ¤®бвгЇ­л¬, Їа®жҐбб ¤®«¦Ґ­ ЁбЇ®«м§®ў вм shmat , » SHared Memory ATtach » ЏҐаҐ¤ ©вҐ Ґ¬г Ё¤Ґ­вЁдЁЄ в®а ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ SHMID , ў®§ўа 饭­л© shmget . ‚в®а®© Ї а ¬Ґва — гЄ § ⥫м, Є®в®ал© ®ЇаҐ¤Ґ«пҐв, Ј¤Ґ ў ¤аҐб­®¬ Їа®бва ­б⢥ ў 襣® Їа®жҐбб ўл е®вЁвҐ ®в®Ўа §Ёвм ®Ўйго Ї ¬пвм; Ґб«Ё ўл ЇҐаҐ¤ ¤ЁвҐ NULL , в® Linux ўлЎҐаҐв «оЎ®© ¤®бвгЇ­л© ¤аҐб. ’аҐвЁ© Ї а ¬Ґва — д« ¦®Є, Є®в®ал© ¬®¦Ґв ўЄ«озЁвм б«Ґ¤гойЁҐ Ї а ¬Ґвал:

  • SHM_RND гЄ §лў Ґв, зв® ¤аҐб, ®ЇаҐ¤Ґ«Ґ­­л© ¤«п ўв®а®Ј® Ї а ¬Ґва , ¤®«¦Ґ­ Ўлвм ®ЄагЈ«Ґ­ ­ § ¤ Є ¬­®¦ЁвҐ«о а §¬Ґа бва ­Ёжл. …б«Ё ‚л ­Ґ ®ЇаҐ¤Ґ«пҐвҐ нв®в д« ¦®Є, ‚л ¤®«¦­л ўла®ў­пвм ­ Ја ­Ёжг бва ­Ёжл ўв®а®© Ї а ¬Ґва ЇҐаҐ¤ ў Ґ¬л© shmat б ¬®бв®п⥫쭮.
  • SHM_RDONLY гЄ §лў Ґв, з⮠ᥣ¬Ґ­в Ўг¤Ґв ¤®бвгЇҐ­ в®«мЄ® ¤«п з⥭Ёп.
  • ЃЁвл Їа ў ¤®бв Ї в ЄЁҐ ¦Ґ Є Є Ё ¤«п д ©«®ў.

…б«Ё ўл§®ў гᯥ襭, ®­ ўҐа­Ґв ¤аҐб Ї®¤Є«о祭­®Ј® ®ЎйҐЈ® ᥣ¬Ґ­в . Џ®в®¬ЄЁ, б®§¤ ­­лҐ ўл§®ў ¬Ё fork , ­ б«Ґ¤гов Ї®¤Є«о祭­лҐ ®ЎйЁҐ ᥣ¬Ґ­вл; ®­Ё ¬®Јгв ®вЄ«озЁвм ᥣ¬Ґ­вл ®ЎйҐ© Ї ¬пвЁ, Ґб«Ё § е®впв.

Љ®Ј¤ ўл § Є®­зЁ«Ё а Ў®вг б ᥣ¬Ґ­в®¬ ®ЎйҐ© Ї ¬пвЁ, ᥣ¬Ґ­в ¤®«¦Ґ­ Ўлвм ®вЄ«о祭, ЁбЇ®«м§гп shmdt (» SHared Memory DeTach «). ЏҐаҐ¤ ©вҐ Ґ¬г ¤аҐб, ў®§ўа 饭­л© shmat . …б«Ё ᥣ¬Ґ­в Ўл« ®бў®Ў®¦¤Ґ­, Ё Ў®«миҐ ­Ґ ®бв «®бм Їа®жҐбб®ў, ЁбЇ®«м§гойЁе ҐЈ®, ®­ Ўг¤Ґв г¤ «Ґ­. ‚л§®ўл exit Ё exec ўв®¬ вЁзҐбЄЁ ®вЄ«оз ов ᥣ¬Ґ­вл.

“Їа ў«Ґ­ЁҐ Ё ®бў®Ў®¦¤Ґ­ЁҐ ®ЎйҐ© Ї ¬пвЁ

Shmctl (» SHared Memory ConTrol «) ўл§®ў ў®§ўа й Ґв Ё­д®а¬ жЁо ®Ў ᥣ¬Ґ­вҐ ®ЎйҐ© Ї ¬пвЁ Ё ¬®¦Ґв Ё§¬Ґ­Ёвм ҐЈ®.ЏҐаўл© Ї а ¬Ґва — Ё¤Ґ­вЁдЁЄ в®а ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ.

—в®Ўл Ї®«гзЁвм Ё­д®а¬ жЁо ® ᥣ¬Ґ­вҐ ®ЎйҐ© Ї ¬пвЁ, ЇҐаҐ¤ ©вҐ IPC_STAT Є Є ўв®а®© Ї а ¬Ґва Ё гЄ § вҐ«м ­ struct shmid_ds .

—в®Ўл г¤ «Ёвм ᥣ¬Ґ­в, ЇҐаҐ¤ ©вҐ IPC_RMID Є Є ўв®а®© Ї а ¬Ґва, Ё ЇҐаҐ¤ ©вҐ NULL Є Є ваҐвЁ© Ї а ¬Ґва. ‘ҐЈ¬Ґ­в г¤ «Ґ­, Є®Ј¤ Ї®б«Ґ¤­Ё© Їа®жҐбб, Є®в®ал© Ї®¤Є«озЁ« ҐЈ®, ®вЄ«озЁв ҐЈ®.

Љ ¦¤л© ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ ¤®«¦Ґ­ Ўлвм пў­® ®бў®Ў®¦¤Ґ­, ЁбЇ®«м§гп shmctl , Є®Ј¤ ‚л § Є®­зЁ«Ё а Ў®вг б ­Ё¬, зв®Ўл Ё§ЎҐ¦ вм ­ аг襭ЁҐ бЁб⥬­®Ј® ЇаҐ¤Ґ« а §¬Ґа Є®«ЁзҐб⢠ᥣ¬Ґ­в®ў ®ЎйҐ© Ї ¬пвЁ. ‚л§®ўл exit Ё exec ®вЄ«оз в ᥣ¬Ґ­вл Ї ¬пвЁ, ­® ­Ґ ®бў®Ў®¦¤пв Ёе.

‘¬®ваЁ shmctl man-бва ­Ёжг ¤«п ®ЇЁб ­Ёп ¤агЈЁе ®ЇҐа жЁ©, Є®в®алҐ ¬®¦­® ўлЇ®«­пвм б ᥣ¬Ґ­в ¬Ё ®ЎйҐ© Ї ¬пвЁ.

ЏаЁ¬Ґа Їа®Ја ¬¬л

Џа®Ја ¬¬ «ЁбвЁ­Ј 5.1 Ё««обваЁагҐв ЁбЇ®«м§®ў ­ЁҐ ®ЎйҐ© Ї ¬пвЁ.

Љ®¬ ­¤ ipcs ЇаҐ¤®бв ў«пҐв Ё­д®а¬ жЁо ®в­®бЁвҐ«м­® б।бвў ў§ Ё¬®¤Ґ©бвўЁп Їа®жҐбб®ў, ўЄ«оз п ®ЎйЁҐ ᥣ¬Ґ­вл Ї ¬пвЁ. €бЇ®«м§г©вҐ д« Ј -m , зв®Ўл Ї®«гзЁвм Ё­д®а¬ жЁо ®Ў ®ЎйҐ© Ї ¬пвЁ. Ќ ЇаЁ¬Ґа, нв®в Є®¤ Ё««обваЁагҐв з⮠ᥣ¬Ґ­в ®ЎйҐ© Ї ¬пвЁ, Їа®­г¬Ґа®ў ­­л© 1627649, ­ 室Ёвбп ў ЁбЇ®«м§®ў ­ЁЁ:

…б«Ё нв®в ᥣ¬Ґ­в Ї ¬пвЁ Ўл« ®иЁЎ®з­® ®бв ў«Ґ­ Їа®Ја ¬¬®©, ўл ¬®¦ҐвҐ ЁбЇ®«м§®ў вм Є®¬ ­¤г ipcrm , зв®Ўл г¤ «Ёвм ҐЈ®.

‡ Ё Їа®вЁў

CҐЈ¬Ґ­вл ®ЎйҐ© Ї ¬пвЁ Ї®§ў®«пов ®бгйҐбвў«пвм Ўлбваго ¤ўг­ Їа ў«Ґ­­го бўп§м б।Ё «оЎ®Ј® зЁб« Їа®жҐбб®ў. Љ ¦¤л© Ї®«м§®ў вҐ«м ¬®¦Ґв Ё зЁв вм Ё ЇЁб вм, ­® Їа®Ја ¬¬ ¤®«¦­ гбв ­®ўЁвм Ё б«Ґ¤®ў вм ­ҐЄ®в®а®¬г Їа®в®Є®«г ¤«п в®Ј®, зв®Ўл ЇаҐ¤®вўа вЁвм гб«®ўЁп Ј®­ЄЁ вЁЇ ЇҐаҐ§ ЇЁбЁ Ё­д®а¬ жЁЁ ЇаҐ¦¤Ґ, 祬 ®­ Їа®зЁв Ґвбп. Љ ᮦ «Ґ­Ёо, Linux бва®Ј® ­Ґ Ј а ­вЁагҐв нЄбЄ«о§Ёў­л© ¤®бвгЇ ¤ ¦Ґ Ґб«Ё ўл б®§¤ ¤ЁвҐ ­®ўл© ®ЎйЁ© ᥣ¬Ґ­в б IPC_PRIVATE .

Ља®¬Ґ в®Ј®, ¤«п в®Ј® зв®Ў ­ҐбЄ®«мЄ® Їа®жҐбб®ў ¬®Ј«Ё ЁбЇ®«м§®ў вм ®Ўйго Ї ¬пвм, ®­Ё ¤®«¦­л ЇаЁ­пвм ¬Ґал, зв®Ўл ­Ґ ЁбЇ®«м§®ў вм ®¤Ё­ Ё в®в ¦Ґ Є«оз.

Источник

STL, allocator, его разделяемая память и её особенности

Разделяемая память — самый быстрый способ обмена данными между процессами. Но в отличие от потоковых механизмов (трубы, сокеты всех мастей, файловые очереди . ), здесь у программиста полная свобода действий, в результате пишут кто во что горазд.

Так и автор однажды задался мыслью, а что если … если произойдёт вырождение адресов сегментов разделяемой памяти в разных процессах. Вообще-то именно это происходит, когда процесс с разделяемой памятью делает fork, а как насчет разных процессов? Кроме того, не во всех системах есть fork.

Читайте также:  Last wake up windows

Казалось бы, совпали адреса и что с того? Как минимум, можно пользоваться абсолютными указателями и это избавляет от кучи головной боли. Станет возможно работать со строками и контейнерами С++, сконструированными из разделяемой памяти.

Отличный, кстати, пример. Не то, чтобы автор сильно любил STL, но это возможность продемонстрировать компактный и всем понятный тест на работоспособность предлагаемой методики. Методики, позволяющей (как видится) существенно упростить и ускорить межпроцессное взаимодействие. Вот работает ли она и чем придётся заплатить, будем разбираться далее.

Введение

Идея разделяемой памяти проста и изящна — поскольку каждый процесс действует в своём виртуальном адресном пространстве, которое проецируется на общесистемное физическое, так почему бы не разрешить двум сегментам из разных процессов смотреть на одну физическую область памяти.

А с распространением 64-разрядных операционных систем и повсеместным использованием когерентного кэша, идея разделяемой памяти получила второе дыхание. Теперь это не просто циклический буфер — реализация “трубы” своими руками, а настоящий “трансфункционер континуума” — крайне загадочный и мощный прибор, причем, лишь его загадочность равна его мощи.

Рассмотрим несколько примеров использования.

    Протокол “shared memory” при обмене данными с MS SQL. Демонстрирует некоторое улучшение производительности (

10. 15%)

  • Mysql также имеет под Windows протокол “shared memory”, который улучшает производительность передачи данных на десятки процентов.
  • Sqlite размещает в разделяемой памяти индекс навигации по WAL-файлу. Причем берётся существующий файл, который отображается в память. Это позволяет использовать его процессам с разными корневыми директориями (chroot).
  • PostgreSQL использует как раз fork для порождения процессов-обработчиков запросов. Причем эти процессы наследуют разделяемую память, структура которой показана ниже.

    Фиг.1 структура разделяемой памяти PostgreSQL (отсюда)

    Из общих соображений, а какой бы мы хотели видеть идеальную разделяемую память? На это легко ответить — желаем, чтобы объекты в ней можно было использовать, как если бы это были объекты, разделяемые между потоками одного процесса. Да, нужна синхронизация (а она в любом случае нужна), но в остальном — просто берёшь и используешь! Пожалуй, … это можно устроить.

    Для проверки концепции требуется минимально-осмысленная задача:

    • есть аналог std::map , расположенный в разделяемой памяти
    • имеем N процессов, которые асинхронно вносят/меняют значения с префиксом, соответствующим номеру процесса (ex: key_1_… для процесса номер 1)
    • в результате, конечный результат мы можем проконтролировать

    Начнём с самого простого — раз у нас есть std::string и std::map, потребуется и специальный аллокатор STL.

    Аллокатор STL

    Допустим, для работы с разделяемой памятью существуют функции xalloc/xfree как аналоги malloc/free. В этом случае аллокатор выглядит так:

    Этого достаточно, чтобы подсадить на него std::map & std::string

    Прежде чем заниматься заявленными функциями xalloc/xfree, которые работают с аллокатором поверх разделяемой памяти, стоит разобраться с самой разделяемой памятью.

    Разделяемая память

    Разные потоки одного процесса находятся в одном адресном пространстве, а значит каждый не thread_local указатель в любом потоке смотрит в одно и то же место. С разделяемой памятью, чтобы добиться такого эффекта приходится прилагать дополнительные усилия.

    Windows

      Создадим отображение файла в память. Разделяемая память так же как и обычная покрыта механизмом подкачки, здесь помимо всего прочего определяется, будем ли мы пользоваться общей подкачкой или выделим для этого специальный файл.

    Префикс имени файла “Local\\” означает, что объект будет создан в локальном пространстве имён сессии.
    Чтобы присоединиться к уже созданному другим процессом отображению, используем

    Теперь необходимо создать сегмент, указывающий на готовое отображение

    segment size 0 означает, что будет использован размер, с которым создано отображение с учетом сдвига.

    Самое важно здесь — hint. Если он не задан (NULL), система подберет адрес на своё усмотрение. Но если значение ненулевое, будет сделана попытка создать сегмент нужного размера с нужным адресом. Именно определяя его значение одинаковым в разных процессах мы и добиваемся вырождения адресов разделяемой памяти. В 32-разрядном режиме найти большой незанятый непрерывный кусок адресного пространства непросто, в 64-разрядном же такой проблемы нет, всегда можно подобрать что-нибудь подходящее.

    Linux

    Здесь принципиально всё то же самое.

      Создаём объект разделяемой памяти

    ftruncate в данном случае используется чтобы задать размер разделяемой памяти. Использование shm_open аналогично созданию файла в /dev/shm/. Есть еще устаревший вариант через shmget\shmat от SysV, где в качестве идентификатора объекта используется ftok (inode от реально существующего файла).
    Чтобы присоединиться к созданной разделяемой памяти

    для создания сегмента

    Здесь также важен hint.

    Ограничения на подсказку

    Что касается подсказки (hint), каковы ограничения на её значение? Вообще-то, есть разные виды ограничений.

    Во-первых, архитектурные/аппаратные. Здесь следует сказать несколько слов о том, как виртуальный адрес превращается в физический. При промахе в кэше TLB, приходится обращаться в древовидную структуру под названием “таблица страниц” (page table). Например, в IA-32 это выглядит так:

    Фиг.2 случай 4K страниц, взято здесь

    Входом в дерево является содержимое регистра CR3, индексы в страницах разных уровней — фрагменты виртуального адреса. В данном случае 32 разряда превращаются в 32 разряда, всё честно.

    В AMD64 картина выглядит немного по-другому.

    Фиг.3 AMD64, 4K страницы, взято отсюда

    В CR3 теперь 40 значимых разрядов вместо 20 ранее, в дереве 4 уровня страниц, физический адрес ограничен 52 разрядами при том, что виртуальный адрес ограничен 48 разрядами.

    И лишь в(начиная с) микроархитектуре Ice Lake(Intel) дозволено использовать 57 разрядов виртуального адреса (и по-прежнему 52 физического) при работе с 5-уровневой таблицей страниц.

    Читайте также:  Не запускаются скачанные файлы windows 10

    До сих пор мы говорили лишь об Intel/AMD. Просто для разнообразия, в архитектуре Aarch64 таблица страниц может быть 3 или 4 уровневой, разрешая использование 39 или 48 разрядов в виртуальном адресе соответственно (1).

    Во вторых, программные ограничения. Microsoft, в частности, налагает (44 разряда до 8.1/Server12, 48 начиная с) таковые на разные варианты ОС исходя из, в том числе, маркетинговых соображений.

    Между прочим, 48 разрядов, это 65 тысяч раз по 4Гб, пожалуй, на таких просторах всегда найдётся уголок, куда можно приткнуться со своим hint-ом.

    Аллокатор разделяемой памяти

    Во первых. Аллокатор должен жить на выделенной разделяемой памяти, размещая все свои внутренние данные там же.

    Во вторых. Мы говорим о средстве межпроцессного общения, любые оптимизации, связанные с использованием TLS неуместны.

    В третьих. Раз задействовано несколько процессов, сам аллокатор может жить очень долго, особую важность принимает уменьшение внешней фрагментации памяти.

    В четвертых. Обращения к ОС за дополнительной памятью недопустимы. Так, dlmalloc, например, выделяет фрагменты относительно большого размера непосредственно через mmap. Да, его можно отучить, завысив порог, но тем не менее.

    В пятых. Стандартные внутрипроцессные средства синхронизации не годятся, требуются либо глобальные с соответствующими издержками, либо что-то, расположенное непосредственно в разделяемой памяти, например, спинлоки. Скажем спасибо когерентному кэшу. В posix на этот случай есть еще безымянные разделяемые семафоры.

    Итого, учитывая всё вышесказанное а так же потому, что под рукой оказался живой аллокатор методом близнецов (любезно предоставленный Александром Артюшиным, слегка переработанный), выбор оказался несложным.

    Описание деталей реализации оставим до лучших времён, сейчас интересен публичный интерфейс:

    Деструктор тривиальный т.к. никаких посторонних ресурсов BuddyAllocator не захватывает.

    Последние приготовления

    Раз всё размещено в разделяемой памяти, у этой памяти должен быть заголовок. Для нашего теста этот заголовок выглядит так:

    • own_addr_ прописывается при создании разделяемой памяти для того, чтобы все, кто присоединяются к ней по имени могли узнать фактический адрес (hint) и пере-подключиться при необходимости
    • вот так хардкодить размеры нехорошо, но для тестов приемлемо
    • вызывать конструктор(ы) должен процесс, создающий разделяемую память, выглядит это так:
  • подключающийся к разделяемой памяти процесс получает всё в готовом виде
  • теперь у нас есть всё что нужно для тестов кроме функций xalloc/xfree

    Похоже, можно начинать.

    Эксперимент

    Сам тест очень прост:

    Curid — это номер процесса/потока, процесс, создавший разделяемую память имеет нулевой curid, но для теста это неважно.
    Qmap, LOCK/UNLOCK для разных тестов разные.

    Проведем несколько тестов

    1. THR_MTX — многопоточное приложение, синхронизация идёт через std::recursive_mutex,
      qmap — глобальная std::map
    2. THR_SPN — многопоточное приложение, синхронизация идёт через спинлок:

    qmap — глобальная std::map
    PRC_SPN — несколько работающих процессов, синхронизация идёт через спинлок:

    qmapglob_header_t::pglob_->q_map_
    PRC_MTX — несколько работающих процессов, синхронизация идёт через именованный мутекс.

    qmapglob_header_t::pglob_->q_map_

    Результаты (тип теста vs. число процессов\потоков):

    1 2 4 8 16
    THR_MTX 1’56’’ 5’41’’ 7’53’’ 51’38’’ 185’49
    THR_SPN 1’26’’ 7’38’’ 25’30’’ 103’29’’ 347’04’’
    PRC_SPN 1’24’’ 7’27’’ 24’02’’ 92’34’’ 322’41’’
    PRC_MTX 4’55’’ 13’01’’ 78’14’’ 133’25’’ 357’21’’

    Эксперимент проводился на двухпроцессорном (48 ядер) компьютере с Xeon® Gold 5118 2.3GHz, Windows Server 2016.

    Итого

    Вдогонку

    Разделяемую память часто используют для передачи больших потоков данных в качестве своеобразной “трубы”, сделанной своими руками. Это отличная идея даже несмотря на необходимость устраивать дорогостоящую синхронизацию между процессами. То, что она не дешевая, мы видели на тесте PRC_MTX, когда работа даже без конкуренции, внутри одного процесса ухудшила производительность в разы.

    Объяснение дороговизны простое, если std::(recursive_)mutex (критическая секция под windows) умеет работать как спинлок, то именованный мутекс — это системный вызов, вход в режим ядра с соответствующими издержками. Кроме того, потеря потоком/процессом контекста исполнения это всегда очень дорого.

    Но раз синхронизация процессов неизбежна, как же нам уменьшить издержки? Ответ давно придуман — буферизация. Синхронизируется не каждый отдельный пакет, а некоторый объем данных — буфер, в который эти данные сериализуются. Если буфер заметно больше размера пакета, то и синхронизироваться приходится заметно реже.

    Удобно смешивать две техники — данные в разделяемой памяти, а через межпроцессный канал данных (ex: петля через localhost) отправляют только относительные указатели (от начала разделяемой памяти). Т.к. указатель обычно меньше пакета данных, удаётся сэкономить на синхронизации.

    А в случае, когда разным процессам доступна разделяемая память по одному виртуальному адресу, можно еще немного добавить производительности.

    • не сериализуем данные для отправки, не десериализуем при получении
    • отправляем через поток честные указатели на объекты, созданные в разделяемой памяти
    • при получении готового (указателя) объекта, пользуемся им, затем удаляем через обычный delete, вся память автоматически освобождается. Это избавляет нас от возни с кольцевым буфером
    • можно даже посылать не указатель, а (минимально возможное — байт со значением “you have mail”) уведомление о факте наличия чего-нибудь в очереди

    Напоследок

    Чего нельзя делать с объектами, сконструированными в разделяемой памяти.

    1. Использовать RTTI. По понятным причинам. Std::type_info объекта существует вне разделяемой памяти и недоступен в разных процессах.
    2. Использовать виртуальные методы. По той же причине. Таблицы виртуальных функций и сами функции недоступны в разных процессах.
    3. Если говорить об STL, все исполняемые файлы процессов, разделяющих память, должны быть скомпилированы одним компилятором с одними настройками да и сама STL должна быть одинаковой.

    PS: спасибо Александру Артюшину и Дмитрию Иптышеву (Dmitria) за помощь в подготовке данной статьи.

    UPD: исходники BuddyAllocator выложены здесь под BSD лицензией.

    Источник

  • Оцените статью