Linux — общая память для обмена данными между процессами

Оглавление

I. Ссылаясь на вышеизложенное

2. Общая память

1. Определение

2. Особенности:

3. Этапы реализации:

Ниже приведены полные шаги для успешного связывания прав на использование общей памяти:

4. Введение в функцию

        4.1 функция shmget

        4.1.2 Введение параметров       

        4.2фток функция:

        4.2.1 Введение параметров

                Эксперимент с кодом функции ftok(); shmget();:

        Код работает: 

                Приведу пример из жизни:

                результат операции:

                Итак, есть два пути решения: 

               4.3shmctl();—— используется для удаления общей памяти

               Введение параметра:

               возвращаемое значение:

              Демонстрация кода: 

    Следующий шаг — это третий шаг: связывание «оков» между каждым процессом и общим пространством памяти!

            4.4shmat();——Получить адрес области общей памяти

         Введение параметра:

        Демонстрация кода: 

        результат операции:

         4.6 shmdt();—— Отменить ассоциацию с общим пространством памяти

         Демонстрация кода:

        Последнее - это передача данных между двумя сторонами!

 2. Окончательная карта кода:

        Связь.hpp:

        Север.cc:

        Клиент.cc: 

        результат операции:

3. Резюме:

По сравнению с общей памятью и конвейерами, когда два процесса обмениваются данными между процессами, сколько раз им нужно копировать данные?

Недостатки общей памяти:


I. Ссылаясь на вышеизложенное

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

        1. Конвейер — это способ связи процессов, представляющий собой полудуплексный способ связи, то есть данные односторонние (один процесс читает, другой процесс пишет).

        2. Каналы делятся на анонимные каналы и именованные каналы.

        3. Анонимные каналы используются для того, чтобы связанные с кровью процессы родитель-потомок могли выполнять специализированную передачу данных.Функция канала используется для создания канала, а функция fork используется для создания дочернего процесса для начала обмена данными. Но анонимный канал должен закрыть соответствующий файловый дескриптор, родительский процесс доступен либо только для чтения, либо только для записи, а дочерний процесс такой же.

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

        

        Помимо конвейеров необходимо изучить еще один способ передачи данных: System V. Наиболее распространенным тестом связи System V является разделяемая память, поэтому далее мы сосредоточимся на разделяемой памяти.

         На рисунке выше эти два процесса независимы.В нижней части системы все они имеют свою собственную структуру данных ядра (struct_task), называемую PCB, а также имеют собственное адресное пространство процесса (mm_struct), хотя их код данные все в памяти., но они не мешают друг другу, поэтому они независимы.

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

2. Общая память

1. Определение

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

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

2. Особенности:

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

3. Этапы реализации:

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

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

Ниже приведены полные шаги для успешного связывания прав на использование общей памяти:

4. Введение в функцию

         4.1 функция shmget

        Функция shmget создает пространство разделяемой памяти и возвращает идентификатор разделяемой памяти.При успешном завершении shmget() должна возвращать неотрицательное целое число, идентификатор разделяемой памяти: в противном случае она должна возвращать -1 и устанавливать Errno, чтобы указать на ошибку.

        4.1.2 Введение параметров
       

        Параметры функции Shmget, как указано выше: три параметра, второй — для установки размера пространства общей памяти, а третий параметр — бит флага, который аналогичен параметрам определения макроса, таким как O_RDONLYO и O_CTREAT функции открытия.

        На рисунке ниже показаны варианты по умолчанию третьего параметра, их два: IPC_CREAT, IPC_EXCL

 

IPC_CREAT: если нет области общей памяти, создайте область общей памяти; если область общей памяти существует, она получит ее.

IPC_EXCL: нельзя использовать отдельно, необходимо использовать с IPC_CREAT.

Комбинированный эффект: если пространство не существует, оно будет создано, если пространство существует, будет возвращена ошибка .

        Первый параметр key_t key — это возвращаемое значение функции ftok, поэтому, если вы хотите использовать ключ, вы должны сначала получить возвращаемое значение функции ftok.

        4.2фток функция:

         4.2.1 Введение параметров

        Функция ftok имеет два параметра.Первый параметр такой же, как и первый параметр функции открытия, и оба должны указывать имя файла пути;идентификатор второго параметра, его диапазон значений составляет значение между 0-255, в этом диапазоне Вы можете взять его по желанию.

        После успешного использования функции ftok возвращается значение ключа типа key_t и -1, если функция не может быть использована и возникает ошибка.

