信号量(semaphore)是用于保护临界区的一种常用方法。只有得到信号量的进程才能执行临界区代码,而没有得到信号量的进程进入休眠等待状态。

Linux系统中与信号量相关的操作主要有如下4种。

1 定义信号量

下面代码定义名为sem的信号量。

struct semaphore sem;

struct semaohore结构体在内核中定义如下:

在/include/linux/semaphore.h目录下:

struct semaphore{

spinlock_t              lock;

unsigned int           count;

struct list_head       wait_list;

};

2初始化信号量

在/include/linux/semaphore.h目录下,void sema_init(struct semaphore*

sem, int val) 函数用于初始化信号量,并设置信号量sem的值为val。尽管信号量可以被初始化为大于1的值从而成为一个计数信号量,但是它通常不被这样使用。

内核定义了两个宏来把sem的值设置为1或者0。

#define init_MUTEX(sem)                  sema_init(sem, 1)

#define init_MUTEX_LOCKED(sem)         sema_init(sem, 0)

使用init_MUTEX(sem) 初始化信号量时,表示信号量最初是可以被获取的。而使用init_MUTEX_LOCKED(sem) 初始化信号量时,此信号量只有先被释放才可以获取。

3获取信号量

void down(struct semaphore *sem);

该函数用于获取信号量sem,它会导致睡眠,因此不能在中断上下文使用。

在内核里该函数的源代码如下:

在kernel/semaphore.c文件里:

53 void down(struct semaphore *sem)

54 {

55         unsigned long flags;

56

57         spin_lock_irqsave(&sem->lock, flags);

58         if (likely(sem->count > 0))

59                 sem->count--;

60         else

61                 __down(sem);

62         spin_unlock_irqrestore(&sem->lock, flags);

63 }

这里重点看58行:if (likely(sem->count > 0)),这句话表示当获取信号量成功时,就执行sem->count—; 即对信号量的值减一。else表示获取信号量失败,此时调用__down函数进入睡眠状态,并将此进程插入到等待队列尾部。

内核定义了信号量的等待队列结构体:

193 struct semaphore_waiter {

194         struct list_head list;

195         struct task_struct *task;

196         int up;

197 };

此结构体是一个双向循环链表。

int down_interruptible(struct semaphore *sem);

该函数功能与down()类似,不同之处是,down()在获取信号量失败进入睡眠状态时的进程是不能被打断的,而down_interruptible()在进入睡眠状态时的进程能被信号打断,信号也会导致函数返回。下面我们也来看一看这个函数的源码:

在kernel/semaphore.c文件里:

75 int down_interruptible(struct semaphore *sem)

76 {

77         unsigned long flags;

78         int result = 0;

79

80         spin_lock_irqsave(&sem->lock, flags);

81         if (likely(sem->count > 0))

82                 sem->count--;

83         else

84                 result = __down_interruptible(sem);

85         spin_unlock_irqrestore(&sem->lock, flags);

86

87         return result;

88 }

这里我们可以看到,当获取信号量成功时,返回0,而获取信号量失败时,返回一个非0的值。在使用down_interruptible()函数获取信号量时,对返回值一般会进行检查,如果非0,通常立即返回-ERESTARTSYS。如:

if ( down_interruptible(&sem) )

return -ERESTARTSYS;

这里还有一个问题:在获取信号量失败后,为什么down不能被中断,而down_interruptible却可以被中断呢?我们从down和down_interruptible的源代码可以得知,在获取信号量失败后,down函数运行了__down函数,而down_interruptible函数运行了__down_interruptible。那么让我们来看一下这两个函数的源码:

在kernel/semaphore.c文件里:

236 static noinline void __sched __down(struct semaphore *sem)

237 {

238   __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);

239 }

240

241 static noinline int __sched __down_interruptible(struct semaphore *sem)

242 {

243 return __down_common(sem,TASK_INTERRUPTIBLE,                     MAX_SCHEDULE_TIMEOUT);

244 }

在__down函数里,是把进程的状态设置为TASK_UNINTERRUPTIBLE ,即不可中断状态。

而在__down_interruptible里,是把进程的状态设置为TASK_INTERRUPTIBLE ,即可中断状态。这就解释了以上提出的问题。

4释放信号量

void up(struct semaphore *sem);

该函数用于释放信号量sem,唤醒等待者。

它的源代码如下:

178 void up(struct semaphore *sem)

