---
# System prepended metadata

title: 2021-04-06 Julian-Chu
tags: [linux2021]

---

# 2021-04-06 Julian-Chu
contributed by < [Julian-Chu](https://github.com/Julian-Chu) >
###### tags: `linux2021`
> [GitHub](https://github.com/Julian-Chu/linux2021-quiz7)

## Part1: Unix like shell

```cpp=
/* Recursively run right-most part of the command line printing output to the    
 * file descriptor @t                                                                                                                                                                  
 */    
static void run(char *c, int t)    
{    
    char *redir_stdin = NULL, *redir_stdout = NULL;    
    int pipefds[2] = {0, 0}, outfd = 0;    
    char *v[99] = {0};    
    char **u = &v[98]; /* end of words */    
    for (;;) {    
        // move to last non-null char    
        c--;    
        if (is_delim(*c)) /* if NULL (start of string) or pipe: break */    
            break;    
        if (!is_special(*c)) {    
            // move back to null termination    
            c++;    /* Copy word of regular chars into previous u */    
            /* 在此提交你的程式碼 */    
            *c = '\0';    
            c--;    
            u--;    
            while(!is_special(*c)){    
                c--;    
            }    
            *u = (c+1);    
        }    
    
        if (is_redir(*c)) { /* If < or > */    
            if (*c == '<')    
                redir_stdin = *u;    
            else    
                redir_stdout = *u;    
            if ((u - v) != 98)    
                u++;    
        }    
    }    
    if ((u - v) == 98) /* empty input */    
        return;   
```

#### 解說：
原本 c 可能會是 `"0echo hi\0"`, 經過處理後變成 `"0echo\0hi\0"`
然後將v[index] 指向 c 裡面每個命令開頭 char 的位置, 
`v[98] = "hi\0"`
`v[97] = "echo\0"`
這樣 v 就可以符合 `execvp` 的參數簽章

#### ref
`man pipe`
`man execvp`: 傳入命令字串陣列

## Part2: httpd
```cpp=
static void dequeue(queue_t *q, int *fd)    
{    
    node_t *old_head;    
    pthread_mutex_lock(q->head_lock);    
    /* Wait until signaled that queue is non_empty.    
     * Need while loop in case a new thread manages to steal the queue    
     * element after the waiting thread is signaled, but before it can    
     * re-acquire head_lock.    
     */    
    /* 在此提交你的程式碼 */;    
    while(q->head == q->tail) pthread_cond_wait(q->non_empty, q->head_lock);    
    old_head = q->head->next;    
    q->head = q->head->next;    
    q->size--;                                                                                                                                                                         
    *fd = old_head->fd;    
    pthread_mutex_unlock(q->head_lock);    
    free(old_head);     
}  
```
問題1: 執行緒函式 greeter_routine 和 worker_routine 在上述網頁伺服器中，分別負責什麼功能？
* greeter: 負責取得可用的 socket connection, 將 connection 轉換成 file descriptor 後放入 queue
* worker: 從 queue 之中取得可用的 fd,  從 fd 中讀取資料, 做 request 的解析, 並回傳 response

問題2: enqueue 和 dequeue 是單向鏈結串列 (singly-linked list) 所實作的佇列 (queue) 裡頭的基本操作，你如何驗證自己的實作能在並行環境正確無誤？

答: 利用 [Thread Sanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html) 看在 benchmark 的情況下, 是否有發生 data race 的情況

---
## Part3: ringbuffer


```cpp=
/* 提供你的實作 */
#define ALIGN_FLOOR(val, align) (((typeof(val))(val + ((typeof(val))align-1)))&(~((typeof(val))align-1))) 



static inline int ringbuffer_sp_do_enqueue(ringbuf_t *r,    
                                           void *const *obj_table,    
                                           const unsigned n)    
{    
    uint32_t mask = r->prod.mask;    
    uint32_t prod_head = r->prod.head, cons_tail = r->cons.tail;    
    /* The subtraction is done between two unsigned 32-bits value (the result    
     * is always modulo 32 bits even if we have prod_head > cons_tail). So    
     * @free_entries is always between 0 and size(ring) - 1.    
     */                                                                           
    uint32_t free_entries = mask + cons_tail - prod_head;    
    
    /* check that we have enough room in ring */    
    if ((n > free_entries))                                                     
        return -ENOBUFS;                                                        
                                                                                
    uint32_t prod_next = prod_head + n;                                         
    r->prod.head = prod_next;                                                   
                                                                                
    /* write entries in ring */                                                 
    ENQUEUE_PTRS();                                                             
    __compiler_barrier();                                                       
                                                                                
    r->prod.tail = prod_next;                                                   
                                                                                
    /* if we exceed the watermark */    
    /* 請提交你的實作 */     
    if((cons_tail - prod_head + 1 + n) > r->prod.watermark){    
       return -EDQUOT;                                                          
    }    
    return 0;    
} 
```
:::warning
q1: 這邊有疑問的是`ALIGN_FLOOR`, 因為我的想法對齊應該是 round up/CEIL, 所以實作上是找到下一個 64byte 的倍數, 但就跟 macro 的命名不合?
:::
問題1: 上述程式碼的 watermark 有何作用？跟 lock-less 演算法有何關聯？
答：標記已處理資料的位置, 避免覆蓋掉尚未處理的資料。與 lock-less 演算法的關係, CAS的判斷條件(?)

問題2: 除了題目提到的 SPSC (single-producer and single-consumer)，該如何擴展為 single producer + multiple consumer (SPMC) 和 single producer + multiple consumer (MPMC) 呢？請簡述你的手段，都針對 fixed-size queue。
答: 利用 lock 或是 atomic operation 避免 data race


## Part4: mbus

```cpp=
static bool execute_client_callback(bus_client_t *client, void *msg)                                                                                                                   
{    
    /* Load the client with which we are attempting to communicate. */    
    bus_client_t local_client;    
    __atomic_load(client, &local_client, __ATOMIC_SEQ_CST);    
    
    /* Loop until reference count isupdated or client becomes unregistered */    
    while (local_client.registered) {    
        /* The expected reference count is the current one + 1 */    
        bus_client_t new_client = local_client;    
        ++(new_client.refcnt);    
      
        /* If CAS succeeds, the client had the expected reference count, and    
         * we updated it successfully. If CAS fails, the client was updated    
         * recently. The actual value is copied to local_client.    
         */    
    
        /* 提交你的實作 */ ;    
        if(CAS(&client->refcnt, &new_client.refcnt, &local_client.refcnt)){
            client->callback(client->ctx, msg);
            return true;    
        }    
        __atomic_load(&client->refcnt, &local_client.refcnt, __ATOMIC_SEQ_CST);    
                 
    }                         
     
    /* Client was not registered or got unregistered while we attempted to send    
     * a message    
     */    
    return false;    
} 
```

問題1: mbus.c 貌似彆扭的 callback function 設計，能夠解決什麼問題？
答：解耦執行緒之間的通訊跟資料傳送, e.g. thread 1不需要知道 thread 2 的具體函數即可以調用

問題2: 考慮到訊息處理的數量可能相當大，我們可事先建立 thread pool 並透過 Processor affinity 讓執行緒在執行 callback function 時，儘量不要相互干擾。請簡述你打算如何修改程式碼，以實現這目標，可斟酌列出關鍵程式碼。
答: 可考慮在 thread_func 引入`sched_setaffinity`指令, 並在 ctx 指定要綁定的 CPU ID