Эксперимент с кодом функции ftok(); shmget();:

        Примечание. В этом эксперименте два файла .cc используются для имитации двух процессов, а один заголовочный файл используется для инкапсуляции функций ftok и shmget:

Код работает: 

        В результате было обнаружено, что адреса ключей двух процессов совпадают, потому что параметры функции ftok одинаковы, когда два процесса вызывают getKey(), поэтому им присваивается одно и то же значение ключа.

        Поскольку два процесса имеют одно и то же значение ключа, это заложило прочную основу для того, чтобы они могли видеть одно и то же пространство общей памяти, а затем создавать пространство общей памяти. Здесь я использую процесс Sever для его создания. Это Нет предела, любой процесс может его создать. Но так как один процесс создан, другой процесс может напрямую получить общее пространство, нет необходимости создавать еще один, предшественники посадили деревья (процесс Север), а будущие поколения могут наслаждаться тенью (процесс Клиент). 

Приведу пример из жизни:

        Я пригласил друга в ресторан на ужин, а также забронировал в ресторане пустую отдельную комнату в зале 3. После того, как все было готово, я отправил информацию о местоположении своему другу через WeChat, попросив его прийти в xx Hotel на ужин в Комната 3. После того, как друг получил сообщение, он пришел в отель в назначенное время. Ему нужно было найти приватную комнату, поэтому он сказал официанту о приватной комнате. Официант провел его в приватную комнату и нашел меня. После этого мы начали поесть и поболтать.


        В этом примере я подобен серверному процессу, я открыл пространство общей памяти, которое принадлежит нам обоим, я отправляю информацию о местонахождении ему (клиентский процесс), и он получает (getShm()) информацию о местоположении в отдельной комнате ресторана. , он показывал официанту конкретную информацию о отдельной комнате (значение ключа), и официант проводил его к двери отдельной комнаты (общая память), и он (система ОС) сравнивал ключ клиент с ключом от отдельной комнаты, если они одинаковы, клиент может войти в отдельную комнату.
Ключ — это уникальный идентификатор пространства общей памяти, распознаваемый системой.Когда процесс А создает пространство общей памяти, другой процесс Б может войти в то же самое пространство общей памяти, если у него есть тот же ключ, что и у процесса А.

        Ключ используется для указания того, что вы хотите выполнить shmget, и установить его в атрибут общей памяти!

результат операции:

         Как показано на рисунке выше: shmid обоих процессов равен 1, что указывает на то, что серверный процесс успешно создал пространство общей памяти, а клиентский процесс также успешно получил пространство памяти, созданное сервером, тогда шаг 1 завершается. завершено! Пришло время перейти к шагу 2: пусть каждый процесс выполняет отображение таблицы страниц и подключается к общему пространству памяти.


        Здесь следует отметить одну вещь: при запуске процесса сервера во второй раз, поскольку разделяемая память была успешно создана в первый раз, второй запуск сообщит об ошибке о возвращаемом значении функции shmget. Ошибка указывает на «файл уже существует!», так что мы не можем Это вызвано третьим параметром IPC_CREAT | IPC_EXCL функции shmget, потому что жизненный цикл успешно созданного shmid в первый раз следует за системой ОС, а не за процессом — грубо говоря, значение shmid не последует. Оно уничтожается в конце процесса и исчезнет только тогда, когда мы выключим систему Linux.

        Итак, есть два пути решения: 

1. Просто выключите систему Linux и перезапустите ее, чтобы запустить ./server;

2. Воспользуйтесь командой: ipcrm -m shmid (введите ее значение);

Команда ipcs предназначена для отображения панели свойств пространства общей памяти, очереди сообщений и семафора:

         Из рисунка выше видно, что эта команда отображает адрес ключа, значение shmid и значение владельца (owner) пространства общей памяти. Остальные атрибуты будут рассмотрены позже!

        Используйте команду ipcrm -m shmid, чтобы удалить значение shmid, которое было успешно запущено в первый раз, а затем снова запустите процесс Sever для успешного выполнения. Но после удаления значения shmid пространство общей памяти, созданное в первый раз, также будет соответственно удалено, потому что значение shmid, созданное во второй раз, равно 32769, что отличается от первого 32768.

                  На самом деле разделяемая память == блок физической памяти (разделяемое пространство памяти хранится в физической памяти) + соответствующие атрибуты разделяемой памяти!!

        Например, когда мы изучаем язык C, мы используем malloc для создания пространства кучи для динамического хранения Например, мы используем указатели, чтобы открыть 1024 байта пространства, и когда мы освободимся, система переработает указатели. адрес, а указатель указывает только на первый адрес этого адреса, как он узнает, насколько велико это адресное пространство и сколько байтов пространства нужно освободить? Как реализуется конкретный принцип?


        Потому что нам нужно открыть пространство в 1024 байта, и ЦП откроет для него пространство больше 1024 байт, и может открыть 1034 байта, а дополнительные 10 байтов пространства будут хранить соответствующую информацию об атрибутах этого Например, начальный адрес этого пространства, размер байтов в конце и время создания... Когда ЦП выполняет функцию освобождения, он обращается к соответствующим атрибутам этого пространства кучи, чтобы точно освободить пространство. !!!

       Таким образом, принцип инструкции, которую мы используем, ipcrm -m shmid заключается в том, что система освобождает пространство общей памяти через соответствующий атрибут shmid пространства общей памяти. Важность shmid эквивалентна важности pid в процессе.


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

