Тип указателя на разделяемую память POSIX - PullRequest
1 голос
/ 24 ноября 2011

Мой оригинальный вопрос здесь , который, кажется, никого не интересует.

Я решил разбить этот утомительный вопрос и задать следующее:

char* shared_memory;
shared_memory = (char*) shmat (segment_id, 0, 0);

Получаем ли мы обычно указатель на разделяемую память, как в примере выше?Другими словами, должны ли мы всегда приводить указатель к char* или к чему-то, что лучше соответствует нашим потребностям?

Ответы [ 3 ]

4 голосов
/ 25 ноября 2011

Я не знаю точно о shmat, но у меня есть некоторый опыт работы с эквивалентом WinAPI ( MapViewOfFile , поэтому я дам более общий ответ.

Поскольку вы связали два своих вопроса вместе, а другой вопрос касается объектов C ++ в разделяемой памяти, я рассмотрю здесь случаи как C, так и C ++. Я приглашаю вас добавить тег [c ++] к вашему вопросу.

Память и общая память

Каким бы ни был API, вы в итоге получите void *, потому что это то, что есть: адрес какой-то зоны памяти (совместно используемой или нет).

Этот «API распределения» (либо malloc, shmat, MapViewOfFile и т. Д.) Не имеет ни малейшего представления, что вы будете делать с этой памятью, какие абстракции вы будете использовать (структуры C, объекты C ++, даже макросы C или встроенные в типах), поэтому единственное, что может сделать API - это дать вам:

  • адрес, таким образом, void *
  • выравнивание (обычно что-то вроде «этот адрес будет выравниваться для всего, что вы можете придумать»). Вы должны обратиться к документации API, чтобы получить эту информацию. В редких случаях гарантия выравнивания не предоставляется, поэтому вы должны использовать только выровненное подмножество этой памяти (это для другого вопроса)

Проблема в том, что вы будете делать с этой памятью?

Нет способа получить доступ к содержимому этой памяти через void *, потому что вы не можете разыменовать void * (это, в конце концов, указатель на void ...). A void * содержит только адрес. Ни больше, ни меньше.

Абстракция char

Первая абстракция, которую вы найдете, самая простая - это char абстракция. В C и C ++ нет типа byte, а роль char (или unsigned char) играет роль. Итак, если вы хотите получить доступ к этой памяти в виде массива байтов, вы приведете возврат этой памяти в char:

/* C code */
char * pc = p ; /* p being the void * pointer */

// C++ code
char * pc = static_cast<char *>(p) ; // p being the void * pointer

Абстракция struct 1042 * Вторая абстракция предполагает, что разделяемая память является структурой (или, возможно, даже массивом структуры), поэтому вы должны привести указатель в указатель на эту структуру: /* C code */ typedef struct S { /* etc. */ } S ; S * ps = p ; /* p being the void * pointer */ // C++ code struct S { /* etc. */ } ; S * ps = static_cast<S *>(p) ; // p being the void * pointer Таким образом, вы можете получить доступ к этой общей памяти через структуру. параллелизм

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

Об общей памяти и указателях

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

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

/* C code */

/* process A */
void * p = getSharedMemory("ABCD") ;
/* p could have the value 0x1234 */

/* process B */
void * p = getSharedMemory("ABCD") ;
/* p could have the value 0x56789 */

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

То, что вы можете поместить в общую память, это индексы. Например, вы можете иметь структуру:

/* C code */
typedef struct S
{
   size_t index ;
   double value[1000] ;
} S ;

С помощью этой структуры вы можете установить значение 3.1415 в массиве value с индексом 42:

/* C code - process A */
S * s = p ; /* p being the pointer to the shared memory */
s->index = 42 ;
s->value[42] = 3.1415 ;

А затем получить его в другом процессе:

/* C code - process B */
S * s = p ; /* p being the pointer to the shared memory */
size_t index = s->index ;
double value = s->value[index] ;

О C и C ++

Эта проблема с общей памятью связана с API, а не с C или C ++.

В своем оригинальном вопросе вы упомянули объекты C ++ в разделяемой памяти, поэтому я подробно опишу некоторые различия в C и C ++, о которых сообщалось, несмотря на то, что они выходили за рамки истинной области вашего вопроса.

C бросает против C ++ бросает

В C допустимо неявно приводить любой указатель void * к любому указателю T *. В C ++ вам нужно статическое приведение для этого:

/* valid C code */
T * t = p ;        /* p being a void * pointer */
T * t = (T *) p ;  /* useless but authorized cast */

// valid C++ code ;
T * t = static_cast<T *>(p) ; // p being a void * pointer
T * t = (T *) p ;             // considered bad style for multiple reasons

Таким образом, обычно для создания C / C ++-совместимого кода большинство людей будет использовать приведение в стиле C, которое является общим для двух языков, с неизменными замечаниями языковых юристов (я виновен в этом).

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

Помещение объекта C ++ в общую память

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

Это верно для C и C ++: в тот момент, когда у вас есть указатель вместо относительного индекса в разделяемой памяти, у вас возникает вероятная проблема (т.е. возможная ошибка).

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

C ++ объекты предлагают сильную абстракцию, что означает, что они просты и безопасны в использовании (при использовании std::string или std::vector<std::string> objects нет риска утечек памяти, несмотря на объем выделенного объема памяти, например). Но эта сильная абстракция скрывает только тот факт, что внутри у вас все еще есть указатели ...

Вторая трудность с объектами C ++ в разделяемой памяти заключается в том, что создание и уничтожение должны обрабатываться вручную (с помощью размещения нового и явного вызова деструктора).

Заключение: Если вы не знаете, что используемый вами объект может с ним справиться, и вы правильно использовали этот объект, написав следующее приведение:

// C++ code
struct MyObject { /* constructors, destructors, etc. */ }  ;

MyObject * myObject = static_cast<MyObject*>(p) ; // p being void *

не будет работать правильно с указателями на разделяемую память.

4 голосов
/ 24 ноября 2011

Да. Несмотря на то, что актерский состав излишен в C,

char *shared_memory = shmat(segment_id, 0, 0);

В наши дни обычно предпочитают использовать более новые объекты совместно используемой памяти (shm_open / mmap).

Вы можете поместить любой тип данных в сегмент общей памяти,

struct s { int x; char s[16]; float z; };
struct s *shared_memory = shmat(segment_id, 0, 0);

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

1 голос
/ 24 ноября 2011

shmat, как и malloc, возвращает void *, который может быть приведен (лучше неявно) к любому другому типу указателя.

В общем случае указатель void * нельзя безопасно привести к любомудругой тип указателя из-за проблем с выравниванием, но если он уже выровнен по достаточно большой границе (как в случае с возвращенными указателями на shmat, malloc, mmap и т. д.), вы можете безопасно привести его к другомууказатель типа (даже неявно) и безопасно разыменовывать его.

...