179 {

180         unsigned long flags;

181

182         spin_lock_irqsave(&sem->lock, flags);

183         if (likely(list_empty(&sem->wait_list)))

184                 sem->count++;

185         else

186                 __up(sem);

187         spin_unlock_irqrestore(&sem->lock, flags);

在183行的语句中,up函数首先判断等待队列是否为空,如果是空的话,就执行sem->count++;否则,执行__up() 函数,释放掉等待队列尾部的信号量。

信号量用于同步举例:

如果信号量被初始化为0,则它可以用于同步,同步意味着一个执行单元的继续执行需要等待另一个执行单元完成某事,保证执行的先后顺序。

如上图所示,执行单元A执行代码区域a之前,必须等待执行单元B执行完代码区域b后释放信号量给它。

以下模块很好地使用了信号量:

#include <linux/init.h>

#include <linux/module.h>

#include <linux/sched.h>

#include <linux/sem.h>

struct semaphore sem1;

struct semaphore sem2;

int num[2][5] = {

{0,2,4,6,8},

{1,3,5,7,9}

};

int thread_one(void *p);

int thread_two(void *p);

int thread_one(void *p)

{

int *num = (int *)p;

int i;

for(i = 0; i < 5; i++){

down(&sem1);      //获取信号量1

printk("%d ", num[i]);

up(&sem2);    //释放信号量2

}

return 0;

}

int thread_two(void *p)

{

int *num = (int *)p;

int i;

for(i = 0; i < 5; i++){

down(&sem2);             //获取信号量2

printk("%d ", num[i]);

up(&sem1);           //释放信号量1

}

return 0;

}

static int lan_init(void)

{

printk("lan is coming\n");

init_MUTEX(&sem1);  //初始化信号量1, 使信号量1最初可被获取

init_MUTEX_LOCKED(&sem2);  //初始化信号量2,使信号量2只有被释放后才可被获取

kernel_thread(thread_one, num[0], CLONE_KERNEL);

kernel_thread(thread_two, num[1], CLONE_KERNEL);

return 0;

}

static void lan_exit(void)

{

printk("\nlan exit\n");

}

module_init(lan_init);

module_exit(lan_exit);

本文来源:谁不小心的CSDN博客 临界资源 互斥访问 内核中的up和down函数

临界资源 互斥访问 内核中的up和down函数相关推荐

  1. 什么是临界资源计算机网络,临界资源互斥访问

    分布式互斥是随着分布式系统的出现而出现的,并随着分布式系统理论发展而发展.在分布式系统中,很多进程能够在微观上并行执行.但由于共享资源的有限性,以及全局数据要求的一致性,一些临界资源的访问需要以互斥的 ...

  2. 在内核中C语言实现htons()函数

    Htons():[摘自百度百科] htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为:高位字节存放在内存的低地址处. 网络字节顺序是TCP/IP中规定好的一种数据 ...

  3. linux 打印函数宏,linux内核中的嵌入式汇编宏函数

    在看linux内核代码时,常会遇到诸如:static inline _syscall0(int,fork)这样的函数.经查阅资料,发现该函数是嵌入式汇编宏函数. linux内核提供了7个非常有用的宏定 ...

  4. linux中min函数用法,linux内核中的min、max函数

    这些天为了整理一下前段时间看ldd3时所学的驱动知识,所以就去看了看usb驱动.不看不知道,一看吓一跳,里面有很多语法我发现用的太好了,不像我们平时那样写代码.里面写的代码真是太好了.然而要理解到里面 ...

  5. 如何使用Linux内核中没有被导出的变量或函数

    更多文章目录:点击这里 GitHub地址:https://github.com/ljrkernel Linux 内核为了减少命名空间的污染,并做到正确的信息隐藏,内核提供了管理内核符号可见性的方法.不 ...

  6. Linux内核中的proc文件系统

    简介 procfs文件系统是内核中的一个特殊文件系统.它是一个虚拟文件系统: 它不是实际的存储设备中的文件,而是存在于内存中.procfs中的文件是用来允许用户空间的程序访问内核中的某些信息(比如进程 ...

  7. 内核中_init,_exit中的作用

    __init, __initdata等属性标志,是要把这种属性的代码放入目标文件的.init.text节,数据放入.init.data节──这一过程是通过编译内核时为相关目标平台提供了xxx.lds链 ...

  8. linux 内核io操作,关于Linux内核中的异步IO的使用

    我们都知道异步IO的作用,就是可以提高我们程序的并发能力,尤其在网络模型中.在linux中有aio的一系列异步IO的函数接口,但是这类函数都是glibc库中的函数,是基于多线程实现,不是真正的异步IO ...

  9. linux内核中的文件描述符(四)--fd的分配--get_unused_fd

    linux内核中的文件描述符(四)--fd的分配--get_unused_fd Kernel version:2.6.14 CPU architecture:ARM920T Author:ce123( ...

最新文章

  1. The Tail at Scale
  2. html中列表导航怎么和图片对齐_HTML实战篇:html仿百度首页
  3. vbs按钮传递过程_哈希传递攻击仍然是一种威胁
  4. 转→js数组遍历 千万不要使用for...in...
  5. django新闻页面编写
  6. 20100823工作记录
  7. 解决手机端上的iframe无法触摸滚动
  8. 【2012.1.24更新】不要再在网上搜索eclipse的汉化包了!
  9. js代码混淆 webpack-obfuscator
  10. 一、Python数据挖掘(环境篇——Anaconda与Jupyter Notebook)
  11. 包学会之浅入浅出Vue.js:开学篇(转)
  12. 业务层战略制定的思路和方法_如何确保公司年度战略目标落地—打造战略执行的方法论...
  13. 2015年3月CCF软考试题
  14. iOS App Security and Analysis: Part 1/2
  15. 2-1个人小程序注册
  16. python处理金融数据_Python金融大数据分析-数据获取与简单处理
  17. stm32学习笔记---STM32F4知识
  18. Qt开发高级进阶:如何拷贝生成后的文件到特定文件夹
  19. 作者:​孙少陵(1972-),男,中移(苏州)软件技术有限公司高级工程师、副总经理。...
  20. 开机内存占用过高解决方案

热门文章

  1. 1102 教超冠军卷(20分) -- 测试点1
  2. python学习总结一(快速入门)
  3. C#程序采用AOT发布,真的可以避免被反编译?
  4. 密码学 BugKu滴答~滴
  5. 服务器托管达人谈各地机房托管经验
  6. Android 将依赖完全打入aar包供第三方使用
  7. 最新 M1 版 MacBook 买前必知
  8. 收到银行短信你正在使用Android设备,手机收到扣费短信,无故被扣钱?小心自动订阅的大坑!...
  9. mysql中dual表
  10. redhat linux bash 漏洞,GNU Bash 漏洞及修补方法