【APUE】信号量、互斥体和自旋锁

http://www.cnblogs.com/biyeymyhjob/archive/2012/07/21/2602015.htmlhtml

http://blog.chinaunix.net/uid-20543672-id-3252604.htmllinux

http://bbs.chinaunix.net/thread-2333160-1-1.html数据结构

1、信号量ide

      信号量又称为信号灯,它是用来协调不一样进程间的数据对象的,而最主要的应用是共享内存方式的进程间通讯。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取情况。通常说来,为了得到共享资源,进程须要执行下列操做: 函数

   (1) 测试控制该资源的信号量。 
   (2) 若此信号量的值为正,则容许进行使用该资源。进程将信号量减1。 
   (3) 若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1)。 
   (4) 当进程再也不使用一个信号量控制的资源时,信号量值加1。若是此时有进程正在睡眠等待此信号量,则唤醒此进程。 
post

维护信号量状态的是Linux内核操做系统而不是用户进程。咱们能够从头文件/usr/src/linux/include/linux/sem.h 中看到内核用来维护信号量状态的各个结构的定义。信号量是一个数据集合,用户能够单独使用这一集合的每一个元素。要调用的第一个函数是semget,用以得到一个信号量ID。测试

Linux2.6.26下定义的信号量结构体:ui

struct semaphore {
        spinlock_t                lock;
        unsigned int             count;
        struct list_head        wait_list;
};

从以上信号量的定义中,能够看到信号量底层使用到了spin lock的锁定机制,这个spinlock主要用来确保对count成员的原子性的操做(count--)和测试(count > 0)atom

2、互斥体spa

      互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(因此名为互斥体(mutex))。互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section)。所以,在任意时刻,只有一个线程被容许进入这样的代码保护区。

  任何线程在进入临界区以前,必须获取(acquire)与此区域相关联的互斥体的全部权。若是已有另外一线程拥有了临界区的互斥体,其余线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。
  何时须要使用互斥体呢?互斥体用于保护共享的易变代码,也就是,全局或静态数据。这样的数据必须经过互斥体进行保护,以防止它们在多个线程同时访问时损坏

struct mutex {
        /* 1: unlocked, 0: locked, negative: locked, possible waiters */
        atomic_t                  count;
        spinlock_t                wait_lock;
        struct list_head          wait_list;
#ifdef CONFIG_DEBUG_MUTEXES
        struct thread_info        *owner;
        const char                *name;
        void                      *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map         dep_map;
#endif
};

对比前面的struct semaphore,struct mutex除了增长了几个做为debug用途的成员变量外,和semaphore几乎长得同样。可是mutex的引入主要是为了提供互斥机制,以免多个进程同时在一个临界区中运行。

从原理上讲,mutex其实是count=1状况下的semaphore,底层实现和semaphore几乎同样。

3、自旋锁

  自旋锁它是为为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较相似,它们都是为了解决对某项资源的互斥使用。不管是互斥锁,仍是自旋锁,在任什么时候刻,最多只能有一个保持者,也就说,在任什么时候刻最多只能有一个执行单元得到锁。可是二者在调度机制上略有不一样。对于互斥锁,若是资源已经被占用,资源申请者只能进入睡眠状态。可是自旋锁不会引发调用者睡眠,若是自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是所以而得名。

自旋锁通常原理

  跟互斥锁同样,一个执行单元要想访问被自旋锁保护的共享资源,必须先获得锁,在访问完共享资源后,必须释放锁。若是在获取自旋锁时,没有任何执行单元保持该锁,那么将当即获得锁;若是在获取自旋锁时锁已经有保持者,那么获取锁操做将自旋在那里,直到该自旋锁的保持者释放了锁。由此咱们能够看出,自旋锁是一种比较低级的保护数据结构或代码片断的原始方式,这种锁可能存在两个问题:死锁和过多占用cpu资源

自旋锁适用状况

  自旋锁比较适用于锁使用者保持锁时间比较短的状况。正是因为自旋锁使用者通常保持锁时间很是短,所以选择自旋而不是睡眠是很是必要的,自旋锁的效率远高于互斥锁信号量和读写信号量适合于保持时间较长的状况,它们会致使调用者睡眠,所以只能在进程上下文使用(若是在中断上下文中使用,调用者是系统进程,可能引发系统进程睡眠),而自旋锁适合于保持时间很是短的状况,它能够在任何上下文使用。若是被保护的共享资源只在进程上下文访问,使用信号量保护该共享资源很是合适,若是对共享资源的访问时间很是短,自旋锁也能够。可是若是被保护的共享资源须要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是能够被抢占的。自旋锁只有在内核可抢占或SMP(多处理器)的状况下才真正须要,在单CPU且不可抢占的内核下,自旋锁的全部操做都是空操做。另外格外注意一点:自旋锁不能递归使用

4、信号量、互斥体和自旋锁的区别

信号量/互斥体和自旋锁spinlock的区别

信号量/互斥体容许进程睡眠属于睡眠锁,自旋锁则不容许调用者睡眠,而是让其循环等待,因此有如下区别应用 

    1)、信号量和读写信号量适合于保持时间较长的状况,它们会致使调用者睡眠,于是自旋锁适合于保持时间很是短的状况
    2)、自旋锁能够用于中断,不能用于进程上下文(会引发死锁)。而信号量不容许使用在中断中,而能够用于进程上下文
    3)、自旋锁保持期间是抢占失效的,自旋锁被持有时,内核不能被抢占,而信号量和读写信号量保持期间是能够被抢占的

