Форум программистов, компьютерный форум, киберфорум
C/С++ под Linux
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.89/18: Рейтинг темы: голосов - 18, средняя оценка - 4.89
14 / 7 / 2
Регистрация: 25.03.2014
Сообщений: 90
1

Разделяемая память POSIX IPC как узнать, что память выделена и её уже можо использовать?

14.06.2016, 13:31. Показов 3756. Ответов 10
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
1. Описание проблемы:
Две программы. Одна пишет в разделяемую память, другая читает. Из-под читающей программы read невозможно понять высвобождена разделяемая память или она есть и её можно читать. Если память выделена, то программа read нормально читает данные из разделяемой области. Если память не выделена, то программа read всё равно обращается к объектам, но в результате получается Bus Error. При этом shm_open и mmap не возвращают никаких ошибок (errno Success)

Проблема возникает только если для программы read используется режим O_RDONLY. Если shm_open вызывать с O_RDWR, то логика программы позволяет обработать возникшую ошибку, чего нет при O_RDONLY. Мне нужно запретить программе read модификацию разделяемой памяти, поэтому требуется O_RDONLY.

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

2. Описание исходников:

write:
* Программа write считывает строки из текстового файла в массив float. Массив в разделяемой памяти.
* После запуска программа ждёт нажатия любой клавиши. В этот момент к разделяемой памяти можно подключиться из программы read.
* После нажатия клавиши write высвобождает память и завершает работу.

read:
* Программа read подключается к разделяемой памяти и считывает данные из массива float. Выводит на экран.

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// read.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
#include <inttypes.h>
#include <sys/time.h>
#include <errno.h>
 
#define __USE_FILE_OFFSET64 1
 
#define SHARED_MEMORY_OBJECT_NAME "my_shared_memory"
 
long long time_ms(void);
 
int main (void) {
 
    int shm, mode = O_CREAT;;
 
    size_t array_size = 10;
 
    printf("Открыть загруженные данные из разделённой памяти\n");
    fflush(stdout);
 
    if ((shm = shm_open(SHARED_MEMORY_OBJECT_NAME, mode|O_RDONLY, 0600)) == -1) {
        return 1;
    }
    fprintf(stderr, "shm_open error: %s\n", strerror(errno));
 
 
    float *array = mmap(0, array_size*sizeof(float), PROT_READ, MAP_SHARED, shm, 0);
    if (array == (float*)-1) {
        return 1;
    }
    fprintf(stderr, "mmap error: %s\n", strerror(errno));
 
    printf("Got from shared memory:\n");
    for(size_t i=0;i<array_size;++i){
        printf("array[%zu]=%.3f\n",i,array[i]);
    }
 
    munmap(array,array_size*sizeof(float));
    close(shm);
 
    printf("All Good!\n");
 
    return(EXIT_SUCCESS);
}
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// write.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
#include <inttypes.h>
#include <sys/time.h>
#include <errno.h>
 
#define __USE_FILE_OFFSET64 1
 
#define SHARED_MEMORY_OBJECT_NAME "my_shared_memory"
 
long long time_ms(void);
 
