標籤:blog http 使用 strong 資料 art
- struct kfifo {
- unsigned char *buffer; /* the buffer holding the data */
- unsigned int size; /* the size of the allocated buffer */
- unsigned int in; /* data is added at offset (in % size) */
- unsigned int out; /* data is extracted from off. (out % size) */
- spinlock_t *lock; /* protects concurrent modifications */
- };
- struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock)
- {
- unsigned char *buffer;
- struct kfifo *ret;
-
- /*
- * round up to the next power of 2, since our ‘let the indices
- * wrap‘ tachnique works only in this case.
- */
- if (size & (size - 1)) {
- BUG_ON(size > 0x80000000);
- size = roundup_pow_of_two(size);
- }
-
- buffer = kmalloc(size, gfp_mask);
- if (!buffer)
- return ERR_PTR(-ENOMEM);
-
- ret = kfifo_init(buffer, size, gfp_mask, lock);
-
- if (IS_ERR(ret))
- kfree(buffer);
-
- return ret;
- }
這裡值得一提的是,kfifo->size的值總是在調用者傳進來的size參數的基礎上向2的冪擴充,這是核心一貫的做法。這樣的好處不言而喻--對kfifo->size模數運算可以轉化為與運算,如下:
kfifo->in % kfifo->size 可以轉化為 kfifo->in & (kfifo->size – 1)
在kfifo_alloc函數中,使用size & (size – 1)來判斷size 是否為2冪,如果條件為真,則表示size不是2的冪,然後調用roundup_pow_of_two將之向上擴充為2的冪。 這些都是很常用的技巧,只不過大家沒有將它們結合起來使用而已,下面要分析的__kfifo_put和__kfifo_get則是將kfifo->size的特點發揮到了極致。
3. __kfifo_put和__kfifo_get,巧妙的入隊和出隊操作,無鎖並發
__kfifo_put是入隊操作,它先將資料放入buffer裡面,最後才修改in參數;__kfifo_get是出隊操作,它先將資料從buffer中移走,最後才修改out。你會發現in和out兩者各司其職。電腦科學家已經證明,當只有一個讀經程和一個寫線程並行作業時,不需要任何額外的鎖,就可以確保是安全執行緒的,也即kfifo使用了無鎖編程技術,以提高kernel的並發。
下面是__kfifo_put和__kfifo_get的代碼
[cpp] view plaincopyprint?
- unsigned int __kfifo_put(struct kfifo *fifo,
- unsigned char *buffer, unsigned int len)
- {
- unsigned int l;
-
- len = min(len, fifo->size - fifo->in + fifo->out);
-
- /*
- * Ensure that we sample the fifo->out index -before- we
- * start putting bytes into the kfifo.
- */
-
- smp_mb();
-
- /* first put the data starting from fifo->in to buffer end */
- l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
- memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
-
- /* then put the rest (if any) at the beginning of the buffer */
- memcpy(fifo->buffer, buffer + l, len - l);
-
- /*
- * Ensure that we add the bytes to the kfifo -before-
- * we update the fifo->in index.
- */
-
- smp_wmb();
-
- fifo->in += len;
-
- return len;
- }
-
- unsigned int __kfifo_get(struct kfifo *fifo,
- unsigned char *buffer, unsigned int len)
- {
- unsigned int l;
-
- len = min(len, fifo->in - fifo->out);
-
- /*
- * Ensure that we sample the fifo->in index -before- we
- * start removing bytes from the kfifo.
- */
-
- smp_rmb();
-
- /* first get the data from fifo->out until the end of the buffer */
- l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
- memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
-
- /* then get the rest (if any) from the beginning of the buffer */
- memcpy(buffer + l, fifo->buffer, len - l);
-
- /*
- * Ensure that we remove the bytes from the kfifo -before-
- * we update the fifo->out index.
- */
-
- smp_mb();
-
- fifo->out += len;
-
- return len;
- }