# 2019q1 Homework7 (ringbuffer)
contributed by < `yiwei01` >
###### tags: `sysprog2019`
## 測驗 `1`
考慮到 [cirbuf](https://github.com/sysprog21/cirbuf) 這個 [circular buffer](https://en.wikipedia.org/wiki/Circular_buffer) 實作,嘗試透過 mmap 系統來化簡緩衝區邊界處理的議題。對照 [cirbuf.h](https://github.com/sysprog21/cirbuf/blob/master/cirbuf.h) 和 [test-cirbuf.c](https://github.com/sysprog21/cirbuf/blob/master/tests/test-cirbuf.c) 以得知具體用法,請補完以下程式碼:
```cpp
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;
MM1
return written;
}
static inline unsigned char *cirbuf_peek(const cirbuf_t *cb)
{
if (cirbuf_is_empty(cb))
return NULL;
MM2
}
```
==作答區==
MM1 = ?
* `(d)` if (cb->size < cb->tail) cb->tail %= cb->size;
MM2 = ?
* `(c)` returb cb->data + cb->head;
## 閱讀程式碼並著手改善
### cirbuf.h
- [int mkstemp(char *template);](https://linux.die.net/man/3/mkstemp)
> The mkstemp() function generates a unique temporary filename from template, creates and opens the file, and returns an open file descriptor for the file.
>
> The last six characters of template must be "XXXXXX" and these are replaced with a string that makes the filename unique. Since it will be modified, template must not be a string constant, but should be declared as a character array.
>
> On success, these functions return the file descriptor of the temporary file. <font color ="red">On error, -1 is returned, and errno is set appropriately.</font>
1. template 的最後六個字必須要是 "XXXXXX" ,以便被替換成獨特的檔案名稱
2. "XXXXXX" 必須要透過 character array 來宣告
3. Return -1 when function creates and opens temporary file and errno is set appropriately.
- unlink
> Upon successful completion, 0 shall be returned. <font color ="red">Otherwise, -1 shall be returned and errno set to indicate the error.</font> If -1 is returned, the named file shall not be changed.
- ftruncate
> Upon successful completion, ftruncate() shall return 0; <font color ="red">otherwise, -1 shall be returned and errno set to indicate the error.</font>
- mmap
> On success, mmap() returns a pointer to the mapped area. <font color ="red">On error, the value MAP_FAILED (that is, (void *) -1) is returned, and errno is set to indicate the cause of the error.</font>
因此
1. 可以透過這些 function 的回傳值來判斷此次的呼叫是否成功
2. 若此次 <font color ="red">function 呼叫若是失敗,`errno` 的值會被設定,並可查詢 `errno` 的值得知呼叫失敗的原因 </font>
3. 一旦
`cb->data = mmap(NULL, cb->size << 1, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);` 成功要到記憶體空間,接下來
`address_start = mmap(cb->data, cb->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0);` 或是 `void *address_half = mmap(cb->data + cb->size, cb->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0); ` 若回傳值不對,則要記得 `munmap(cb->data, cb->size << 1);`
因此程式碼可改善為:
```clike=
static void create_buffer_mirror(cirbuf_t *cb)
{
char path[] = "/tmp/cirbuf-XXXXXX";
int fd = mkstemp(path);
if (fd == -1 || unlink(path) || ftruncate(fd, cb->size)) {
if (-1 != fd) {
close(fd);
}
return;
}
/* create the array of data */
cb->data = mmap(NULL, cb->size << 1, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE,
-1, 0);
if (cb->data == MAP_FAILED) {
close(fd);
return;
}
void *address_start = mmap(cb->data, cb->size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, fd, 0);
if (cb->data != address_start) {
munmap(cb->data, cb->size << 1);
close(fd);
return;
}
void *address_half =
mmap(cb->data + cb->size, cb->size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, fd, 0);
if (address_half != cb->data + cb->size) {
munmap(cb->data, cb->size << 1);
close(fd);
return;
}
close(fd);
return;
}
```
`create_buffer_mirror` 後若 errno 的值不為 0 則代表在 create buffer mirror 的過程中有出錯。
```clike=
/** Create new circular buffer.
* @param size Size of the circular buffer.
* @return pointer to new circular buffer
*/
static inline void cirbuf_new(cirbuf_t *dst, const unsigned long int size)
{
dst->size = size;
dst->head = dst->tail = 0;
assert(!errno);
create_buffer_mirror(dst);
if (errno) {
printf("Fail to create buffer mirror\n");
errno = 0;
}
}
```
## 延伸問題:
1. 解析 test suite 運作原理,嘗試強化現有 cirbuf 的例外處理機制,並且實作於 unit test 內部;
2. 儘管已有 12 個 test case,但涵蓋層面仍不夠廣泛,請指出現有實作的缺陷並著手改善; (提示: 數值範圍及多個 PAGE_SIZE 的空間)
3. 學習 [Using black magic to make a fast circular buffer](https://lo.calho.st/quick-hacks/employing-black-magic-in-the-linux-page-table/),指出 fast circular buffer 實作的技巧,並分析 cirbuf 的效能,並逐步量化及改善效率;
4. 向 OP-TEE 的 [Benchmark framework](https://optee.readthedocs.io/debug/benchmark.html) 取鏡,學習效能評比框架的開發;