int main (int argc, char **argv) {
 
    int shm = 0;
    int mode = O_CREAT;
    size_t array_size = 0;
 
    // Было ли передано имя файла в качестве аргумента?
    if (argc == 1) {
        printf("Не задано имя файла\n");
        printf("Программа запускается так:\n");
        printf("%s имя_файла\n", argv[0]);
        exit(EXIT_FAILURE);
    }
 
    // Время для замера длительности в миллисекундах
    long long remember_the_time = 0;
 
    // Инициализируем динамический массив в куче и записываем в него значения
    // из файла
    printf("Открытие файла");
    fflush(stdout);
    remember_the_time=time_ms();
 
    FILE *ptrfile;
    float s;
    ptrfile=fopen(argv[1],"r+");
    if (NULL==ptrfile) {
        printf("Файл не найден!\n");
        exit(EXIT_FAILURE);
    }
    printf(" завершено за %lldms\n",time_ms()-remember_the_time);
 
    printf("Подсчёт количества значений в файле");
    fflush(stdout);
    remember_the_time=time_ms();
    while ((fscanf(ptrfile, "%f",&s)!=EOF)) {
        if (!ptrfile) {
            break; //чтобы не делал лишнего
        }
        array_size++;
    }
    printf(" завершён за %lldms\n",time_ms()-remember_the_time);
 
    printf("Резерование разделённой памяти");
    fflush(stdout);
    remember_the_time=time_ms();
 
    if ((shm = shm_open(SHARED_MEMORY_OBJECT_NAME, mode|O_RDWR, 0600)) == -1) {
        fprintf(stderr, "shm_open error: %s\n", strerror(errno));
        return 1;
    }
 
    if (ftruncate(shm, array_size*sizeof(float)) == -1){
        perror("ftruncate error");
        return 1;
    }
 
    float *array = mmap(0, array_size*sizeof(float), PROT_WRITE|PROT_READ, MAP_SHARED, shm, 0);
    if (array == (float*)-1) {
        fprintf(stderr, "mmap error: %s\n", strerror(errno));
        return 1;
    }
    printf(" завершено за %lldms\n",time_ms()-remember_the_time);
 
 
 
    printf("Считывание %zu элементов из файла в память",array_size);
    fflush(stdout);
 
    remember_the_time=time_ms();
    rewind(ptrfile);//перематываем файл для повторного чтения
    for(size_t m=0; m<array_size; ++m){
        fscanf(ptrfile, "%f",&array[m]);
    }
    fclose(ptrfile);
 
    printf(" завершено за %lldms\n",time_ms()-remember_the_time);
 
    printf("Got from shared memory:\n");
    for(size_t i=0;i<array_size;++i){
        if (i%1000000==0){
            printf("array[%zu]=%.3f\n",i,array[i]);
        }
    }
 
    munmap(array,array_size*sizeof(float));
    close(shm);
 
    /*
     * When you read from stdin (with getchar(), or any other way), it will
     * return characters immediately, without waiting for a Return/Enter. In
     * addition, backspace will no longer 'work' -- instead of erasing the
     * last character, you'll read an actual backspace character in the input.
     * Also, you'll want to make sure to restore canonical mode before your
     * program exits, or the non-canonical handling may cause odd effects
     * with your shell.
     */
    printf("Press any key to free shared memory\n");
    // save old terminal attributes
    struct termios ttyold,ttynew;
    /* get current terminal attirbutes; 0 is the file descriptor for stdin */
    if (tcgetattr(0, &ttynew) != 0)
    {
        fprintf(stderr, "Failed getting terminal attributes\n");
        exit(EXIT_FAILURE);
    }
    ttyold = ttynew;
 
    ttynew.c_lflag &= ~ECHO; /* disable canonical mode */
    ttynew.c_lflag &= ~ICANON;
     /* set immediately */
    tcsetattr(0, TCSANOW, &ttynew);
 
    // Press any key there
    getchar();
 
    // Return old settings
    if (tcsetattr(0, TCSANOW, &ttyold) != 0)
    {
        fprintf(stderr, "Failed getting terminal attributes\n");
        exit(EXIT_FAILURE);
    }
 
    shm_unlink(SHARED_MEMORY_OBJECT_NAME);
 
    printf("All Good!\n");
 
    return(EXIT_SUCCESS);
}
 
// Текущее время в миллисекундах
long long time_ms(void){
    struct timeval t;
 
    gettimeofday(&t, NULL);
    long long mt = (long long)t.tv_sec * 1000 + t.tv_usec / 1000;
    return mt;
}
3. Сборка:
Bash
1
2
cc -pipe -Wall -Wextra -pedantic -Wshadow -march=native --std=c11 -O0 -g -D_XOPEN_SOURCE=500 -o write write.c -lrt
cc -pipe -Wall -Wextra -pedantic -Wshadow -march=native --std=c11 -O0 -g -D_XOPEN_SOURCE=500 -o read read.c -lrt
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
14.06.2016, 13:31
Ответы с готовыми решениями:

Как корректно очистить память, которая была выделена с помощью malloc?
Добрый день. Как корректно очистить память, которая была выделена с помощью malloc? Приведу код: ...

Разделяемая память
Добрый день!!! Столкнулся вот с какой проблемой, мне необходимо написать программу крестики-нолики...

разделяемая память
Всем привет! подскажите, пожалуйста, как решить такую проблему: У меня есть программа которая...

Разделяемая память
Как создать разделяемую память?? какие функции можно использовать. Я вроде нашел CreateFileMaping....

10
3 / 3 / 0
Регистрация: 22.09.2015
Сообщений: 124
14.06.2016, 13:44 2
а что с помощью семафоров или mutex нельзя ограничить чтение пока не будешь уверен что уже точно можно читать?
0
14 / 7 / 2
Регистрация: 25.03.2014
Сообщений: 90
14.06.2016, 13:50  [ТС] 3
Sibmax1988, зачем же усложнять, если есть встроенные механизмы O_RDONLY, O_EXCL. Только непонятно, почему shm_open не возвращает ошибку если объект разделяемой памяти не существует.

У меня нигде не используется распараллеливание записи в эту область; семафоры или мьютексы добавлять ИМХО избыточно.
0
3 / 3 / 0
Регистрация: 22.09.2015
Сообщений: 124
14.06.2016, 13:53 4
Loinch, зато точно сработает
особенно если воспользоваться мьтексами 4 лишних строчки в коде и все
но тут дело исключительно ваше
1
14 / 7 / 2
Регистрация: 25.03.2014
Сообщений: 90
14.06.2016, 14:20  [ТС] 5
Sibmax1988, я бы предпочёл этого избежать в основном из-за недостаточного опыта с блокировками. Если подскажите что и как правильно сделать, то с удовольствием воспользуюсь советом.
0
3 / 3 / 0
Регистрация: 22.09.2015
Сообщений: 124
14.06.2016, 15:00 6
да там все достаточно просто прочитайте теорию мьютексам
в момент когда нельзя читать из памяти закрываете ее а когда можно открываете
вы же знаете в какой момент можно читать а в какой нет
есть код для этого но показать смогу только вечером если интересно конечно
1
14 / 7 / 2
Регистрация: 25.03.2014
Сообщений: 90
14.06.2016, 15:11  [ТС] 7
Цитата Сообщение от Sibmax1988 Посмотреть сообщение
есть код для этого но показать смогу только вечером если интересно конечно
Это будет очень любезно с Вашей стороны поделиться кодом. Могу подождать до вечера. Заранее спасибо!
0
923 / 639 / 198
Регистрация: 08.09.2013
Сообщений: 1,693
14.06.2016, 17:29 8
Цитата Сообщение от Loinch Посмотреть сообщение
int shm, mode = O_CREAT;;
Логика программы требует создавать разделяемую память в ОБОИХ процессах?
Простите, в тонкости не углублялся :-)))
0
14 / 7 / 2
Регистрация: 25.03.2014
Сообщений: 90
14.06.2016, 18:30  [ТС] 9
Цитата Сообщение от gng Посмотреть сообщение
Логика программы требует создавать разделяемую память в ОБОИХ процессах?
Простите, в тонкости не углублялся :-)))
Один блок разделяемой памяти для двух отдельных программ. Одна в неё пишет, другая только читает. Каждая из программ однопоточная.

Цитата Сообщение от gng Посмотреть сообщение
int shm, mode = O_CREAT;;
Нда... Мелкий неприятный баг. Так правильно:
C
1
2
int shm = 0;
int mode = O_CREAT;
0
923 / 639 / 198
Регистрация: 08.09.2013
Сообщений: 1,693
14.06.2016, 18:57 10
Лучший ответ Сообщение было отмечено Loinch как решение

Решение

Цитата Сообщение от Loinch Посмотреть сообщение
int mode = O_CREAT;
При обычной логиге блок создается одним процессом, а вторым открывается созданный ранее блок уже без использования этого флага. Если такого блока нет, должна вернуться ошибка. У вас в этом случае создается новый.
1
14 / 7 / 2
Регистрация: 25.03.2014
Сообщений: 90
14.06.2016, 20:45  [ТС] 11
Цитата Сообщение от gng Посмотреть сообщение
уже без использования этого флага
Действительно. Помогло! Большое спасибо! Вот так работает:
C
1
int mode = O_RDONLY|O_EXCL;
Работающий пример полностью:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// write.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
#include <inttypes.h>
#include <sys/time.h>
#include <errno.h>
 
#define __USE_FILE_OFFSET64 1
 
#define SHARED_MEMORY_OBJECT_NAME "my_shared_memory"
 
long long time_ms(void);
 
int main (int argc, char **argv) {
 
    int shm = 0;
    int mode = O_CREAT;
    size_t array_size = 0;
 
    // Было ли передано имя файла в качестве аргумента?
    if (argc == 1) {
        printf("Не задано имя файла\n");
        printf("Программа запускается так:\n");
        printf("%s имя_файла\n", argv[0]);
        exit(EXIT_FAILURE);
    }
 
    // Время для замера длительности в миллисекундах
    long long remember_the_time = 0;
 
    // Инициализируем динамический массив в куче и записываем в него значения
    // из файла
    printf("Открытие файла");
    fflush(stdout);
    remember_the_time=time_ms();
 
    FILE *ptrfile;
    float s;
    ptrfile=fopen(argv[1],"r+");
    if (NULL==ptrfile) {
        printf("Файл не найден!\n");
        exit(EXIT_FAILURE);
    }
    printf(" завершено за %lldms\n",time_ms()-remember_the_time);
 
    printf("Подсчёт количества значений в файле");
    fflush(stdout);
    remember_the_time=time_ms();
    while ((fscanf(ptrfile, "%f",&s)!=EOF)) {
        if (!ptrfile) {
            break; //чтобы не делал лишнего
        }
        array_size++;
    }
    printf(" завершён за %lldms\n",time_ms()-remember_the_time);
 
    printf("Резерование разделённой памяти");
    fflush(stdout);
    remember_the_time=time_ms();
 
    if ((shm = shm_open(SHARED_MEMORY_OBJECT_NAME, mode|O_RDWR, 0440)) == -1) {
        fprintf(stderr, "shm_open error: %s\n", strerror(errno));
        return 1;
    }
 
    if (ftruncate(shm, array_size*sizeof(float)) == -1){
        perror("ftruncate error");
        return 1;
    }
 
    float *array = mmap(0, array_size*sizeof(float), PROT_WRITE|PROT_READ, MAP_SHARED, shm, 0);
    if (array == (float*)-1) {
        fprintf(stderr, "mmap error: %s\n", strerror(errno));
        return 1;
    }
    printf(" завершено за %lldms\n",time_ms()-remember_the_time);
 
 
 
    printf("Считывание %zu элементов из файла в память",array_size);
    fflush(stdout);
 
    remember_the_time=time_ms();
    rewind(ptrfile);//перематываем файл для повторного чтения
    for(size_t m=0; m<array_size; ++m){
        fscanf(ptrfile, "%f",&array[m]);
    }
    fclose(ptrfile);
 
    printf(" завершено за %lldms\n",time_ms()-remember_the_time);
 
    printf("Got from shared memory:\n");
    for(size_t i=0;i<array_size;++i){
        if (i%1000000==0){
            printf("array[%zu]=%.3f\n",i,array[i]);
        }
    }
 
    munmap(array,array_size*sizeof(float));
    close(shm);
 
    /*
     * When you read from stdin (with getchar(), or any other way), it will
     * return characters immediately, without waiting for a Return/Enter. In
     * addition, backspace will no longer 'work' -- instead of erasing the
     * last character, you'll read an actual backspace character in the input.
     * Also, you'll want to make sure to restore canonical mode before your
     * program exits, or the non-canonical handling may cause odd effects
     * with your shell.
     */
    printf("Press any key to free shared memory\n");
    // save old terminal attributes
    struct termios ttyold,ttynew;
    /* get current terminal attirbutes; 0 is the file descriptor for stdin */
    if (tcgetattr(0, &ttynew) != 0)
    {
        fprintf(stderr, "Failed getting terminal attributes\n");
        exit(EXIT_FAILURE);
    }
    ttyold = ttynew;
 
    ttynew.c_lflag &= ~ECHO; /* disable canonical mode */
    ttynew.c_lflag &= ~ICANON;
     /* set immediately */
    tcsetattr(0, TCSANOW, &ttynew);
 
    // Press any key there
    getchar();
 
    // Return old settings
    if (tcsetattr(0, TCSANOW, &ttyold) != 0)
    {
        fprintf(stderr, "Failed getting terminal attributes\n");
        exit(EXIT_FAILURE);
    }
 
    shm_unlink(SHARED_MEMORY_OBJECT_NAME);
 
    printf("All Good!\n");
 
    return(EXIT_SUCCESS);
}
 