4.3shmctl();—— используется для удаления общей памяти

        Введение параметра:

1. shmid — это идентификатор общего хранилища, возвращаемый функцией shmget
2. Параметр cmd — это параметр определения макроса, всего их три:

        IPC_RMID: обычно используется для удаления разделяемой памяти;

        IPC_STAT:: Получить состояние разделяемой памяти, скопировать структуру shmid ds разделяемой памяти в bu;

        IPC SET: изменить состояние разделяемой памяти и скопировать uid, gid и режим из структуры shmid ds, на которую указывает bu, в структуру shmid ds разделяемой памяти. (Ядро поддерживает структуру для каждого сегмента разделяемой памяти, эта структура называется shmid ds, в которой хранятся некоторые параметры, такие как размер разделяемой памяти, pid, время хранения и т. д.) 3. buf — это структура shmid ds
, и обычно заполняют nullptr.


        возвращаемое значение:

                Возвращает 0 в случае успеха: возвращает ошибку в случае неудачи

Демонстрация кода: 

Следующий шаг — это третий шаг: связывание «оков» между каждым процессом и общим пространством памяти!


4.4shmat();——Получить адрес области общей памяти

 Введение параметра:

У этой функции три параметра:
        первый — возвращаемое значение shmid функции shmget;

        Второе предложение параметра — использовать nullptr. Если NULL, разделяемая память будет подключена к подходящему виртуальному адресному пространству. Not NULL: система выделит соответствующий адрес в соответствии с параметрами и выравниванием границ адреса;

        Третий параметр — это параметр определения макроса, если он не указан, значение по умолчанию равно 0 .

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

Демонстрация кода: 

        Анализ кода: поскольку start является указателем, размер указателя под Linux64 составляет 8 байт, поэтому я хочу судить, равно ли -1 значение, преобразованное из указателя (8 байт) в целое число int (4 байта) ( оценивая функцию shmat, успешно ли она возвращается), ее нельзя преобразовать в 4 байта при принудительном преобразовании, но найти длинное длинное целое того же размера, что и 8-байтовое целое, поэтому помните: if((int) start==-1 )) это неправильно!

результат операции:

    


4.6 shmdt();—— Отменить ассоциацию с общим пространством памяти

 Демонстрация кода:

За исключением записи и чтения данных с обеих сторон процесса, все приготовления были выполнены, и ниже приводится сводка:

1. key_t Create_Key();—— используется для получения ключа возвращаемого значения ftok;

2. int Create_Shm(key_t k);— используется для создания разделяемой памяти;

3. int Get_Shm(key_t k);— используется для другого процесса для получения пространства общей памяти
4. void* attach_Shm(int shmid);— используется для каждого процесса для получения адреса пространства общей памяти и связывания их ;
5 , void Dattach_Shm(void* start); -- используется, чтобы позволить каждому процессу отменить ассоциацию с пространством общей памяти;
6, void void Del_Shm(int shmid) -- используется, чтобы разрешить процессу A, создавшему пространство общей памяти. освободить пространство;


Последнее - это передача данных между двумя сторонами!

        Обмен данными очень прост, то есть один записывает содержимое в общую область памяти, а другой читает и печатает содержимое из общей области. Указатель start в коде указывает на открытое пространство общей памяти, просто рассматривайте start как буфер.

        Существует два способа написания контента: один — написать строку заранее, просто передать ее, как указано выше, другой — ввести и использовать немедленно, как показано ниже:


 2. Окончательная карта кода:

        Связь.hpp:

#include<cstdio>
#include<iostream>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstring>
#include<unistd.h>
#include<cerrno>

#define NAME_ "./tmp/"
#define ID 0x62
#define SIZE 4096

