sem_unlink在man手冊里有這么一段話:
sem_unlink() removes the named semaphore referred to by name.
The semaphore name is removed immediately. The semaphore is
destroyed once all other processes that have the semaphore open close it.
這段話的意思是說,sem_unlink會馬上刪除指定的信號量名,但要等到所有打開該信號量的進程關閉該信號量后才刪除該信號。詳細地說,當進程創建一個有名信號量,會在/dev/shm下生成一個sem.xxx的文件,所有打開該信號量的進程(包括創建它的進程)都會增加該文件的引用計數,並且這個計數由內核管理。當調用sem_unlink時,/dev/shm下的sem.xxx文件會馬上被刪除,但是信號量本身並沒有被刪除,所有已打開該信號量的進程仍能正常使用它。直到所有打開該信號量的進程關閉該信號量后,內核才真正刪除信號量。
因此,信號量跟sem.xxx並不是共存亡的。下面來測試下:
/* sem_create.cpp */
#include <iostream> #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h> #include <unistd.h> #include <cerrno> #include <cstring> int main() { sem_t *psem = sem_open( "/sem_test",O_CREAT,S_IRWXU,1 ); if ( SEM_FAILED == psem ) { std::cout << "sem_open fail:" << strerror(errno) << std::endl; return 0; } int sval = 0; sem_getvalue( psem,&sval ); std::cout << "sleep now ....value is " << sval << std::endl; sleep( 30 ); sem_getvalue( psem,&sval ); std::cout << "wake up ....value is " << sval << std::endl; sem_post( psem ); sem_getvalue( psem,&sval ); std::cout << "raise value and try again ....value is " << sval << std::endl; sem_close( psem ); }
/** * * sem_destruct.cpp */ #include <iostream> #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h> #include <cerrno> #include <cstring> int main() { sem_t *psem = sem_open( "/sem_test",O_CREAT,S_IRWXU,1 ); if ( SEM_FAILED == psem ) { std::cout << "sem_open fail:" << strerror(errno) << std::endl; return 0; } std::cout << "decrease value and remove sem now..." << std::endl; sem_wait ( psem ); sem_close( psem ); sem_unlink( "/sem_test" ); std::cout << "sem should remove from /dev/shm,but not really remove" << std::endl; }
分別編譯上面的文件:
g++ -pthread -o sem_create sem_create.cpp g++ -pthread -o sem_destruct sem_destruct.cpp
先運行sem_create,再運行sem_destruct這時個可以看到輸出為:
xzc@xzc-HP-ProBook-4446s:~/code/sem$ ./sem_create sleep now ....value is 1 xzc@xzc-HP-ProBook-4446s:~/code/sem$ ./sem_destruct decrease value and remove sem now... sem should remove from /dev/shm,but not really remove wake up ....value is 0 raise value and try again ....value is 1
可以看到,即使sem_destruct從/dev/shm把信號量刪除了,ls /dev/shm也確認對應的文件刪除了。但是經過sleep的sem_create進程在wake up后仍能對信號量進行操作。
可以看到,有名信號量一旦打開后,跟名字並沒有多大關系。下面再來測試有名信號量調用sem_unlink但仍有進程在使用,這時另外一個進程又要創建同名信號量的情況
/** * * sem_again.cpp */ #include <iostream> #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h> #include <unistd.h> #include <cerrno> #include <cstring> int main() { sem_t *psem = sem_open( "/sem_test",O_CREAT|O_EXCL,S_IRWXU,1 ); if ( SEM_FAILED == psem ) { std::cout << "sem_open fail:" << strerror(errno) << std::endl; return 0; } int sval = 0; sem_getvalue( psem,&sval ); //std::cout << "sleep now ....value is " << sval << std::endl; //sleep( 30 ); sem_getvalue( psem,&sval ); std::cout << "value is " << sval << std::endl; sem_post( psem ); sem_getvalue( psem,&sval ); std::cout << "raise value and try again ....value is " << sval << std::endl; sem_close( psem ); sem_unlink( "/sem_test" ); }
編譯一下:g++ -pthread -o sem_again sem_again.cpp,然后依次運行sem_create、sem_destruct、sem_again
xzc@xzc-HP-ProBook-4446s:~/code/sem$ ./sem_create sleep now ....value is 1 xzc@xzc-HP-ProBook-4446s:~/code/sem$ ./sem_destruct decrease value and remove sem now... sem should remove from /dev/shm,but not really remove xzc@xzc-HP-ProBook-4446s:~/code/sem$ ./sem_again value is 1 raise value and try again ....value is 2 wake up ....value is 0 raise value and try again ....value is 1
在sem_again中特意使用了O_CREATE|O_EXCL標志,如果已存在同名信號量,則創建失敗。但從上面的結果來看,創建成功並且互不干擾。也就是說,一個有名信號量,在打開並調用sem_unlink后可以當作無名信號量來使用(但這時新的進程還想使用該信號量,不可能了)。
注:上面只做了簡單測試,並未測試sem_unlink后各進程的競爭情況,在生產環境請自行測試。
PS:在debian 6上,不用unistd.h也能調用sleep函數,在ubuntu 14.04上不行。在debian 6上是鏈接的庫是-lrt,在ubuntu 14.04上是-pthread,-lrt不行。linux這是要鬧哪樣。
