###### tags: `Linux kernel` # spinlock踩坑記趣 spinlock這東西對我來說還是有點新奇,就僅止於「大概知道他是busy waiting」的critical section保護機制。之前改過semaphore的bug,還實做過mutex,但是spinlock就沒有了。之前去面試還被面到這一題,當時跟面試人員討論spinlock在單CPU上面的用途,對方的結論是單CPU上面就只剩下disable interrupt的功能。 乍看之下spinlock好像是很沒效率的事情。busy waiting?還加上disable interrupt?這都是非常危險的事情啊!不說disable interrupt,單是busy waiting那我8個CPU開8個thread搶同一個lock那豈不是馬上卡死。 後來看了一些網上的討論,說到其實他的busy waiting也不是多busy,主要會有兩個內圈和外圈的測試(好像叫做Test&Test&Set),內圈會下一些instruction讓CPU睡下去,直到bus上面有event傳來,這個event主要就是告知說,你等的東西有update啦,快來撈撈看能不能解lock吧,於是CPU可以再次測試拿鎖。當然再讀下去還會有一些議題,像是CPU topology上較遠的CPU就比較沒機會拿到鎖了,和之後Linux上面的spinlock ticket解法,這個就先不研究了,先來實驗看看所謂的spinlock busy waiting,到底有多busy呢? 首先在main() function裡面,開十個thread,大家競爭同一個spinlock鎖,然後看看CPU使用率怎麼樣? ``` int main(void) { spin_lock_init(&lock); int err; for(int i=0; i<MAX_SL_THREADS; i++) { err = pthread_create(&(tid[i]), NULL, &doSpinLock, NULL); if (err != 0) printf("Can't create thread :[%s]\n", strerror(err)); else printf("Thread created successfully\n"); } return 0; } ``` 結果是死活編不過: ``` test_spinlock.c:(.text+0x7e): undefined reference to `spin_lock_init' /usr/bin/ld: test_spinlock.c:(.text+0xb7): undefined reference to `pthread_create' ``` 為什麼會這樣呢?我有include linux/spinlock.h了啊...看了看才發現,啊...spinlock是給kernel module用的,我這個是user space program,所以不給用。 那怎麼辦呢?查了一下有另一組API ``` #include <pthread.h> int pthread_spin_lock(pthread_spinlock_t *lock); int pthread_spin_trylock(pthread_spinlock_t *lock); int pthread_spin_unlock(pthread_spinlock_t *lock); ``` 不過這裡又衍生出一個問題: 不是說spinlock只給kernel用嗎?那這一組pthread API為什麼可以用,實做上面有什麼差異嗎? 先不管這個問題,跑跑看再說,還要記得編的時候gcc加上-thread才編的過(看書的時候都嗯嗯嗯,有道理,這我懂,用的時候到處碰壁才會知道更多細節): ``` #include <stdio.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> //#include <linux/spinlock.h> #define MAX_SL_THREADS 8 pthread_t tid[MAX_SL_THREADS]; static pthread_spinlock_t lock; void* doSpinLock(void *arg) { pthread_spin_lock(&lock); printf("Getting lock! Now do garbage things...\n"); // Doing garbage things... int j=0; for(int i=0; i<(0xFFFFFFFF);i++){ j++; if((j%0x10000000) == 0) printf("j=%d\n", j); } printf("Release lock...\n"); pthread_spin_unlock(&lock); } int main(void) { pthread_spin_init(&lock, PTHREAD_PROCESS_SHARED); int err; for(int i=0; i<MAX_SL_THREADS; i++) { err = pthread_create(&(tid[i]), NULL, &doSpinLock, NULL); if (err != 0) printf("Can't create thread :[%s]\n", strerror(err)); else printf("Thread created successfully\n"); } for(int i=0; i<MAX_SL_THREADS; i++) pthread_join(tid[i], NULL); return 0; } ``` main()生出8個threads,每個thread試著拿到lock,拿到後開始做一些耗時的工作,一看一開始的CPU usage,哇不得了,還真的是貨真價實的busy waiting...  有空的時候再來研究看看kernel mode spinlock實做到底有什麼不一樣,不過我想我應該是被這樣的結果勸退spinlock了吧,除非是像之前用的driver code裡面只lock並修改一個field,這種簡單的情境,用簡單的spinlock來鎖,否則user space還是少用的好。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up