c++ - que - memoria optane vs ssd
Definir un nuevo tipo de memoria (2)
¿Es posible definir un nuevo tipo de memoria? Por ejemplo, en algunos compiladores de sistemas integrados puede hacer esto:
__flash const char array[] = "This is memory in flash (not ram) so I can waste it!";
Entonces, ¿es posible volverse aún más loco y definir un nuevo tipo de memoria (como decir una tarjeta SD).
Básicamente, estoy preguntando si es posible definir qué es una tarjeta SD (cómo acceder a los datos en ella) y luego declarar una variable en la memoria SD. (cada vez que ve una escritura llama al método sd, cada vez que ve una lectura llama al método sd):
class SDCard{
public:
void write(void* data,size_t length);
void* read(size_t length);
void* memAlloc(size_t length);
};
__sd char myVar; //Grabs an address in the sd card based on SDCard::memAlloc
myVar = ''A''; //Calls whatever I defined the sd card write operation as
char other = myVar; //Calls whatever I defined the sd card read operation as
Estoy usando gcc si puedo hacer algo especial con eso (estoy casi dispuesto a modificar la fuente del compilador para permitirme hacer esto).
Algo como esto es posible, pero hay algunos problemas:
struct Vol_t{ //Would represent an SD card an external RAM or any other memory
void write(void* data,size_t len,size_t add) const{}
void* read(size_t len,size_t add) const{}
};
template<Vol_t* sd, class Type,size_t address>
struct MemDef{ //Wrap your type with this (maybe add -> operator support later
void operator=(Type&& data){
sd->write(&data,sizeof(data),address);
}
operator Type&(){
return *reinterpret_cast<Type*>(sd->read(sizeof(Type),address));
}
};
Vol_t SD; //Initialize our SD card
MemDef<&SD,int,0xdeadbeaf> sdVar; //Declare an int variable on the SD card
int main(int argc,char** args){
sdVar = 12; //Writes to the SD card
int local = sdVar; //Reads from the SD card
system("Pause");
}
Problemas con este enfoque:
- El optimizador debe hacer cada lectura / escritura ---- no puede hacer ninguna optimización en una variable declarada de esta manera. (este es el problema principal)
- Es un poco poco elegante (pero hace el trabajo)
- Tienes que especificar qué dirección de memoria usar (Sería increíble si el compilador pudiera resolver todo eso antes de la compilación y automáticamente
Así que tal vez tenga que agregar una palabra clave a gcc (si ese es el caso, indíqueme la dirección correcta para comenzar).
Editar: hay otro problema con este enfoque. Si un tipo tiene un puntero a otra variable, esa variable no se inicializará en la tarjeta SD (solo lo hará el puntero).
Bueno, en el mundo C ++ para abstraer la memoria usualmente escribes un asignador personalizado. A lo largo de las lineas
template <class T>
class SDAlloc {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
// rebind allocator to type U
template <class U>
struct rebind {
typedef SDAlloc<U> other;
};
// return address of values
pointer address (reference value) const {
return &value;
}
const_pointer address (const_reference value) const {
return &value;
}
SDAlloc(const char* device) {
// open device by name
// make helper structures
}
~SDAlloc() {
// close device
}
// return maximum number of elements that can be allocated
size_type max_size () const throw() {
return SDsize;
}
// allocate but don''t initialize num elements of type T
pointer allocate (size_type num, const void* = 0) {
// print message and allocate memory on SD
}
.......
Podría haber dos casos:
- El hardware es tal que un cierto rango de direcciones se asigna a la tarjeta SD, y los datos pueden escribirse / leerse desde la tarjeta SD usando las instrucciones normales de acceso a la memoria, dentro de ese rango. Punteros regulares se utilizan.
- Se deben usar instrucciones / funciones especiales para leer / escribir en la tarjeta SD. Por ejemplo, una función
SDCard::read
, que llama a una función especial en el sistema operativo (si existe) o una instrucción de interrupción.
__flash
es una extensión de GCC. Utiliza una instrucción diferente para acceder a la memoria, y localiza datos static
en otro segmento. Pero no se puede generalizar de esta manera usando solo C ++. Tampoco se puede usar con la asignación dinámica.
Primer caso (rango de direcciones)
Para usar punteros regulares para leer / escribir datos en la tarjeta SD, deben marcarse como volatile
. De esta forma, el compilador no optimiza lejos y lee / escribe. volatile
significa que la memoria puede cambiarse / usarse desde fuera del programa, como por ejemplo, el hardware que lo escribe en una tarjeta SD. Ver http://en.cppreference.com/w/cpp/language/cv .
Por ejemplo
volatile char* data = 0x00010000;
memcpy(data, "test", 5);
escribe "test"
en la tarjeta SD, si, por ejemplo, el rango de memoria 0x00010000
.. 0x0001ffff
asigna a él.
Para asignar dinámicamente la memoria en la tarjeta SD, como con malloc
y free
para la memoria de trabajo normal, se necesitaría un asignador personalizado. Tendría que manejar la segmentación de la memoria en sí, es decir, necesita asignar qué áreas de la memoria están libres o asignadas, y allocate(len)
necesidades para encontrar un segmento libre de longitud al menos len
. Esto normalmente es manejado por el sistema operativo.
Esto se puede escribir en C ++ como una clase de asignador , una clase que debe cumplir los requisitos de Allocator
(concepto): http://en.cppreference.com/w/cpp/concept/Allocator . Por ejemplo (incompleto):
template<typename T>
class SD_allocator {
using value_type = T;
using pointer = T*;
pointer allocate(std::size_t len) {} // allocate segment of len bytes
void deallocate(pointer, std::size_t) {}
};
Si puede usarse con contenedores STL, como:
std::vector<int, SD_allocator<int>> vec;
utiliza la memoria en la tarjeta SD para los elementos de vec
. Aquí no son volatile
, y están destinados para su uso dentro del programa solamente, no para el almacenamiento persistente en la tarjeta SD.
El asignador estándar en C ++ es std::allocator
, que asigna memoria regular como malloc
y free
.
Boost parece proporcionar un asignador que maneja la segmentación, en una región de memoria definida personalizada:
http://www.boost.org/doc/libs/1_35_0/doc/html/interprocess/managed_memory_segments.html http://www.boost.org/doc/libs/1_55_0/doc/html/boost/interprocess/allocator.html
Para el almacenamiento persistente, como una tarjeta SD, puede ser mejor definir una estructura y un diseño fijo para los datos en la tarjeta SD, y luego leer / escribir en ella.
struct SDCard_data {
std::int32_t int1;
std::int32_t buffer1[500];
std::int8_t padding_[34];
int four_bit1 : 4;
int four_bit2 : 4;
bool bit1:1;
bool bit2:1;
bool bit3:1;
bool bit4:1;
};
static volatile SDCard_data* sdcard
= reinterpret_cast<volatile SDCard_data*>(0x0001000);
int write_to_card() {
// writes to the card
card->int1 = 32;
card->bit3 = true;
}
Segundo caso (instrucciones especiales)
Si las lecturas / escrituras en la tarjeta SD no se corresponden con las instrucciones normales de acceso a la memoria en el hardware, no se puede acceder a los datos directamente usando punteros volatile
crudos.
Si el objetivo es seguir accediendo de esa manera, se necesitaría una clase como MemDef
. Puede ser mejor tratar la tarjeta SD como un archivo / secuencia, y en su lugar escribir / leer fragmentos enteros de datos desde / hacia ella, usando funciones como fopen
, fread
, fprintf
o similar.
Los objetos necesitan serializarse / deserializarse para esto. Copiando una struct
como memoria en bruto, como
struct A;
A a;
write_to_card(reinterpret_cast<void*>(&a), sizeof(A))
funciona siempre que la struct
sea un PODType
y no contenga ningún puntero / referencia, es decir, tipos cuya representación interna dependa de las direcciones de memoria. También depende del diseño de la memoria de la plataforma (alineación, relleno estructural), endianness, representación de float
, CHAR_BIT
, etc. Para soporte multiplataforma (por ejemplo, cuando la tarjeta SD se lee desde otro dispositivo con otro microcontrolador, algún formato de archivo) necesitaría ser usado en su lugar.
También puede ser posible (pero difícil) definir una clase de Allocator
personalizada, que utiliza una clase como MemDef
como tipo de puntero.