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,则不提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果某个线程尝试解除锁定的互斥锁 未锁定 或 不是由该线程锁定,则将产生不确定的行为。

如果互斥锁类型为 PTHREAD_MUTEX_ERRORCHECK,则会提供错误检查。如果某个线程尝试重新锁定的互斥锁已经由该线程锁定,则将返回错误。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
如果互斥锁类型为 PTHREAD_MUTEX_RECURSIVE,则该互斥锁会保留锁定计数这一概念。线程首次成功获取互斥锁时,锁定计数会设置为 1。线程每重新锁定该互斥锁一次,锁定计数就增加 1。线程每解除锁定该互斥锁一次,锁定计数就减小 1。 锁定计数达到 0 时,该互斥锁即可供其他线程获取。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
如果互斥锁类型是 PTHREAD_MUTEX_DEFAULT,则尝试以递归方式锁定该互斥锁将产生不确定的行为。对于不是由调用线程锁定的互斥锁,如果尝试解除对它的锁定,则会产生不确定的行为。如果尝试解除锁定尚未锁定的互斥锁,则会产生不确定的行为。

 

条件变量:

#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);

 

posted @ 2015-07-03 15:59  xiaokuang  阅读(421)  评论(0编辑  收藏  举报