Ник:
Пароль:

Контакты

E-mail: info@starterkit.ru
тел.: +7 922 680-21-73
тел.: +7 922 680-21-74
Телеграм: t.me/starterkit_ru

Способы оплаты

User Info


Добро пожаловать,
Guest

Регистрация или входРегистрация или вход
Потеряли пароль?Потеряли пароль?

Ник:
Пароль:

ПользователейПользователей:3
Поисковых ботовПоисковых ботов:3
ГостейГостей:1

ОбновитьПодробнееВсегоВсего:7
Форум » starterkit.ru » Embedded Linux
Sharing memory
Pav
Добавлено 28.01.2012 18:46
0
Сообщение: 1
Pav
0

Пункты: 262
Регистрация: 25.02.2011
Добрый День

Нужна помощь с вызовом mmap.

Ситуация такая: в драйвере создается статичный элемент (типа shared_mem_t), который хочется примапить из прикладного уровня. У драйвера реализован нод с операцией чтения, которая возвращает адрес этого буфера.

В прикладной программе я читаю адрес, вычисляю длину, маплю нужное кол-во page'ей и настраиваю указатель на эту структуру.

Все бы ничего, только при попытке доступа по указателю программа сегфолтится.

Вот как чего создается:

Kernel module

shared_memory.h
Код
#include "../Interface/shared_mem.h"

/// Shared memory symbol
extern shared_mem_t shared_mem;


shared_memory.c
Код
#include "shared_memory.h"

shared_mem_t shared_mem;


Процедура по настройке указателя в прикладном уровне:
Код
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))

/// shared memory pointer
shared_mem_t *shared_mem;

char configure_mem_ptr()
{
int fd;
off_t target;
size_t length;

#ifdef TERMINAL_APP_LOG
printf("Starting logging server\n");
#endif

if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) {
printf("ERROR: failed to open /dev/mem\napplication exit\n");
return 1;
}

#ifdef TERMINAL_APP_LOG
printf("Openned /dev/mem\n");
#endif

if ((int *)(target = (int) read_address_from_node()) == NULL) {
close(fd);
return 1;
}

#ifdef TERMINAL_APP_LOG
printf("Address read; address: 0x%04X\n", (unsigned int) target);
#endif

length = ((target + sizeof(shared_mem_t) + (PAGE_MASK)) & (PAGE_MASK)) - (target & (PAGE_MASK));

// map the shared memory
shared_mem = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & PAGE_MASK);

#ifdef TERMINAL_APP_LOG
printf("Mapped\n");
#endif

shared_mem+= target & PAGE_MASK;

return 0;
}


Такое ощущение, что система прерывает процесс. Однако должен же быть способ как-то пометить память, чтобы разрешить доступ?

Буду очень благодарен за любой совет.
Спуститься к концу Подняться к началу
Персональная информация
sasamy
Добавлено 28.01.2012 19:45 Редактировалось 28.01.2012 19:47 Сообщение: 2
sasamy
4.71

Пункты: 83542
Регистрация: 14.08.2009
Цитата

Kernel module

/// Shared memory symbol
extern shared_mem_t shared_mem;


А что это за тип и как вы вообще выделяете память в ядре ? как получаете указатель на буфер который передаете через "ноду" ?
Спуститься к концу Подняться к началу
Персональная информация
Pav
Добавлено 28.01.2012 20:32 Сообщение: 3
Pav
0

Пункты: 262
Регистрация: 25.02.2011
Тип объявлен мной как

typdef struct
{ bla bla bla } shared_mem_t;

в одном из хедеров.

Там много чего есть, но содержимое структуры я думаю ничего не меняет.

Память выделяется статичным объявлением объекта:
в исходном файле:

Код
#include "shared_memory.h"

shared_mem_t shared_mem;


Адрес читается следующим образом:
In kernel
Код
int read_function( char *page, char **start, off_t off,
int count, int *eof, void *data )
{
*((int *)page) = (int) &shared_mem;

return 4;
}


А в приложении следующей процедурой:

Код
int *read_address_from_node()
{
FILE *pfile;
int val;
char *char_ptr, i;

pfile = fopen( PROC_NODE, "r" );

if (pfile == NULL) {
// no such file: we've got to exit then
printf("ERROR: module is not loaded - application exit\n");

return NULL;
}

char_ptr = (char *)&val;
// read 4 bytes of the address
for (i = 0; i < 4; i++) {
*char_ptr = fgetc(pfile);

if ((*char_ptr) == EOF) {
// that is inconsistent - we exit
printf("ERROR: reading only %d bytes of the address - application exit\n", i + 1);
val = (int)NULL;
break;
}

char_ptr++;
}

fclose (pfile);

return (int *) val;
}
Спуститься к концу Подняться к началу
Персональная информация
sasamy
Добавлено 28.01.2012 21:26 Редактировалось 28.01.2012 21:34 Сообщение: 4
sasamy
4.71

Пункты: 83542
Регистрация: 14.08.2009
Цитата

Память выделяется статичным объявлением объекта:


В итоге вы получаете указатель на область виртуальной памяти в пространстве ядра, /dev/mem работает с физическими адресами памяти
man mem
Цитата

Byte addresses in mem are interpreted as physical memory addresses.


Вам нужно другой файл устройств попробовать
Цитата

The file kmem is the same as mem, except that the kernel virtual memory rather than physical memory is accessed.

It is typically created by:

mknod -m 640 /dev/kmem c 1 2
chown root:kmem /dev/kmem
Спуститься к концу Подняться к началу
Персональная информация
Pav
Добавлено 28.01.2012 22:06 Редактировалось 28.01.2012 22:27 Сообщение: 5
Pav
0

Пункты: 262
Регистрация: 25.02.2011
Ага, вот значит где собака зарыта.

Я по необразованности решил, что в ядре мы работаем с физическими адресами. Спасибо большое за помощь!

Пойду пробовать.

Update.

Как ни прискорбно, но не это являлось причиной сегфолта. Это была еще одна бага, от которой бы потом волосы бы встали дыбом - почему читается не то, что пишется. Очень благодарен за такую помощь.

Тем не менее, при попытке чтения система завершает программу.

Вот что находится в main:

Код
int main(int argc, char *argv[])
{
// int i;
char *ptr;
// unsigned char val = 0x12;

if(configure_mem_ptr() != 0) return 1;

#ifdef TERMINAL_APP_LOG
printf("Pointer set\n");
#endif

ptr = (char *)shared_mem;

#ifdef TERMINAL_APP_LOG
if(ptr == NULL) printf("ptr pointer is NULL\n");
else printf("ptr is not NULL\n");
#endif
/*
for(i = 0; i < sizeof(shared_mem_t); i++){
if(*ptr != val) printf("E");
ptr++;
val++;
}
*/
printf("read_ind = %d\n", shared_mem->log_buff.read_ind);

printf("\nDone\n");

return 0;
}


Вероятно нужно в ядре как-то разрешить доступ к памяти. Я просто гадаю, но ведь люди как-то это делают, верно?
Спуститься к концу Подняться к началу
Персональная информация
sasamy
Добавлено 28.01.2012 22:12 Редактировалось 28.01.2012 22:22 Сообщение: 6
sasamy
4.71

Пункты: 83542
Регистрация: 14.08.2009
Еще можете получить физический адрес этой структуры в ядре и его передавать через "ноду" - тогда можно и через /dev/mem его мапить
Код

#include <asm/memory.h>

phys_addr = virt_to_phys(virt_addr);
Спуститься к концу Подняться к началу
Персональная информация
Pav
Добавлено 28.01.2012 22:28 Редактировалось 28.01.2012 22:38 Сообщение: 7
Pav
0

Пункты: 262
Регистрация: 25.02.2011
это тоже отличная мысль, сейчас попробую

Update

похоже, что все равно что мапить - все падает. Вот лог из терминала:


Openned /dev/mem
Address read; address: 0x3F017218
Mapped
Pointer set
ptr is not NULL
Segmentation fault
Спуститься к концу Подняться к началу
Персональная информация
sasamy
Добавлено 28.01.2012 23:39 Редактировалось 28.01.2012 23:52 Сообщение: 8
sasamy
4.71

Пункты: 83542
Регистрация: 14.08.2009
Как-то вы замысловато длину вычисляете
Цитата

length = ((target + sizeof(shared_mem_t) + (PAGE_MASK)) & (PAGE_MASK)) - (target & (PAGE_MASK));


Как минимум тут получаются нереальные размеры - вероятно там вы хотели написать PAGE_SIZE

Вообще лучше бы под эту структуру через kmalloc память выделять - она будет автоматом по границам страницы памяти и гарантированно непрерывна физически, а то при любом промахе будете затирать из юзерспейс что-то в ядре.
Спуститься к концу Подняться к началу
Персональная информация
Pav
Добавлено 29.01.2012 00:16 Сообщение: 9
Pav
0

Пункты: 262
Регистрация: 25.02.2011
Да, очепятался.

Проверил длины по запуску программа вывела следующее:
sizeof(shared_mem_t) = 16516
length = 20480

Теперь выглядит правильно.

Может проблема в физической непрерывности? Ведь если буфер лежит в памяти как попало, а я маплю через физический адрес - оно и не едет?
Спуститься к концу Подняться к началу
Персональная информация
sasamy
Добавлено 29.01.2012 00:21 Редактировалось 29.01.2012 00:58 Сообщение: 10
sasamy
4.71

Пункты: 83542
Регистрация: 14.08.2009
Цитата

Может проблема в физической непрерывности?


Может и в этом - в любом случае выделяйте память лучше через kmalloc - тогда хотя бы ничего лишнего не затрете случайно, там память странично выделяется и выровнена по границе страницы памяти. Я думал у вас небольшая структурка байт 100 :) а тут целый буфер 16 кб.
Спуститься к концу Подняться к началу
Персональная информация
Форум » starterkit.ru » Embedded Linux