Форум программистов, компьютерный форум, киберфорум
Наши страницы

С под Linux

Войти
Регистрация
Восстановить пароль
 
Loinch
13 / 6 / 1
Регистрация: 25.03.2014
Сообщений: 89
#1

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

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

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)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
14.06.2016, 13:31
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Разделяемая память POSIX IPC как узнать, что память выделена и её уже можо использовать? (C Linux):

Разделяемая память и семафоры для написания чата - C Linux
Суть задания. Программа при первом запуске выполняет роль сервера. Все остальные запуски этой же программы - клиенты. Организовать это...

Разделяемая память linux, Ошибка сегментирования (сделан дамп памяти) - C Linux
здравствуйте! помогите с задачей. Четыре дочерних процесса выполняют некоторые циклы работ, передавая после окончания ...

Как корректно очистить память, которая была выделена с помощью malloc? - C (СИ)
Добрый день. Как корректно очистить память, которая была выделена с помощью malloc? Приведу код: void print_array(int *a, int n) {...

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

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

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

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

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

Цитата Сообщение от gng Посмотреть сообщение
int shm, mode = O_CREAT;;
Нда... Мелкий неприятный баг. Так правильно:
C
1
2
int shm = 0;
int mode = O_CREAT;
0
gng
686 / 532 / 141
Регистрация: 08.09.2013
Сообщений: 1,413
14.06.2016, 18:57 #10
Лучший ответ Сообщение было отмечено автором темы, экспертом или модератором как ответ
Цитата Сообщение от Loinch Посмотреть сообщение
int mode = O_CREAT;
При обычной логиге блок создается одним процессом, а вторым открывается созданный ранее блок уже без использования этого флага. Если такого блока нет, должна вернуться ошибка. У вас в этом случае создается новый.
1
Loinch
13 / 6 / 1
Регистрация: 25.03.2014
Сообщений: 89
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
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
14.06.2016, 20:45
Привет! Вот еще темы с ответами:

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

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

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

Проверка выделена ли память под указатель - C++
Уважаемые программисты!!!!! подскажите пожалуйста можно ли каким-либо способом проверить в условном операторе, выделена ли память под...


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

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru