.
This commit is contained in:
parent
5b881c5cde
commit
47c10976f0
@ -2,6 +2,8 @@
|
||||
|
||||
### Primitive
|
||||
|
||||
in `./src/lib.c` sind folgende Primitive:
|
||||
|
||||
`time_maccess` misst wie lange der Zugriff auf die Speicheradresse `addr` dauert.
|
||||
```C
|
||||
size_t time_maccess(void (* addr)(void))
|
||||
@ -53,16 +55,36 @@ void flush(void* addr)
|
||||
```bash
|
||||
gcc -O0 cache_test.c primitive.c -o cache_test
|
||||
```
|
||||
##### [Muster](./src/cache_test.c)
|
||||
- CMake ist praktisch! siehe `./src/CMakeList.txt`. Verwenden durch `cmake .` und dann `make`.
|
||||
|
||||
Muster: `./src/cache_test.c`
|
||||
### Block 2: Signale über den Cache
|
||||
|
||||
`sharedlib.c` sieht so aus:
|
||||
|
||||
```C
|
||||
...
|
||||
void function(void)
|
||||
{ asm volatile (
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
...
|
||||
```
|
||||
Das kann man so kompilieren:
|
||||
|
||||
Die Adresse von `function` kann also zum Flushen und Reloaded verwendet werden.
|
||||
Da immer eine gesamte Cacheline geladen wird, sollte sich nichts anderes in dem selben 64Byte Block befinden.
|
||||
Die Funktionen `padding_before` und `padding_after` sind beide 64 Byte groß, also eine Cacheline.
|
||||
Damit kann dann auch gar nichts mehr schief gehen :)
|
||||
|
||||
`sharedlib.c` kann man so kompilieren und linken:
|
||||
|
||||
```bash
|
||||
# sharedlib kompilieren
|
||||
@ -73,9 +95,11 @@ gcc -O0 empfaenger.c primitive.c -L. -lsharedlib -o empfaenger
|
||||
# ggfs. muss man noch den PATH exportieren
|
||||
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
|
||||
```
|
||||
|
||||
dann kann man `function` verwenden:
|
||||
|
||||
```C
|
||||
# in e.g. sender.c
|
||||
maccess((void *) function)
|
||||
...
|
||||
```
|
||||
@ -83,7 +107,12 @@ maccess((void *) function)
|
||||
|
||||
- `taskset` zum pinnen der Programme auf einen bestimmten CPU-Kern.
|
||||
- Verwendet man blockierenden Sleep um zu Takten, z.B. `clock_nanosleep`, dann sollte man unbedingt darauf achten nicht eine Periodendauer zu schlafen, sondern nur bis zum nächsten Zeitabschnitt. Einfacher geht es mit nicht-blockierendem Sleep, z.B. `ualarm`.
|
||||
- Der Zeitabstand von Flush zu Reload sollte MAXIMIERT werden! Dies ist das Zeitfenster, in welchem der Sender die Adresse(n) zurück in den Cache laden kann.
|
||||
- (Der Sender kann das Flushen übernehmen)
|
||||
- (Der Sender kann im gesamten Zeitabschnitt Flushen oder Laden, nicht nur einmal)
|
||||
|
||||
### Block 3: Pakete über den Cache als Medium
|
||||
|
||||
- Kann man irgendwie mehr als ein Bit gleichzeitig senden? ;)
|
||||
|
||||
Muster: `./src/sender.c` und `./src/receiver.c`
|
||||
@ -8,8 +8,10 @@ extern void flush(void* addr);
|
||||
extern void maccess(void* addr);
|
||||
extern uint64_t time_maccess(void* addr);
|
||||
|
||||
void measure_multiple_accesses(void (*addr)(void), size_t num_accesses, size_t* hits, size_t* misses) {
|
||||
for (size_t i = 0; i < num_accesses; i++) {
|
||||
void measure_multiple_accesses(void (*addr)(void), size_t num_accesses, size_t* hits, size_t* misses)
|
||||
{
|
||||
for (size_t i = 0; i < num_accesses; i++)
|
||||
{
|
||||
flush((void*) addr);
|
||||
misses[i] = time_maccess(addr);
|
||||
|
||||
@ -19,10 +21,12 @@ void measure_multiple_accesses(void (*addr)(void), size_t num_accesses, size_t*
|
||||
}
|
||||
}
|
||||
|
||||
size_t find_crossover_point(size_t* hits, size_t* misses, size_t num_accesses) {
|
||||
size_t find_crossover_point(size_t* hits, size_t* misses, size_t num_accesses)
|
||||
{
|
||||
size_t hit_sum = 0, miss_sum = 0;
|
||||
|
||||
for (size_t i = 0; i < num_accesses; i++) {
|
||||
for (size_t i = 0; i < num_accesses; i++)
|
||||
{
|
||||
hit_sum += hits[i];
|
||||
miss_sum += misses[i];
|
||||
}
|
||||
@ -33,16 +37,18 @@ size_t find_crossover_point(size_t* hits, size_t* misses, size_t num_accesses) {
|
||||
return (avg_hit + avg_miss) / 2; // Midpoint as threshold
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
size_t* hits = malloc(NUM_ACCESSES * sizeof(size_t));
|
||||
size_t* misses = malloc(NUM_ACCESSES * sizeof(size_t));
|
||||
|
||||
if (!hits || !misses) {
|
||||
if (!hits || !misses)
|
||||
{
|
||||
perror("Memory allocation failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
measure_multiple_accesses((void*)maccess, NUM_ACCESSES, hits, misses);
|
||||
measure_multiple_accesses((void*) maccess, NUM_ACCESSES, hits, misses);
|
||||
|
||||
size_t threshold = find_crossover_point(hits, misses, NUM_ACCESSES);
|
||||
printf("Suggested threshold: %zu\n", threshold);
|
||||
|
||||
25
src/recv.c
25
src/recv.c
@ -16,23 +16,31 @@ volatile int sample_interval = 100; // Default: 100 µs
|
||||
volatile int threshold = 245; // Default: 245 cycles
|
||||
|
||||
void signal_handler(int signo) {
|
||||
if (signo == SIGALRM) {
|
||||
if (byte_index >= BUFFER_SIZE) {
|
||||
if (signo == SIGALRM)
|
||||
{
|
||||
// Check if buffer is full
|
||||
if (byte_index >= BUFFER_SIZE)
|
||||
{
|
||||
ualarm(0, 0); // Stop sampling
|
||||
printf("Buffer full. Stopping capture.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Receive a byte
|
||||
uint8_t byte = 0;
|
||||
for (int i = 0; i < BITS; i++) {
|
||||
for (int i = 0; i < BITS; i++)
|
||||
{
|
||||
size_t time = time_maccess((void *) function + (i << 9));
|
||||
if (time < threshold) {
|
||||
if (time < threshold)
|
||||
{
|
||||
// set the ith bit of byte to 1 if it was a cache hit
|
||||
byte |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
// flush the cache
|
||||
for (int i = 0; i < BITS; i++) {
|
||||
for (int i = 0; i < BITS; i++)
|
||||
{
|
||||
flush((void *) function + (i << 9));
|
||||
}
|
||||
|
||||
@ -41,7 +49,8 @@ void signal_handler(int signo) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 3) {
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <sample_interval> <threshold>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
@ -51,7 +60,9 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
printf("Receiver started with sample interval: %d µs, threshold: %d cycles\n", sample_interval, threshold);
|
||||
|
||||
for (int i = 0; i < BITS; i++) {
|
||||
// Flush the cache once before starting
|
||||
for (int i = 0; i < BITS; i++)
|
||||
{
|
||||
flush((void *) function + (i << 9));
|
||||
}
|
||||
|
||||
|
||||
11
src/send.c
11
src/send.c
@ -14,8 +14,10 @@ volatile size_t buffer_len = 0;
|
||||
volatile size_t current_index = 0;
|
||||
volatile uint8_t byte_to_send = 0;
|
||||
|
||||
void signal_handler(int signo) {
|
||||
if (signo == SIGALRM) {
|
||||
void signal_handler(int signo)
|
||||
{
|
||||
if (signo == SIGALRM)
|
||||
{
|
||||
if (current_index < buffer_len)
|
||||
{
|
||||
byte_to_send = buffer[current_index++];
|
||||
@ -26,7 +28,8 @@ void signal_handler(int signo) {
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <string> <sample_interval>\n", argv[0]);
|
||||
@ -37,9 +40,11 @@ int main(int argc, char *argv[]) {
|
||||
buffer_len = strlen(argv[1]);
|
||||
int sample_interval = atoi(argv[2]);
|
||||
|
||||
// set up an alarm signal that triggers every timeframe
|
||||
signal(SIGALRM, signal_handler);
|
||||
ualarm(sample_interval, sample_interval);
|
||||
|
||||
// during the timeframe, continuously send the signal
|
||||
while (1)
|
||||
{
|
||||
for (int i = 0; i < BITS; i++)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user