Magic Ring Buffer
@urakarin
Profile
warawara@bug.co.jp
Twitter : @urakarin
mocomocolab.com
Fujiwara Takeshi (40)
Documents
codezine
 2008 : JPEGを散歩する(色の世界, 人間の特性を生かした色表現)
http://codezine.jp/article/detail/3749
slideshare
 2009 : 身の回りの圧縮
 2009 : 色を認識する仕組み&色空間
 2009 : スケジューラから見たOSの話
http://www.slideshare.net/urakarin
社内 (BUG-LAPIN 左の社内ゼミメニュー)
 H.264, JPEGほか
http://zeppeli.bug.co.jp/xoops/
“UDP受信時 の一時バッファ”
“動画再生  の一時バッファ”
“シリアル送信の一時バッファ”
を作ってください。
Today’s theme
...なんてときに必要なものは
共通項
共通項
FIFO
First In First Out (FIFO)
Queue, Pipe
What is FIFO ?
First In First Out (FIFO)
Queue, Pipe
What is FIFO ?
First In First Out (FIFO)
Queue, Pipe
What is FIFO ?
First In First Out (FIFO)
Queue, Pipe
First In Last Out (FILO)
Stack
What is FIFO ?
First In First Out (FIFO)
Queue, Pipe
First In Last Out (FILO)
Stack
What is FIFO ?
Memory
FIFO Buffer Implementation
Memory
data
address
FIFO Buffer Implementation
memcpy
Memory
FIFO Buffer Implementation
memcpy
Memory
FIFO Buffer Implementation
Memory
FIFO Buffer Implementation
memmove
Memory
FIFO Buffer Implementation
Memory
FIFO Buffer Implementation
Memory
!?
FIFO Buffer Implementation
Memory
!? memmove ... orz
FIFO Buffer Implementation
Memory
FIFO Buffer Implementation 2
memcpy
Memory
FIFO Buffer Implementation 2
memcpy
Memory
FIFO Buffer Implementation 2
Memory
FIFO Buffer Implementation 2
memcpy
Memory
FIFO Buffer Implementation 2
Memory
FIFO Buffer Implementation 2
!?
Memory
FIFO Buffer Implementation 2
!?
Memory
・バッファをどんなに大きくしてもデータ列の位置が
進んでいくので解決しない
FIFO Buffer Implementation 2
!?
Memory
・バッファをどんなに大きくしてもデータ列の位置が
進んでいくので解決しない
バッファのお尻と頭をくっつけて、輪にしてしまおう!
FIFO Buffer Implementation 2
!?
Memory
FIFO Buffer Implementation 2
!?
Memory
Ring Buffer
(Circular Buffer)
FIFO Buffer Implementation 2
!?
Memory
Ring Buffer
(Circular Buffer)
FIFO Buffer Implementation 2
Memory
Ring Buffer
(Circular Buffer)
FIFO Buffer Implementation 2
memcpymemcpy
Memory
Ring Buffer
(Circular Buffer)
FIFO Buffer Implementation 2
memcpymemcpy
Memory
FIFO Buffer Implementation 2
memcpymemcpy
Memory
・メモリの端に到達するかどうかを毎回判定しながら、端に
到達した場合は、memcpyを2回に分けて処理する
 →ラッピング処理 (Wrap around)
FIFO Buffer Implementation 2
Ring Buffer 完成 !!
完
あれ? “Magic” は?
あれ? “Magic” は?
Wrap aroundの排除 !!
そのまえに...
iPhone5
物理メモリ 1GB仮想メモリ4GB
0x0000 0000 ∼ 0xFFFF FFFF 0x0000 0000 ∼ 0x3FFF FFFF
仮想アドレスと物理アドレス
0xFFFF FFFF
0x0000 0000
0x3FFF FFFF
物理メモリ 1GB仮想メモリ4GB
iPhone5
System
Framework他
Appでの
確保メモリ
※ アドレスサイズや位置などかなり適当
仮想アドレス
物理アドレス
仮想アドレスと物理アドレス
Page size (4KB)単位での割当
物理メモリ 1GB
iPhone5
仮想アドレスと物理アドレス
・物理アドレスが足りなくなったらどうするの?
物理メモリ 1GB
iPhone5
仮想アドレスと物理アドレス
・物理アドレスが足りなくなったらどうするの?
→HDDにPage単位で退避する(Swap out)
→iPhoneはSwap outはしないので、Read onlyのpageを
破棄したり、他のアプリにメモリの解放を求める
iPhone5
0x0000 0000
0x0000 2000
0x2F00 4000
0x8000 2000 0x2F00 4000
0x4000 2000 0x0000 2000
... ...
仮想アドレス→物理アドレス変換
0xFFFF FFFF
0x0000 0000
0x4000 2000
0x8000 2000
Memory Management Unit
MMUの持つ機能のうちの
一つがアドレス変換
0x8000 2000 0x2000 0000
... ...
0x8000 20000x8000 2000
0x8000 2000 0x2F00 4000
... ...
0x2000 0000
0x2F00 4000
別アプリから同じアドレスを指定されても混ざらない!
(他のアプリの仮想アドレス空間にはアクセスできない)
MMUのすごいところ 1
0x4000 2000 0x0000 2000
... ...
0x4000 2000 0x0000 2000
... ...
同じ物理アドレスを指すことで共有ができる!
0x4000 2000 0x4000 2000
0x0000 2000
MMUのすごいところ 2
0x4000 2000 0x0000 2000
... ...
0x4000 2000 0x0000 2000
... ...
同じ物理アドレスを指すことで共有ができる!
0x4000 2000 0x4000 2000
0x0000 2000
MMUのすごいところ 2
共有ライブラリ、共有メモリ
0x8000 2000 0x2F00 4000
... ...
0x8000 2000 0x2F00 4000
... ...
MMUのすごいところ 3
0x8000 2000
0x2F00 4000
0x2000 00000x8000 2000 0x2000 0000
... ...
fork (プロセス複製) .. iOSではできないけど..
write
大きなメモリ領域をコピーするときに、コピーしたように
見せて、実は書込みがあるまでコピーしない。
= COW (Copy On Write)
仮想アドレスは用意されるが、実体
(物理メモリ)は確保されない
fork
k
write
MMUのすごいところ 4
0x8000 2000
0x2000 0000
callocしたときに0 fillしたように見せて実はしていない
k
0x1000 0000
仮想メモリ 物理メモリ
0x8000 3000
0x8000 4000
0x8000 5000
write
calloc直後
write
calloc(4, PAGE_SIZE)
0x8000 2000 0x1000 0000
0x8000 3000 0x1000 0000
0x8000 4000 0x1000 0000
0x8000 5000 0x1000 0000
0x8000 2000 0x2000 0000
0x8000 3000 0x1000 0000
0x8000 4000 0x1000 0000
0x8000 5000 0x1000 0000
あらかじめ用意された
0 fill済みの1 page (4KB)
そして
MMUのすごいところ 5
Ring Buffer用にMirroring
k
仮想メモリ
0x8000 5000 memory_object4
0x8000 4000 memory_object3
0x8000 3000 memory_object2
0x8000 2000 memory_object1
0x8000 3000 memory_object2
0x8000 2000 memory_object1
0x8000 5000 memory_object2
0x8000 4000 memory_object1
0x8000 3000 memory_object2
0x8000 2000 memory_object1
物理メモリ
物理メモリの予約はとってあ
るが、未割当状態
0x8000 2000
0x8000 4000
? ? ?
実際のコード
buffer->length = round_page(length); // We need whole page sizes
vm_address_t bufferAddress;
kern_return_t result = vm_allocate(mach_task_self(),
&bufferAddress,
buffer->length * 2,
VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
result = vm_deallocate(mach_task_self(),
bufferAddress + buffer->length,
buffer->length);
vm_address_t virtualAddress = bufferAddress + buffer->length;
result = vm_remap(mach_task_self(),
&virtualAddress, // mirror target
buffer->length, // size of mirror
...
mach_task_self(), // same task
bufferAddress, // mirror source
... );
https://github.com/michaeltyson/TPCircularBuffer/blob/master/TPCircularBuffer.c
Darwinの場合
void *ptr = 0;
while (!ptr && num_retries-- != 0)
{
void * target_addr = VirtualAlloc(0, ring_size * 2, MEM_RESERVE, PAGE_NOACCESS);
if (target_addr) {
VirtualFree(target_addr, 0, MEM_RELEASE);
// is ring_size a multiple of 64k? if not, this won't ever work!
if ((ring_size & 0xffff) != 0)
return 0; 
// try to allocate and map our space
size_t alloc_size = ring_size * 2;
HANDLE mapping;
if (!(mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE,
(unsigned long long)alloc_size >> 32,
alloc_size & 0xffffffffu, 0)) ||
!(ptr = (char *)MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0, 0,
ring_size, target_addr)) ||
!MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0, 0,
ring_size, (char *)target_addr + ring_size))
{
// something went wrong - clean up
... UnmapViewOfFile(), CloseHandle()
}
}
}
return ptr;
https://gist.github.com/rygorous/3158316
Windowsの場合
char path[] = "/dev/shm/ring-buffer-XXXXXX";
int file_descriptor;
void *address;
int status;
file_descriptor = mkstemp (path);
status = unlink (path);
buffer->count_bytes = 1UL << order;
buffer->write_offset_bytes = 0;
buffer->read_offset_bytes = 0;
status = ftruncate (file_descriptor, buffer->count_bytes);
buffer->address = mmap (NULL, buffer->count_bytes << 1, PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
address = mmap (buffer->address,
buffer->count_bytes, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, file_descriptor, 0);
address = mmap (buffer->address + buffer->count_bytes,
buffer->count_bytes, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, file_descriptor, 0);
status = close (file_descriptor);
http://en.wikipedia.org/wiki/Ringbuffer
Linuxの場合
Demo
(Linuxの場合で)
Keywords
MMU
Paging size
PTE (Page Table Entry)
TLB (Translation Look aside buffer)
MMIO (Memory Mapped I/O)
Demand paging
Sandbox
...

Magic Ring Buffer