#c #linux #pthreads #shared-memory
#c #linux #pthreads #разделяемая память
Вопрос:
Я пишу программу, которая передаст 50 целых чисел из одного потока в другой, используя общую память, и после получения целых чисел поток получателя распечатает их.
Код компилируется без каких-либо ошибок или предупреждений, но когда я запускаю его, я получаю следующий вывод:
pthread_create() for thread 1 returns: 0
pthread_create() for thread 2 returns: 0
Segmentation fault (core dumped)
Вот мой код на данный момент:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/msg.h>
#include <string.h>
//define declarations
#define SIZE 50
//function declarations
void *send_data();
void *receive_data();
int main()
{
pthread_t thread1;
pthread_t thread2;
int ret_val_t1;
int ret_val_t2;
//create thread1
ret_val_t1 = pthread_create( amp;thread1, NULL, send_data, NULL);
if(ret_val_t1)
{
fprintf(stderr,"Error - pthread_create() return value: %dn",ret_val_t1);
exit(EXIT_FAILURE);
}
//create thread2
ret_val_t2 = pthread_create( amp;thread2, NULL, receive_data, NULL);
if(ret_val_t2)
{
fprintf(stderr,"Error - pthread_create() return value: %dn",ret_val_t2);
exit(EXIT_FAILURE);
}
printf("pthread_create() for thread 1 returns: %dn",ret_val_t1);
printf("pthread_create() for thread 2 returns: %dn",ret_val_t2);
//wait untill threads are done with their routines before continuing with main thread
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
exit(EXIT_SUCCESS);
}
void *send_data(){
int shmid;
int *array;
int i = 0;
int SizeMem;
key_t key = 12345;
SizeMem = sizeof(*array)*SIZE;
shmid = shmget(key, SIZE*sizeof(int), IPC_CREAT);
array = (int *)shmat(shmid, 0, 0);
for(i=0; i<SIZE; i )
{
array[i] = i;
}
for(i=0; i<SIZE; i )
{
printf("n%d---n", array[i]);
}
printf("nWritting to memory succesful--n");
shmdt((void *) array);
}
void *receive_data(){
int shmid;
int *array;
int i = 0;
key_t key = 12345;
shmid = shmget(key, SIZE*sizeof(int), IPC_EXCL);
array = shmat(shmid, 0, SHM_RDONLY);
for(i=0; i<SIZE; i )
{
printf("n%d---n", array[i]);
}
printf("nRead to memory succesful--n");
shmdt((void *) array);
}
Комментарии:
1. Зачем вам использовать общую память SysV для передачи данных между потоками одного и того же процесса, мне непонятно.
2. Мне было интересно то же самое, что и @EOF. Это просто упражнение по использованию общей памяти? Я, по общему признанию, мало что знаю об общей памяти, но я думал, что она использовалась для обмена данными между различными процессами
3. @EOF amp; yano Это всего лишь упражнение.
4. @Nikolaj Несмотря на это, я бы не советовал использовать разделяемую память SysV. Эквивалентные возможности POSIX намного более разумны и применимы.
5. В этом случае я бы изменил упражнение (если у вас есть возможность) на настройку общей памяти между двумя процессами.. просто не думаю, что когда-нибудь вы бы так делили память между потоками в одном процессе.
Ответ №1:
Воспользуйтесь ipcs
утилитой и посмотрите на результат из ipcs -m
Согласно справочной странице shmget:
КРАТКОЕ ОПИСАНИЕ top
#include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg);
…
В дополнение к вышеупомянутым флагам, 9 младших разрядов shmflg определяют разрешения, предоставленные владельцу, группе и другим. Эти биты имеют тот же формат и то же значение, что и аргумент mode в open(2). В настоящее время разрешения на выполнение не используются системой.
В строке кода
shmid = shmget(key, SIZE*sizeof(int), IPC_CREAT);
«9 младших значащих бит shmflg» установлены равными нулю.
Ни у кого нет прав на чтение / запись вашего сегмента общей памяти.
Это было бы лучше:
shmid = shmget(key, SIZE*sizeof(int), IPC_CREAT | 0600 );
Это дало бы пользователю-владельцу разрешение на чтение / запись.
Комментарии:
1. @Nikolaj Каков результат
ipcs -m
? Вы также не проверяете возвращаемые значения на наличие ошибок. Поскольку у вас нет доступа на чтение или запись, вашshmat()
вызов вполне может завершиться ошибкой.2.
ipcs -m
вывод показал, что общая память не выделена.3. Это не имеет большого значения, поскольку у создателя всегда есть доступ для записи к ресурсу. Это первый метод блокировки, реализованный в unix (и единственный до версии 7) Поскольку сегмент памяти создается в процессе и используется только внутри, доступ на запись гарантирован, даже если mod mask установлен в
0
.4. Отсутствие выделенной общей памяти, вероятно, является результатом того, что
SIGSEGV
запускался до создания общей памяти.5. @Nikolaj Если
ipcs -m
не отображаются какие-либо сегменты общей памяти, это означает, что ваш вызовshmget()
завершается неудачей, поскольку ваш код никогда не удаляет какой-либо сегмент общей памяти.
Ответ №2:
В этой ситуации разделяемая память является дорогостоящим делом. И вы пытаетесь получить доступ из того же процесса, здесь нет никакой пользы.
Лучший способ справиться с вашей ситуацией — это:
Потоки родительского процесса будут иметь доступ к кучной памяти родительского процесса. Это простая концепция в использовании. Создайте объект кучи для родительского процесса и предоставьте общий доступ к своим потокам.
Как сказано во многих комментариях, разделяемая память является одной из концепций «межпроцессной коммуникации», такой как каналы или именованные каналы или очередь сообщений и т.д….
Если вы используете для практики, то все в порядке. Но, помимо использования PROD, это дорогостоящее дело.
Если вы не применяете блокировки и ограничение доступа к вашей подключенной / совместно используемой памяти должным образом, и вы не находитесь за защищенным брандмауэром, то вы приветствуете злоумышленников.
Комментарии:
1. Я использую это просто как упражнение.
2. Это нормально. Я добавил это в информационных целях 🙂
Ответ №3:
Одно условие гонки возникает из вашего кода. Ваш второй поток может сначала получить доступ к процессору и попытаться использовать еще не созданный сегмент памяти. Поскольку он не существует, и поскольку вы не проверяете наличие ошибок, вы можете получить SIGSEGV
от попытки доступа к памяти по NULL
адресу.
Вы должны установить устройства синхронизации, которые запрещают второму потоку получать доступ к общей памяти до того, как она была создана и заполнена данными. Что-то вроде.
pthread_mutex_t lock;
pthread_cond_t shm_created_and_filled;
затем в main
вы инициализируете lock
и shm_created_and_filled
.
в потоке записи вы создадите сегмент разделяемой памяти и запишете числа, наконец, заблокируя и сигнализируя shm_created_and_filled
условную переменную. В потоке чтения вы сначала блокируете lock
переменную, а затем ожидаете shm_created_and_filled
условия. Все еще существует небольшое условие гонки, при котором первый поток запускается и сигнализирует о shm_created_and_filled
условии до того, как поток чтения ожидает его (он потеряет условие), но я оставил его в качестве упражнения для читателя.