key_t Creat_Key(){
    key_t k=ftok(NAME_,ID);
    if(k<0){
        std::cerr<<"errno:"<<strerror(errno)<<std::endl;
        exit(-1);
    }
    //创建成功
    return k;
}

int Shmet(key_t k,int flags){
    int shmid=shmget(k,SIZE,flags);
    if(shmid<0){
         std::cerr<<"errno:"<<strerror(errno)<<std::endl;
        exit(-2);
    }
    return shmid;
}


int Creat_Shm(key_t k){
    return Shmet(k,IPC_CREAT |IPC_EXCL|0600);
}

int Get_Shm(key_t k){
    return Shmet(k,IPC_CREAT);
}


//关联
void* attach_Shm(int shmid){
    void* start=shmat(shmid,nullptr,0);
    if((long long)start==-1L){
            std::cerr<<"errno:"<<strerror(errno)<<std::endl;
        exit(-4);
    }
    //关联成功
    printf("related successily!\n");
    return start;
}

//去关联
void Dattach_Shm(void* start){
    if(shmdt(start)==-1){
        std::cerr<<"errno:"<<strerror(errno)<<std::endl;
        exit(-5);
    }
    //去关联成功
    printf(" abondon successily !\n");
}


void Del_Shm(int shmid){
    if(shmctl(shmid,IPC_RMID,nullptr)==-1){
           std::cerr<<"errno:"<<strerror(errno)<<std::endl;
        exit(-3);
    }
    //删除成功
    printf("删除共享内存空间\n");
}

        Север.cc:

#include "Comm.hpp"

int main(){
    key_t k=Creat_Key();
    printf("key:%u\n",k);

    int shmid=Creat_Shm(k);
    printf("shmid:%d\n",shmid);

    //关联
    char* start=(char*)attach_Shm(shmid);
    printf("start:%p\n",start);

    //数据通信
    while(true){
        printf(" Client says:%s\n",start);
        sleep(1);
    }

    Dattach_Shm(start);

    Del_Shm(shmid);
    return 0;
}

        Клиент.cc: 

#include "Comm.hpp"

int main(){
    int k=Creat_Key();
    printf("key:%u\n",k);

    int shmid=Get_Shm(k);
    printf("shmid:%d\n",shmid);

//关联
     sleep(1);
    char* start=(char*)shmat(shmid,nullptr,0);
    printf("start:%p\n",start);

    int cnt=0;
    const char* s="我是另一个进程,我正在给Sever发消息!";
    while(true){
       snprintf(start,SIZE,"%s:pid:[%d] cnt:[%d]",s,getpid(),cnt++); 
        sleep(1);
    }  
    
    //去关联
    Dattach_Shm(start);

    return 0;
}

 результат операции:

3. Резюме:

Преимущества разделяемой памяти: Самая быстрая скорость передачи
Почему самый быстрый процесс передачи с использованием разделяемой памяти?

        Причина в том, что этим двум процессам не нужно создавать собственные буферы. Одна за другой информация напрямую!

По сравнению с общей памятью и конвейерами, когда два процесса обмениваются данными между процессами, сколько раз им нужно копировать данные?

        Сначала посмотрите на копию данных двух процессов в конвейере: (Примечание: на следующем рисунке не рассматриваются потоки stdin и stdout в C/C++)

        На приведенном выше рисунке: процесс записи испытал 4 копии данных от записи данных в буфер C до конвейера, а затем их вывода из конвейера в буфер до процесса чтения.

(Примечание. На рисунке ниже рассматриваются потоки стандартного ввода и потока стандартного вывода в C/C++)

        Из рисунка выше: после добавления входного и выходного потоков данные претерпели в общей сложности 4+2=6 копий.


        Далее, давайте взглянем на копию данных двух процессов в пространстве общей памяти: по-прежнему игнорируем потоки stdin и stdout C

  На рисунке выше данные, записываемые процессом записи, поступают непосредственно в пространство разделяемой памяти, а затем выносятся из этого пространства в процесс чтения, всего 2 копии данных. 

         Из рисунка выше: после добавления входного и выходного потоков данные претерпели в общей сложности 2 + 2 = 4 копии.

       

Недостатки общей памяти:

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

        Проще говоря, использование разделяемой памяти может привести к тому, что процесс B будет читать некоторые данные, в то время как процесс A записывает данные. На этот раз процесс A не полностью записал данные, и процесс B ушел, взяв половину данных, в результате чего метод связи не обеспечивает безопасность данных.


 

 

результат операции:

 

Guess you like

Origin blog.csdn.net/weixin_69283129/article/details/131448849