// Текущее время в миллисекундах
long long time_ms(void){
    struct timeval t;
 
    gettimeofday(&t, NULL);
    long long mt = (long long)t.tv_sec * 1000 + t.tv_usec / 1000;
    return mt;
}
В книге Уильям Стивенс "UNIX: взаимодействие процессов" в главе Функции ftruncate и fstat нашёл описание достаточно изящного приёма. Оказывается, fstat() может вернуть читающему процессу размер выделенной области. Вот так будет выглядеть окончательный вариант программы, читающей разделяемую память:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// read.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
#include <inttypes.h>
#include <sys/time.h>
#include <errno.h>
 
#define __USE_FILE_OFFSET64 1
 
#define SHARED_MEMORY_OBJECT_NAME "my_shared_memory"
 
long long time_ms(void);
 
int main (void) {
 
    int shm = 0;
    int mode = O_RDONLY|O_EXCL;
    struct stat info;
 
    printf("Открыть загруженные данные из разделённой памяти\n");
    fflush(stdout);
 
    if ((shm = shm_open(SHARED_MEMORY_OBJECT_NAME, mode, 0400)) == -1) {
        fprintf(stderr, "shm_open error: %s\n", strerror(errno));
        return 1;
    }
 
    if (fstat(shm, &info) == -1 || errno == ENOENT){
        fprintf(stderr, "fstat error: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
 
    size_t array_size = info.st_size / sizeof(float);
 
    float *array = mmap(0, info.st_size, PROT_READ, MAP_SHARED, shm, 0);
    if (array == (float*)-1) {
        fprintf(stderr, "mmap error: %s\n", strerror(errno));
        return 1;
    }
 
    printf("Got from shared memory:\n");
    for(size_t i=0;i<array_size;++i){
        printf("array[%zu]=%.3f\n",i,array[i]);
    }
 
    munmap(array,array_size*sizeof(float));
    close(shm);
 
    printf("All Good!\n");
 
    return(EXIT_SUCCESS);
}
Всем спасибо за проявленный интерес к теме и помощь! Задача успешно решена.
0
14.06.2016, 20:45
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
14.06.2016, 20:45
Помогаю со студенческими работами здесь

Разделяемая память
Нужно файл отобразить в адресное пространство, принцип работы понимаю, но никак на практике...

Проверка, выделена ли память
Возможно ли проверить, указывает указатель на выделенную память, или нет.

Разделяемая память и apache CGI
Что есть. Есть машина с windows 7 и апачем на борту. Так же есть программа А, написанная на c++,...

Разделяемая память без сервера и клиента
Есть контейнер vector из простых объектов: std::vector&lt;int&gt; v; Он соответственно расположен в...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru