FP11: Ringbuffer (dutsai) ===== ###### tags: `linux2019` [2019q1 第 11 週測驗題 (上)](https://hackmd.io/@sysprog/HypUB7HjV?type=view#2019q1-%E7%AC%AC-11-%E9%80%B1%E6%B8%AC%E9%A9%97%E9%A1%8C-%E4%B8%8A) :::info This home work and linux ringbuffer will copy all data to the ringbffer. Prons: We could free packet earlier. Cons: Use memcpy in ring buffer. Todo: Maybe we could try to use the zerocopy in ringbuffer. ::: ## RingBuffer Init (Ask a good address) Most different with [traditional ringbuffer](https://github.com/octoberskyTW/Next-simulation/blob/master/models/icf/src/ringbuffer.c) - [toy_queue](https://github.com/le1ca/toy-queue) - [Using black magic to make a fast circular buffer.](https://lo.calho.st/quick-hacks/employing-black-magic-in-the-linux-page-table/) ```clike= // Check that the requested size is a multiple of a page size % getpagesize() != 0 // Create an anonymous file backed by memory // It is implment by the syscall __NR_memfd_create fd = memfd_create("queue_region", 0) int memfd_create(const char *name, unsigned int flags) { return syscall(__NR_memfd_create, name, flags); } // Set buffer size ftruncate(q->fd, size) // Ask mmap for an address at a location where we can put both virtual copies of the buffer (q->buffer = mmap(NULL, 2 * s, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED // Mmap first region mmap(q->buffer, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, q->fd, 0) == MAP_FAILED // Mmap second region, with exact address mmap(q->buffer + s, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, q->fd, 0) == MAP_FAILED ``` - [cirbuf code](https://github.com/ldotrg/cirbuf) ```clike= // Create an temp file path[] = "/tmp/cirbuf-XXXXXX" fd = mkstemp(path) // unlink(path) // Set buffer size ftruncate(fd, cb->size) // Ask mmap for an address at a location where we can put both virtual copies of the buffer cb->data = mmap(NULL, cb->size << 1, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE,-1, 0); // Same as toy_queue void *address = mmap(cb->data, cb->size, PROT_READ | PROT_WRITE,MAP_FIXED | MAP_SHARED, fd, 0); // Same as toy_queue address = mmap(cb->data + cb->size, cb->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0); ``` ## Put into buffer Write data to the tail of the circular buffer. - buffer is not enough space ```clike= // toy_queue didn't wraparound the index. just keep add. q->buffer_size - (q->tail - q->head) < size // not condiser the cb->head > cb->tail situation // cirbuf version cirbuf_unusedspace(cb) <= size ``` - tail idx wrapparound `if (cb->size < cb->tail) cb->tail %= cb->size;` - Cirbuf Version ```clike static inline int cirbuf_offer(cirbuf_t *cb, const unsigned char *data, const int size) { /* prevent buffer from getting completely full or over commited */ if (cirbuf_unusedspace(cb) <= size) return 0; int written = cirbuf_unusedspace(cb); written = size < written ? size : written; memcpy(cb->data + cb->tail, data, written); cb->tail += written; /* TODO: add your code here */ if (cb->size < cb->tail) cb->tail %= cb->size; return written; } static inline int cirbuf_usedspace(const cirbuf_t *cb) { if (cb->head <= cb->tail) return cb->tail - cb->head; return cb->size - (cb->head - cb->tail); } static inline int cirbuf_unusedspace(const cirbuf_t *cb) { return cb->size - cirbuf_usedspace(cb); } ``` ## Get from buffer - buffer empty ```clike cb->head == cb->tail ``` - head idx wrapparround ```clike= if (cb->head >= cb->size) cb->head -= cb->size; ``` - cirbuf version ```clike static inline unsigned char *cirbuf_poll(cirbuf_t *cb, const unsigned int size) { if (cirbuf_is_empty(cb)) return NULL; void *end = cb->data + cb->head; cb->head += size; if (cb->head >= cb->size) cb->head -= cb->size; return end; } ``` - [linux circular buffer Impl.](https://elixir.bootlin.com/linux/v4.15.18/source/include/linux/circ_buf.h) Original Assignment info: [F11-ringbuffer](https://hackmd.io/@sysprog/SkYLI9CiN?type=view#F11-ringbuffer) ###### tags: `ringbuffer`