信号量与自旋锁(3): Linux信号量的实现
2007-06-30 13:19
267 查看
1. 定义:
头文件: <asm/semaphore.h>
数据类型: struct semaphore
直接创建:
void sema_init(struct semaphore *sem, int val); /* 其中val是信号量的初始值 */
辅助宏:
DECLARE_MUTEX(name); /* 把一个称为name的信号量变量初始化为1 */
DECLARE_MUTEX_LOCKED(name); /* 把一个称为name的信号量变量初始化为0 */
动态分配:
/* 用于运行时的初始化 */
void init_MUTEX(struct semaphore *sem);
void init_MUTEX_LOCKED(struct semaphore *sem);
在Linux世界中, P函数被称为down, 指的是该函数减小了信号量的值, 它也许会将调用者置于休眠状态, 然后等待信号量变得可用, 之后再授予调用者对被保护资源的访问权限. down函数有三个版本:
/* 减小信号量的值, 并在必要时一直等待 */
void down(struct semaphore *sem);
/* 可中断版本, 常用 */
void down_interruptible(struct semephore *sem);
作为通常规则, 我们不应该使用非中断版本.
使用该函数, 如果操作被中断, 该函数会返回非0值, 而调用者不会拥有该信号量.
因此对该函数的正确使用需要始终检查返回值, 并做出相应的响应.
/* 永远不会休眠, 如信号量在调用时不可获得, 立即返回非0值 */
void down_trylock(struct semaphore *sem);
当一个线程成功调用down函数的某个版本之后, 就称为该线程拥有了该信号量, 可以访问被该信号量保护的临界区. 当互斥操作完成后, 必须释放该信号量.
Linux的V函数是up:
/* 调用up之后, 调用者不再拥有该信号量 */
void up(struct semaphore *sem);
2. 举例:
我们拥有一个共享数据结构:
struct st_data
{
char name[32];
char data[128];
int data_len;
};
这个数据结构被多个进程同时访问.
为了避免这些进程在访问该结构时产生竞态, 我们在该结构的底部为其加上信号量:
struct st_data
{
char name[32]; /* name */
char data[128]; /* data */
int data_len; /* data length */
struct semaphore sem; /* semaphore */
};
信号量在使用前必须进行初始化, 而且是在共享数据其他部分可用前初始化. 因此, 我们在其他数据赋值之前调用init_MUTEX, 否则会建立一个竞态, 即在信号量准备好之前, 有代码可能会访问它们.
st_data data;
init_MUTEX(&data->sem);
setup_data(&data); /* 初始化数据 */
...
...
接下来, 我们必须仔细检查代码, 确保在不拥有该信号量的时候不会访问data数据结构. 例如, 在data_write的开始处加入:
if (down_interruptible(&data->sem))
return -ERESTARTSYS;
这是检查down_interruptible的返回值, 如果返回非0值, 说明操作被中断. 这种情况下, 通常要做的工作是返回-ERESTARTSYS. 在得到这个返回值后, 内核会从头重新启动该调用, 或者将该错误返回给用户.
如果我们返回-ERESTARTSYS, 则必须首先撤销已经做出的修改, 这样, 系统调用才可正确重试. 如果无法撤销这些操作, 则应该返回-EINTR, 表明中断.
不管data_write能否完成其他工作, 它都必须释放信号量:
out:
up(&data->sem);
return retval;
在data_write中有几个地方可能会产生错误, 包括内存分配失败等. 在这些情况下, 代码会执行goto out, 确保正确的完成信号量的释放工作.
头文件: <asm/semaphore.h>
数据类型: struct semaphore
直接创建:
void sema_init(struct semaphore *sem, int val); /* 其中val是信号量的初始值 */
辅助宏:
DECLARE_MUTEX(name); /* 把一个称为name的信号量变量初始化为1 */
DECLARE_MUTEX_LOCKED(name); /* 把一个称为name的信号量变量初始化为0 */
动态分配:
/* 用于运行时的初始化 */
void init_MUTEX(struct semaphore *sem);
void init_MUTEX_LOCKED(struct semaphore *sem);
在Linux世界中, P函数被称为down, 指的是该函数减小了信号量的值, 它也许会将调用者置于休眠状态, 然后等待信号量变得可用, 之后再授予调用者对被保护资源的访问权限. down函数有三个版本:
/* 减小信号量的值, 并在必要时一直等待 */
void down(struct semaphore *sem);
/* 可中断版本, 常用 */
void down_interruptible(struct semephore *sem);
作为通常规则, 我们不应该使用非中断版本.
使用该函数, 如果操作被中断, 该函数会返回非0值, 而调用者不会拥有该信号量.
因此对该函数的正确使用需要始终检查返回值, 并做出相应的响应.
/* 永远不会休眠, 如信号量在调用时不可获得, 立即返回非0值 */
void down_trylock(struct semaphore *sem);
当一个线程成功调用down函数的某个版本之后, 就称为该线程拥有了该信号量, 可以访问被该信号量保护的临界区. 当互斥操作完成后, 必须释放该信号量.
Linux的V函数是up:
/* 调用up之后, 调用者不再拥有该信号量 */
void up(struct semaphore *sem);
2. 举例:
我们拥有一个共享数据结构:
struct st_data
{
char name[32];
char data[128];
int data_len;
};
这个数据结构被多个进程同时访问.
为了避免这些进程在访问该结构时产生竞态, 我们在该结构的底部为其加上信号量:
struct st_data
{
char name[32]; /* name */
char data[128]; /* data */
int data_len; /* data length */
struct semaphore sem; /* semaphore */
};
信号量在使用前必须进行初始化, 而且是在共享数据其他部分可用前初始化. 因此, 我们在其他数据赋值之前调用init_MUTEX, 否则会建立一个竞态, 即在信号量准备好之前, 有代码可能会访问它们.
st_data data;
init_MUTEX(&data->sem);
setup_data(&data); /* 初始化数据 */
...
...
接下来, 我们必须仔细检查代码, 确保在不拥有该信号量的时候不会访问data数据结构. 例如, 在data_write的开始处加入:
if (down_interruptible(&data->sem))
return -ERESTARTSYS;
这是检查down_interruptible的返回值, 如果返回非0值, 说明操作被中断. 这种情况下, 通常要做的工作是返回-ERESTARTSYS. 在得到这个返回值后, 内核会从头重新启动该调用, 或者将该错误返回给用户.
如果我们返回-ERESTARTSYS, 则必须首先撤销已经做出的修改, 这样, 系统调用才可正确重试. 如果无法撤销这些操作, 则应该返回-EINTR, 表明中断.
不管data_write能否完成其他工作, 它都必须释放信号量:
out:
up(&data->sem);
return retval;
在data_write中有几个地方可能会产生错误, 包括内存分配失败等. 在这些情况下, 代码会执行goto out, 确保正确的完成信号量的释放工作.
相关文章推荐
- 同步事件,信号量,互斥,临界区,线程,线程池C++实现(win32,linux)
- linux 内核同步机制-自旋锁与信号量及其区别
- Linux平台用C++实现信号量,同步线程
- linux多线程学习(六)——信号量实现同步。
- linux多线程学习(六)——信号量实现同步
- linux 内核同步机制-自旋锁与信号量及其区别
- Linux下用信号量实现对共享内存的访问保护
- Linux平台用C++实现信号量,同步线程
- Linux C++Timer(用信号量实现)
- linux c++多线程 线程私有数据 互斥量 条件变量 信号量 读写锁 自旋锁 屏障
- 生产者-消费者问题实现 (linux下C同步信号量和互斥信号量的应用)
- linux 线程间使用信号量实现消费者生产者
- linux 内核同步机制-自旋锁与信号量及其区别
- linux 命名信号量实现进程间的互斥与同步
- Linux 内核同步之自旋锁与信号量的异同
- Linux信号量的实现(笔记)
- Linux下用信号量实现对共享内存的访问保护(一)
- 【Linux】生产者消费者编程实现-线程池+信号量
- Linux下共享内存+信号量实现
- linux 内核自旋锁spinlock实现详解(基于ARM处理器)