Linux IPC 同步(一):互斥锁 条件变量
线程范围:
同一个进程内的多个线程访问一些全局变量时,需要保护好临界区,保证不产生脏数据
Linux 中的互斥锁
#include <pthread.h> pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥锁属性设置:
#include <pthread.h> int pthread_mutexattr_init(pthread_mutexattr_t *attr); int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind); int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind); /* copy from glibc -2.17 */ /* Mutex attribute data structure. */ struct pthread_mutexattr { /* Identifier for the kind of mutex. Bit 31 is set if the mutex is to be shared between processes. Bit 0 to 30 contain one of the PTHREAD_MUTEX_ values to identify the type of the mutex. */ int mutexkind; };
互斥锁类型:
如果互斥锁类型为 PTHREAD_MUTEX_NORMAL,则不提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果某个线程尝试解除锁定的互斥锁 未锁定 或 不是由该线程锁定,则将产生不确定的行为。
条件变量:
#include <pthread.h> pthread_cond_t cond = PTHREAD_COND_INITIALIZER; int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); int pthread_cond_destroy(pthread_cond_t *cond); /* Conditional variable attribute data structure. */ struct pthread_condattr { /* Combination of values: Bit 0 : flag whether coditional variable will be shareable between processes. Bit 1-7: clock ID. */ int value; };
注:pthread_cond_wait会先释放mutex,系统将调用线程挂在信号的等待队列上,在函数返回时会重新执行mutex_lock动作。
上锁冲突:
如果是这样的操作顺序:
设想一种最坏的情况:线程A调用pthread_cond_wait后进入睡眠,线程B发送信号后,系统立即调度线程A执行,线程A会因为不能lock到mutex而立即停止
为了避免这种上锁冲突,调整线程B的操作如下:
Posix明确允许调用pthread_cond_signal的线程不必是与之关联的mutex的当前属主。但是,posix也说:如果需要可预见的调度行为,那么pthread_cond_signal的调用线程必须锁住mutex
进程范围:
系统范围内的多个进程使用共享内存通信时,也需要同步
#include <pthread.h> int pthread_mutexattr_setpthread(const pthread_mutexattr_t *attr, int *valptr); int pthread_mutexattr_getpthread(const pthread_mutexattr_t *attr, int value); int pthread_condattr_setpthread(const pthread_mutexattr_t *attr, int *valptr); int pthread_condattr_getpthread(const pthread_mutexattr_t *attr, int value);
value的值可以设置成 PTHREAD_PROCESS_PRIVATE 或者 PTHREAD_PROCESS_SHARED (后者代表进程间共享)
Attention!!!! 现在问题来了:
进程间共享一个互斥锁,当某一个进程持有一个互斥锁时意外退出。内核不会自动清理该同步锁... 使用读写锁,Posix信号量也有相同的问题。
使用System V信号量,应用程序可以设置SEM_UNDO选项来让内核自动清理同步中的信号量
不过,进程终止时,fcntl记录锁是唯一 一个内核总是会自动清理的同步锁类型。
p.s. 即使内核自动清理了,但是还是会有临界区的数据一致性问题,比如进程修改数据中途,咔...挂了...
进程间同步有这么个问题,那么线程呢?
一个线程在持有锁期间终止,要么是自己调用了pthread_exit,要么是另一个线程调用了pthread_cancel。主动退出的话,会知道自己还拿着锁的...后一种情况的话,可以安装将在被取消是调用的清理函数。API如下(实际上这是两个宏):
#include <pthread.h> void pthread_cleanup_push(void (*routine)(void *), void *arg); void pthread_cleanup_pop(int execute);