Я не знаю точно о 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 *
не будет работать правильно с указателями на разделяемую память.