另外须要注意的是
     1)、信号量锁保护的临界区可包含可能引发阻塞的代码,而自旋锁则绝对要避免用来保护包含这样代码的临界区,由于阻塞意味着要进行进程的切换,若是进程被切换出去后,另外一进程企图获取本自旋锁,死锁就会发生。
     2)、在你占用信号量的同时不能占用自旋锁,由于在你等待信号量时可能会睡眠,而在持有自旋锁时是不容许睡眠的。

信号量和互斥体之间的区别

概念上的区别:     

      信号量:是进程间(线程间)同步用的,一个进程(线程)完成了某一个动做就经过信号量告诉别的进程(线程),别的进程(线程)再进行某些动做。有二值和多值信号量之分。

     互斥锁:是线程间互斥用的,一个线程占用了某一个共享资源,那么别的线程就没法访问,直到这个线程离开,其余的线程才开始可使用这个共享资源。能够把互斥锁当作二值信号量。  

上锁时:

     信号量: 只要信号量的value大于0,其余线程就能够sem_wait成功,成功后信号量的value减一。若value值不大于0,则sem_wait阻塞,直到sem_post释放后value值加一。一句话,信号量的value>=0。

     互斥锁: 只要被锁住,其余任何线程都不能够访问被保护的资源。若是没有锁,得到资源成功,不然进行阻塞等待资源可用。一句话,线程互斥锁的vlaue能够为负数。  

使用场所:

     信号量主要适用于进程间通讯,固然,也可用于线程间通讯。而互斥锁只能用于线程间通讯。

 ----------------------------------------------------------------------------------------------------------------------------------

 

进程上下文:

在Linux中,用户程序装入系统造成一个进程的实质是系统为用户程序提供一个完整的运行环境。进程的运行环境是由它的程序代码和程序运行所须要的数据结构以及硬件环境组成的。进程的运行环境主要包括:

1.进程空间中的代码和数据、各类数据结构、进程堆栈和共享内存区等。

2.环境变量:提供进程运行所需的环境信息。

3.系统数据:进程空间中的对进程进行管理和控制所需的信息,包括进程任务结构体以及内核堆栈等。

4.进程访问设备或者文件时的权限。

5.各类硬件寄存器。

6.地址转换信息。

从以上组成状况能够看到,进程的运行环境是动态变化的,尤为是硬件寄存器的值以及进程控制信息是随着进程的运行而不断变化的。在Linux中把系统提供给进程的的处于动态变化的运行环境总和称为进程上下文。

系统中的每个进程都有本身的上下文。一个正在使用处理器运行的进程称为当前进程(current)。当前进程因时间片用完或者因等待某个事件而阻塞时,进程调度须要把处理器的使用权从当前进程交给另外一个进程,这个过程叫作进程切换。此时,被调用进程成为当前进程。在进程切换时系统要把当前进程的上下文保存在指定的内存区域(该进程的任务状态段TSS中),而后把下一个使用处理器运行的进程的上下文设置成当前进程的上下文。当一个进程通过调度再次使用CPU运行时,系统要恢复该进程保存的上下文。因此,进程的切换也就是上下文切换。

在系统内核为用户进程服务时,一般是进程经过系统调用执行内核代码,这时进程的执行状态由用户态转换为内核态。可是,此时内核的运行是为用户进程服务,也能够说内核在代替当前进程执行某种服务功能。在这种状况下,内核的运行还是进程运行的一部分,因此说这时内核是运行在进程上下文中。内核运行在进程上下文中时能够访问和修改进程的系统数据。此外,若内核运行在进程上下文中须要等待资源和设备时,系统能够阻塞当前进程

中断上下文:

硬件经过触发信号,致使内核调用中断处理程序,进入内核空间。这个过程当中,硬件的一些变量和参数也要传递给内核,内核经过这些参数进行中断处理。所谓的“中断上下文”,其实也能够看做就是硬件传递过来的这些参数和内核须要保存的一些其余环境(主要是当前被打断执行的进程环境)。中断时,内核不表明任何进程运行,它通常只访问系统空间,而不会访问进程空间,内核在中断上下文中执行时通常不会阻塞

--------------------------------------------------------------------------------------------------------------------------

可抢占式内核:即当进程位于内核空间时,有一个更高优先级的任务出现时,若是当前内核容许抢占,则能够将当前任务挂起,执行优先级更高的进程。

非抢占式内核:高优先级的进程不能停止正在内核中运行的低优先级的进程而抢占CPU运行。进程一旦处于核心态(例如用户进程执行系统调用),则除非进程自愿放弃CPU,不然该进程将一直运行下去,直至完成或退出内核

抢占式内核的意义:首先,这是将Linux应用于实时系统所必需的。实时系统对响应时间有严格的限定,当一个实时进程被实时设备的硬件中断唤醒后,它应在限定的时间内被调度执行。而Linux不能知足这一要求,由于Linux的内核是不可抢占的,不能肯定系统在内核中的停留时间。事实上当内核执行长的系统调用时,实时进程要等到内核中运行的进程退出内核才能被调度,由此产生的响应延迟,在现在的硬件条件下,会长达100ms级。这对于那些要求高实时响应的系统是不能接受的。而可抢占的内核不只对Linux的实时应用相当重要,并且能解决Linux对多媒体(video, audio)等要求低延迟的应用支持不够好的缺陷。

相关文章
相关标签/搜索