Я делаю это правильно? Отображение указателя в структуре на положение вне структуры для использования общей памяти IPC
Отказ от ответственности: я - ноб из C, работаю над проектом по проведению IPC с использованием сегмента общей памяти. Мой план состоит в том, чтобы определить структуру, которая имеет указатель (void *
) к оставшейся памяти, которая отображается (через smget()
) который находится за пределами структуры, которую я буду использовать в качестве заголовка для передачи информации о состоянии запроса между процессами (он также будет иметь структуры mutex/cond).
Я просто хотел посмотреть, правильно ли я это делаю... мои вопросы в комментариях к моей основной функции.
Дайте мне знать, если мне что-то неясно, опять же, я довольно новичок в этом.
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <string.h>
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_error(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define PATHNAME "/tmp"
typedef struct shm_data
{
/* segment id assigned by shmget() */
int segment_id;
/* max size of char *data */
unsigned int buffer_size;
/* nbytes currently in char *data to be read */
unsigned int nbytes_buffer;
/* nbytes remaining to be sent to be read */
unsigned int nbytes_remaining;
/* nbytes total that need to be read */
unsigned int nbytes_total;
/* pointer to the memory poistion just outside of the struct */
char *buffer;
} shm_data;
shm_data *create_shm(unsigned int segment_number, unsigned int segment_size)
{
int segment_id;
shm_data *shm;
// just doing segment_size + 1 for this example so when I print data it has a '\0' for output
segment_id = shmget(ftok(PATHNAME, segment_number), segment_size + 1, IPC_CREAT | S_IRUSR | S_IWUSR);
void *shm_addr = shmat(segment_id, (void *) 0, 0);
shm = (shm_data *) shm_addr;
shm->segment_id = segment_id;
shm->buffer_size = segment_size - sizeof(shm_data);
shm->nbytes_buffer = 0;
shm->nbytes_remaining = 0;
shm->nbytes_total = 0;
// 1 - am I initializing my pointer correctly? I want it to point to the first byte that comes after my struct
shm->buffer = shm_addr + sizeof(shm_data) + 1;
memset(&shm->buffer[0], '\0', shm->buffer_size);
return shm;
}
int main(int argc, char *argv[])
{
char *data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
unsigned int segment_size = 16;
shm_data *shm = create_shm(1, sizeof(shm_data) + segment_size);
shm->nbytes_total = strlen(data);
shm->nbytes_remaining = shm->nbytes_total;
int count = 0;
while (shm->nbytes_remaining > 0)
{
// 2 - is this an appropriate way to "clear" the memory outside of the struct?
memset(&shm->buffer[0], '\0', shm->buffer_size + 1);
int nbytes = shm->nbytes_remaining;
if (nbytes > shm->buffer_size)
{
// max we can buffer is this buffer_size
nbytes = shm->buffer_size;
}
// 3 - is this an appropriate way to copy a segment of data with an offset into the memory outside of the struct?
int offset = count * shm->buffer_size;
memcpy(shm->buffer, &data[0] + offset, nbytes);
log_info("[%d] %s", nbytes, shm->buffer);
shm->nbytes_remaining = shm->nbytes_remaining - nbytes;
count++;
}
return 0;
}
1 ответ
Да, это не сработает. Нет гарантии, что адрес, на который один процесс отображает общую память, совпадает с адресом, который видит другой процесс.
Правила, которые нужно запомнить:
- Не используйте указатели. Используйте смещения, если необходимо. (Хотя в этом случае каждый процесс должен иметь свою собственную частную структуру с указателями на различные части блока общей памяти.)
- Вы не можете использовать указатели в блоке общей памяти, чтобы получить доступ к не общей (нормальной) памяти другого процесса.
Кроме того, я сомневаюсь, что есть какая-либо гарантия того, что идентификатор сегмента будет одинаковым в обоих процессах. Наконец, нет смысла хранить информацию о том, как получить доступ к блоку общей памяти внутри самого блока общей памяти; это круговые рассуждения.
Пример настройки блока управления:
shm_data *create_shm(unsigned int segment_number, unsigned int segment_size)
{
shm_data *shm;
shm = (shm_data*)malloc(sizeof(shm_data));
if(shm == NULL) return shm;
// The following should have more error-trapping code.
shm->segment_id = shmget(ftok(PATHNAME, segment_number),
segment_size, IPC_CREAT | S_IRUSR | S_IWUSR);
shm->buffer_size = segment_size;
shm->data = shmat(shm->segment_id, (void *) 0, 0);
memset(shm->data, 0, shm->buffer_size);
return shm;
}
Также не скрывайте лишние байты завершения в этом коде. Если вызывающий объект должен поместить туда строку, он должен убедиться, что он запрашивает место для байта терминатора. Этот код не должен предполагать, что блок совместно используемой памяти содержит строку. Типично конвертировать shm->data
указатель на другой вид struct
который просто описывает внутреннюю часть разделяемой памяти (и, таким образом, объявляет хранилище для строки как массив символов). Тогда вы можете пройти sizeof(struct)
как segment_size
в эту функцию. Это может быть лучше, чтобы изменить data
к void*
в этом случае.
Хорошо, я вижу, что вы отредактировали вопрос, чтобы превратить общую память в буфер символов. Я все еще думаю, что было бы лучше, если бы вы отделили код управления общей памятью от кода буфера.
Я также рекомендую вам определять переменные буфера в разделяемой памяти с точки зрения смещения чтения и смещения записи. Эти смещения должны быть в типе данных (unsigned int
?) к которому обращаются атомарные операции.
На уровне ОС нет контроля над порядком доступа каждого процесса к памяти; совершенно нормально, когда процесс блокируется в середине чего-либо, и тогда другой процесс будет видеть состояние "полусыры".
Так что либо используйте атомарные типы, и настройте свой код так, чтобы изменение одной атомарной переменной полностью изменяло состояние от одного к другому, или вам нужно будет использовать другой механизм IPC, такой как мьютексы или семафоры, для управления доступом к общей памяти,