精华内容
下载资源
问答
  • 信号量临界

    2009-03-27 11:41:00
    来源:http://blog.csdn.net/mprc_jhq/archive/2007/03/19/1533809.aspx 学习目标:学习信号量及其属性进行同步实验研究临界区的行为使用POSIX命名信号量和无名信号量理解信号量的管理1. 临界临界区是指必须以...

    来源:http://blog.csdn.net/mprc_jhq/archive/2007/03/19/1533809.aspx 

    学习目标:
    学习信号量及其属性
    进行同步实验
    研究临界区的行为
    使用POSIX命名信号量和无名信号量
    理解信号量的管理
    1. 临界区
    临界区是指必须以互斥的方式执行的代码段,也就是说临界区范围内只能由一个活动的线程。例如:修改共享变量的过程中其他的执行线程可能会访问共享变量,那么修改共享变量的代码就被看成是临界区的一部分。
    临界区问题指用安全、公平和对称的方式来执行临界区代码的问题
     
    2. 信号量
    信号量是(S)一个整型变量,它带有两个原子操作信号量锁定wait和信号量解锁signal。
    可以将其看成一个整数值和一个等待signal操作的进程列表。
    wait操作:如果S大于零,wait就在一个原子操作中测试S,并对其进行减量运算;
                   如果S等于零,wait就在一个原子操作中测试S,并阻塞调用程序。
    signal操作:如果有线程在信号量上阻塞,S就等于零,signal就会解除对某个等待线程的阻塞;
                         如果没有线程在信号量上阻塞,signal就对S进行增量运算。
    信号量作用:
    a:保护临界区
     wait(&s)
     <critical section>
     signal(&s);
     <remainder section>
    b:线程同步
     process 1 executes:            process 2 executes:
     a;                           wait(&sync);
     signal(&sync);                 b;
     
    3. POSIX:SEM无名信号量
    信号量是一个sem_t类型的变量,有相关的原子操作来对它的值进行初始化、增量和减量操作。如果一个实现在unistd.h中定义了 _POSIX_SEMAPHORES,那么这个实现就支持POSIX:SEM信号量。无名信号量和命名信号量之间的区别类似于普通管道和命名管道之间的区 别
    信号量的申明:
    #include <semaphore.h>
    sem_t sem;
    信号量的初始化:必须在使用信号量之前对其进行初始化
    #include <semaphore.h>
    int sem_init(sem_t *sem, int pshared, unsigned value);
    没有规定成功时返回值,不成功返回-1并设置errno,必须检测的错误码:
    EINVAL value大于SEM_VALUE_MAX
    ENOSPC 初始化资源已经耗尽,或者信号量的数目超出了SEM_NSEMS_MAX的范围
    EPERM 调用程序不具有适当的特权
    参数pshared等于0,说明信号量只能由初始化这个信号量的进程中的线程使用;
    如果pshared非零,任何可以访问sem的进程都可以使用这个信号量。
    注:在创建信号量之后创建一个子进程,并没有提供对信号量的访问,子进程收到的是信号量的拷贝,而不是真的信号量。
    例:创建一个有进程中的线程使用的信号量
    sem_t semA;
    if (sem_init(&semA, 0, 1) == -1 )
    {
     perror (“failed to initialize semaphore semA”);
    }
    信号量的销毁:
    #include <semaphore.h>
    int sem_destroy(sem_t *sem);
    成功返回0,不成功返回-1并设置errno,检测错误码:
    EINVAL sem不是有效的信号量
    例:
    if (sem_destroy(&semA) == -1)
    {
     perror (“Failed to destroy semA”);
    }
    POSIX申明:销毁一个已经销毁的信号量的结果是未定义的。有其他线程阻塞在一个信号量上时,销毁这个信号量的结果也是未定义的。
     
    4. POSIX:SEM信号量的操作
    这里描述的信号量的操作适用与无名信号量,同时也适用命名信号量
    signal操作:
    #include <semaphore.h>
    int sem_post(sem_t *sem);
    成功返回0,不成功返回-1并设置errno,必须检测的错误码:
    EINVAL *sem不对应有效的信号量
    函数sem_init是信号安全的,可以在信号处理程序中调用它。
    wait操作:
    #include <semaphore.h>
    int sem_trywait(sem_t *sem);
    int sem_wait(sem_t *sem);
    成功返回0,不臣工返回-1并设置errno,必须检测的错误码
    EINVAL    *sem不对应有效的信号量
    EAGAIN    函数sem_trywait不会阻塞,而是设置errno后返回
    EINTR      被信号中断
    如果信号量为0,则调用进程一直阻塞直到一个相应的sem_post调用解除了对它的阻塞为止,或者直到它被信号中断为止(被信号中断后必须手动重启)。
    #include <errno.h>
    #include <semaphore.h>
     
    static int   shared     = 0;
    static sem_t sharedsem;
     
    int initshared(int val)
    {
        if (sem_init(&sharedsem, 0, 1) == -1)
        {
            return -1;
        }
        shared = val;
        return 0;
    }
     
    int getshared(int *val)
    {
        while (sem_wait(&sharedsem) == -1)   //必须考虑被信号中断,重启的情况
        {
            if (errno != EINTR)
                return -1;
        }
     
        *val = shared;
        return sem_post(&sharedsem);    //信号安全的,无须考虑
    }
     
    int incshared()
    {
        while (sem_wait(&sharedsem) == -1)
        {
            if (errno != EINTR)
                return -1;
        }
     
        shared++;
        return sem_post(&sharedsem);
    }
     
    注:如果既要在main程序中,又要在信号处理程序中对一个变量进行增量操作,如何用上面的程序保护着个变量?
    如果不做一些其他的操作,使不能用它来保护这个变量的。如果信号是在上面程序中的某个函数调用锁定了信号量的时候被捕捉到的,那么在信号处理程序中对这些函数中的某一个进行调用的时候,就会引起死锁
    正确的做法是在调用getshared和incshared之前将信号阻塞,调用完成后,解除信号阻塞。
     
    例:创建一个信号量,并将其传递给多个线程,线程函数调用信号量保护临界区
    #include <semaphore.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <errno.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
     
    #define TEN_MILLION   10000000L
    #define BUFSIZE       1024
     
    void *threadout(void *args)
    {
        char             buffer[BUFSIZE];
        char             *c;
        sem_t            *semlockp;
        struct timespec sleeptime;
     
        semlockp          = (sem_t *)args;
        sleeptime.tv_sec = 0;
        sleeptime.tv_nsec = TEN_MILLION;
        snprintf (buffer, BUFSIZE, "This is a thread from process %ld/n",
                        (long)getpid());
        c = buffer;
    //临界区入口
        while (sem_wait(semlockp) == -1)
        {
            if (errno != EINTR)
            {
                fprintf (stderr, "Thread failed to lock semaphore/n");
                return NULL;
            }
     
        }
     
    //临界区
        while (*c != '/0')
        {
            fputc (*c, stderr);
            c++;
            nanosleep(&sleeptime, NULL); //非忙等循环
        }
     
    //临界区出口
        if (sem_post(semlockp) == -1)
        {
            fprintf (stderr, "Thread failed to unlock semaphore/n");
        }
     
        return NULL;
    }
     
    int main(int argc, char *argv[])
    {
        int       error;
        int       i;
        int       n;
        sem_t     semlock;
        pthread_t *tids;
     
        if (argc != 2)
        {
            fprintf (stderr, "Usage: %s numthreads/n", argv[0]);
            return 1;
        }
     
        n    = atoi (argv[1]);
        tids = (pthread_t *)calloc(n, sizeof(pthread_t));
        if (tids == NULL)
        {
            perror ("Failed to initialize semaphore");
            return 1;
        }
     
        if (sem_init(&semlock, 0, 1) == -1)
        {
            perror ("Failed to initialize semaphore");
            return 1;
        }
     
        for (i=0; i<n; i++)
        {
            error = pthread_create(tids+i, NULL, threadout, (void *)&semlock);
            if (error != 0)
            {
                fprintf (stderr, "Failed to create thread:%s/n", strerror(error));
                return 1;
            }
        }
     
        for (i=0; i<n; i++)
        {
            error = pthread_join(tids[i], NULL);
            if (error != 0)
            {
                fprintf (stderr, "Failed to join thread:%s/n", strerror(error));
                return 1;
            }
        }
     
        return 0;
    }
     
    注:sem_init(&semlock, 0, 1) 将semlock初始化为1,如果0的话将产生死锁。
    stderr 标准输出是排他性资源,同时只能由一个线程使用。
    如果改称sem_init(&semlock, 0, 2),程序输出将会混乱。
     
    检测命名信号量和无名信号量的值:
    #include <semaphore.h>
    int sem_getvalue(sem_t *restrict sem, int *restrict sval);
    成功返回0,不成功返回-1并设置errno,必须检测错误码:
    EINVAL   *sem不对应一个有效的信号量
    函数可以用来检测一个命名信号量或者无名信号量的值。
     
    5. POSIX:SEM命名信号量
    命名信号量用来同步那些不共享内存的进程。
    命名信号量和文件一样,有一格名字、有一个用户ID、一个组ID和权限。
    如果两个进程(线程)打开的信号量一“/”开头,则其引用同一个信号量。
    因此,通常都要为POSIX:SEM命名信号量使用以“/”开头的名字。
    5.1创建并打开命名信号量
    #include <semaphore.h>
    sem_t *sem_open( const char *name, int oflag, ...);
    成功返回信号量的地址,不成功返回SEM_FAILED并设置errno,必须检测的错误码:
    EACCES             权限不够
    EEXIST              设置了O_CREATE和O_EXCL,而且信号量存在
    EINTR               函数别信号中断
    EINVAL              name不能作为信号量打开、或者试图用大于SEM_VALUE_MAX的值创建信号量
    EMFILE              进程使用了太多的文件描述符或信号量
    ENAMETOOLONG    name比PATH_MAX长、或者它有一个组件超出NAME_MAX范围
    ENFILE              系统中打开了太多的信号量
    ENOENT             没有设置O_CREATE,而且信号量也不存在
    ENOSPC             没有足够的空间了创建信号量
    函数sem_open功能说明:
    参数oflag用来确定是创建信号量,还是仅仅由函数对其进行访问。
    如果参数oflag设置了O_CREATE比特位就必须设置mode位(mode_t类型的权限位)和value位(unsigned类型的信号量初始值)。
    如果O_CREATE和O_EXCL位都设置了,那么信号量已经存在的话,函数返回一个错误。
    如果仅仅设置了O_CREATE位,那么信号量如果存在,信号量会忽略O_CREATE和其他额外的参数
    在信号量已经存在的情况下,POSIX没有提供直接设置命名信号量值得方法
    例:访问一个命名信号量,如果不存在就创建它
    #include <errno.h>
    #include <semaphore.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
     
    #define PERMS (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
    #define FLAGS (O_CREAT | O_EXCL)
     
    #define BUFSIZE 1024
     
    int getnamed(char *name, sem_t **sem, int val)
    {
        while ( ((*sem = sem_open(name, FLAGS, PERMS, val)) == SEM_FAILED) &&
            (errno == EINTR));    //创建命名信号量,遇信号中断重启
     
        if (*sem != SEM_FAILED)   //创建成功返回
            return 0;
        if (errno != EEXIST)     //失败返回,已经存在执行读取
            return -1;
                                  //信号量已经存在,读取它
        while ( ((*sem = sem_open(name, 0)) == SEM_FAILED) && (errno == EINTR));
        if (*sem != SEM_FAILED)
            return 0;
     
        return -1;
    }
     
    int main(int argc, char *argv[])
    {
        char         buffer[BUFSIZE];
        char         *c;
        pid_t        childpid         = 0;
        int          delay;
        volatile int dummy            = 0;
        int          i;
        int          n;
        sem_t        *sem_lockp;
     
        if (argc != 4)
        {
            fprintf (stderr, "Usage: %s processes delay semaphorename/n", argv[0]);
            return 1;
        }
     
        n     = atoi(argv[1]);
        delay = atoi(argv[2]);
        for (i=1; i<n; i++)
        {
            if (childpid = fork())
                break;               //重要,不能少,子进程推出循环,父进程继续执行循环
           
        }
     
        snprintf (buffer, BUFSIZE, "i:%d process ID:%ld parent ID:%ld child ID:%ld/n",
                                       i, (long)getpid(), (long)getppid(), (long)childpid);
        c = buffer;
        if (getnamed(argv[3], &sem_lockp, 1) == -1)
        {
            perror ("Failed to create named semaphore");
            return 1;
        }
        while (sem_wait(sem_lockp) == -1)             //进入临界区
        {
            if (errno != EINTR)
            {
                perror("Failed to lock semlock");
                return 1;
            }
        }
     
        while (*c != '/0')                   //临界区
        {
            fputc (*c, stderr);
            c++;
            for (i=0; i<delay; i++)
                dummy++;
        }
       
        if (sem_post(sem_lockp) == -1)         //退出临界区
        {
            perror("Failed to unlock semlock");
            return 1;
        }
     
        if (wait(NULL) == -1)                 //等待子进程结束返回
            return 1;
     
        return 0;
    }
    注1 :命名信号量就像文件一样存在系统中的。如果同时运行两个以上程序在一台机器上,则还能够正常运新
    注2 :如果上面的程序正在运行,输入Ctrl-C退出,然后再次运行它,又可能进程都会阻塞,因为Ctrl-C产生的信号有可能在信号量的值为0时被传递。下次运行程序时,信号量的初始值是0,所以所有的进程阻塞。
    命名信号量使多个进程可以实现同步和互斥,无名信号量使同一个进程的多个线程实现同步和互斥。
    5.2关闭并删除命名信号量
    与命名管道一样,命名信号量在单个程序的执行之外是具有持久性的。
    关闭信号量:
    #include <semaphore.h>
    int sem_close(sem_t *sem);
    成功返回0,不成功返回-1并设置errno,检测错误码:
    EINVAL    *sem不是一个有效的信号量
    删除命名信号量:
    #include <semaphore.h>
    int sem_unlink(const char *name);
    成功返回0,不成功返回-1并设置errno,检测错误码:
    EACCES              权限不正确
    ENAMETOOLONG     name比PATH_MAX长、或者它有一个组件超出NAME_MAX范围
    ENOENT              信号量不存在
    说明1:函数在素有的进程关闭了命名信号量之后将命名信号量从系统中删除。当进程显示地调用SEM_CLOSE、_exit、exit、exec或执行从main的返回式,就会出现关闭操作。
    说民2:sem_unlink之后,即使其他的进程仍然将老的信号量打开着,用相同的名字调用的sem_open引用的也是新的信号量。即使其他的进程将信号量打开着,sem_unlink函数也总是会立即返回。
    例:关闭并删除命名信号量的函数
    #include <semaphore.h>
    #include <errno.h>
     
    int destroynamed(char *name, sem_t *sem)
    {
        int error = 0;
     
        if (sem_close(sem) == -1)
            error = errno;
       
        if ( (sem_unlink(name) != -1) && !error)
            return 0;
       
        if (error != 0)
            errno = error;
       
        return -1;
    }
    注:命名信号量具有持久性的。如果创建了这样的一个信号量,即使创建它的进程和所有可以访问它的进程都终止了,它还是一直存在于系统中,保持它的值直到被销毁为止。
    POSIX没有提供方法来确定那些命名信号量是存在的。当显示目录内容是,他们又可能会出现,也有可能不出现。当系统重启时,他们有可能被销毁,也可能不被销毁。
    展开全文
  • 临界资源或临界区是指在同一时刻只允许一个进程或线程访问,并且只有当占有该资源的进程释放了该资源后,...信号量即是一种进程间通信的方法,它使用一个整形变量来累计唤醒次数,供以后使用,当信号量只有两种状态时,

             临界资源或临界区是指在同一时刻只允许一个进程或线程访问,并且只有当占有该资源的进程释放了该资源后,才能被其他进程使用。因此需要设计一种机制保障进程间的通信,使得不同的进程能够知道临界资源的使用情况,当某个进程占据了临界资源时,应该告知其他进程该资源已经被占用,避免其他进程错误的访问和使用临界资源。信号量即是一种进程间通信的方法,它使用一个整形变量来累计唤醒次数,供以后使用,当信号量只有两种状态时,表示该资源只能被唤醒一次或者占用一次,此时对临界资源的访问就是互斥的。

           在这里我设计了一个多线程编程的小程序,通过动画的形式展示出来上述调度过程。设计了一个双向单车道的道路上中间有一个窄桥,一次只允许一辆车通过。因此不同方向的来车在通过窄桥时候应该首先判断桥是否为空,只有为空的情况下才能通过,并且当占有桥时候,还要告诉其他车辆该桥已经有车通过。因此在程序中设置了一个表示临界资源——桥的信号量bridgeMutex,初始化为0,当有车占有桥时候,即将信号量设置为1,其他要上桥的车辆即被阻塞,当车辆通过桥后,就唤醒信号量bridgeMutex,重新设置为0.此时其他车辆就可以访问桥了。在这个程序中每一个车辆是一个线程。测试的时候生成了两个车辆,也就是两个线程,当两个车辆行驶到桥的时候,会发现其中一个车辆会等另外一个车辆先通过后,自己再通过。先通过的车辆即抢先占据临界资源的车辆。

            程序是采用C语言编程的,建立的是.cpp文件,需要配置多线程编程的环境,采用的是POSIX线程标准的Pthread. 另外为了通过动画演示,配置了一个easyx图形库的环境。

    C程序源代码如下:

    #include <pthread.h>
    #include <iostream>
    #include "easyx.h"
    #include <graphics.h> 
    #include "time.h"
    #pragma comment(lib, "pthreadVC2.lib")
    int bridgeMutex=0;
    int rightEnd=617,leftEnd=1;
    int moveStep=22,goRight=295,goLeft=320,onBridge=307;
    int leftRodeUp[12],leftRodeDown[12],rightRodeUp[12],rightRodeDown[12],bridge[5];


    void* carGoRight(void* args);
    void* carGoLeft(void* args);


    int drawGoRight(int i,int j);
    int drawGoLeft(int i,int j);
    int drawRode();


    int main(){
    pthread_t goRight[10];
    pthread_t goLeft[10];
    int i=0;
    initgraph(640,640);
    for (i=0;i<12;i++)
    {
    leftRodeUp[i]=0;
    leftRodeDown[i]=0;
    rightRodeUp[i]=0;
    rightRodeDown[i]=0;
    if(i<5)
    {
    bridge[i]=0;
    }
    }
    drawRode();
    for(i=0;i<1;i++)
    {
    pthread_create(&goRight[i],NULL,carGoRight,NULL);
    pthread_create(&goLeft[i],NULL,carGoLeft,NULL);
    pthread_join(goRight[i],NULL);
    pthread_join(goLeft[i],NULL);
    }
    Sleep(2000);
    return 0;
    }




    void* carGoRight(void* args)
    {
           int i=1,j=295,k=0;
           while(i<=rightEnd)
      {
      k=(i-1)/moveStep;
      if (k<12)
      {
      while (leftRodeUp[k]==1)
      {
      Sleep(100);
      }
      leftRodeUp[k]=1;
      if (k>0)
      {
      leftRodeUp[k-1]=0;
      clearrectangle(i-22,j+4,i,j+5+19); //清除原来位置的车
      }
      drawGoRight(i+1,j+5);


      i += 22;


      }
      else if (k==12) //等待上桥
      {
      while (bridgeMutex==1)
      {
      Sleep(100);
      }
      bridgeMutex=1; //占有临界区——窄桥
      bridge[k-12]=1;
      leftRodeUp[k-1]=0;
      clearrectangle(i-22,j+4,i,j+5+19);


      drawGoRight(i+1,j+5+12);
      i += 22;
      }
      else if(k<17)
      {
      bridge[k-12]=1;
      bridge[k-12-1]=0;
      clearrectangle(i-22,j+4+12,i,j+5+19+12);
      drawGoRight(i+1,j+5+12);
      i += 22;
      }
      else if (k==17) //进入右边道路,释放桥
      {
      while (rightRodeUp[k-17]==1)
      {
      Sleep(100);
      }
      rightRodeUp[k-17]=1;
      bridge[k-12-1]=0;
      bridgeMutex=0;
      clearrectangle(i-22,j+4+12,i,j+5+19+12);
      drawGoRight(i+1,j+5);
      i += 22;
      }
      else if (k<29)//在右边道路上
      {
      while (rightRodeUp[k-17]==1)
      {
      Sleep(100);
      }
      rightRodeUp[k-17]=1;
      rightRodeUp[k-17-1]=0;
      clearrectangle(i-22,j+4,i,j+5+19);
      drawGoRight(i+1,j+5);
      i += 22;
      }
      Sleep(300);
           }
      rightRodeUp[11]=0;
           clearrectangle(i-22,j+4,i,j+5+19);
           return NULL;
    }


    void* carGoLeft(void* args)
    {
    int i=rightEnd,j=320,k=0;
    while(i>=leftEnd)
    {
    k=(rightEnd-i)/moveStep;
    if (k<12)   //在左边道路上
    {
    while (rightRodeDown[11-k]==1)
    {
    Sleep(100);
    }
    rightRodeDown[11-k]=1;
    if (k>0)
    {
    rightRodeDown[11-k+1]=0;
    clearrectangle(i+22,j+4,i+44,j+5+19); //清除原来位置的车
    }
    drawGoLeft(i+1,j+5);

    i -= 22;

    }
    else if (k==12) //等待上桥
    {
    while (bridgeMutex==1)
    {
    Sleep(100);
    }
    bridgeMutex=1; //占有临界区——窄桥
    bridge[16-k]=1;
    rightRodeDown[11-k+1]=0;
    clearrectangle(i+22,j+4,i+44,j+5+19);

    drawGoLeft(i+1,j+5+12-25);
    i -= 22;
    }
    else if(k<17)
    {
    bridge[16-k]=1;
    bridge[16-k+1]=0;
    clearrectangle(i+22,j+4+12-25,i+44,j+5+19+12-25);
    drawGoLeft(i+1,j+5+12-25);
    i -= 22;
    }
    else if (k==17) //进入左边道路,释放桥
    {
    while (leftRodeDown[28-k]==1)
    {
    Sleep(100);
    }
    leftRodeDown[28-k]=1;
    bridge[17-k]=0;
    bridgeMutex=0;
    clearrectangle(i+22,j+4+12-25,i+44,j+5+19+12-25);
    drawGoLeft(i+1,j+5);
    i -= 22;
    }
    else if (k<29)//在左边道路上
    {
    while (leftRodeDown[28-k]==1)
    {
    Sleep(100);
    }
    leftRodeDown[28-k]=1;
    leftRodeDown[28-k+1]=0;
    clearrectangle(i+22,j+4,i+44,j+5+19);
    drawGoLeft(i+1,j+5);
    i -= 22;
    }
    Sleep(300);
    }
    leftRodeDown[0]=0;
    clearrectangle(1,j+4,1+22,j+5+19);
    return NULL;
    }


    int drawGoRight(int i,int j)
    {
    rectangle(i,j,i+20,j+15);
    solidcircle(i+5,j+15,3);
    solidcircle(i+15,j+15,3);
    line(i+7,j+5,i+13,j+5);
    line(i+13,j+5,i+11,j+3);
    line(i+13,j+5,i+11,j+8);
    return 1;
    }
    int drawGoLeft(int i,int j)
    {
    rectangle(i,j,i+20,j+15);
    solidcircle(i+5,j+15,3);
    solidcircle(i+15,j+15,3);


    line(i+7,j+5,i+13,j+5);
    line(i+7,j+5,i+9,j+3);
    line(i+7,j+5,i+9,j+8);
    return 1;
    }
    int drawRode()
    {
    //clearrectangle(i-1,j-1,i+21,j+19);
    line(0,295,265,295);//left rode
    line(0,345,265,345);
    line(0,320,265,320);


    line(265,307,265,295);
    line(265,333,265,345);


    line(265,307,375,307);//bridge
    line(265,333,375,333);


    line(375,295,640,295);//right rode
    line(375,345,640,345);
    line(375,320,640,320);


    line(375,295,375,307);
    line(375,345,375,333);


    return 1;
    }

    程序运行结果;






    展开全文
  • 信号量实现临界信号量是操作系统提供的一种协调共享资源访问的方法 信号量的抽象数据类型 一个整形(sem),具有两个原子操作 P(): sem减一,如果sem<0,等待,否则继续 V(): sem加一,如果sem≤0,唤醒一个等待的P...

    用信号量实现临界区

    信号量是操作系统提供的一种协调共享资源访问的方法

    信号量的抽象数据类型

    • 一个整形(sem),具有两个原子操作
    • P(): sem减一,如果sem<0,等待,否则继续
    • V(): sem加一,如果sem≤0,唤醒一个等待的P

    信号量是整数

    信号量是被保护的变量

    • 初始化完成后,唯一改变一个信号量的值的办法是通过P()和V()
    • 操作必须是原子(p()、v()优先级高于进程的用户代码,不受应用进程代码的干扰)

    P()能够阻塞,V()不会阻塞

    我们假定信号量是公平的

    • 线程不会被无限期阻塞在p()操作,有一个等待时长的参数,超时就错误返回
    • 在实践中,FIFO经常被使用

    信号量的实现

    classSemaphore{
      int sem;
      WaitQueue q;
    }
    
    p(){
      sem--;
      if(sem < 0){ // 说明资源不够,进入等待队列
        add this thread t to queue;
        block(q);
      }
      // 进入临界区
    }
    
    V(){
      sem++;
      if(sem <= 0){  // 说明有线程在等待
         remove a thread t from queue;
         wakeup(t);  // t线程从等待队列被唤醒,就绪状态,可以进入临界区了
      }
    }
    
    

    操作系统实现对p()和v()的原子性。

    两个类型信号量

    • 二进制信号量: 可以是0或1
    • 计数信号量: 可以取任何非负数
    • 两者相互表现(给定一个可以实现另一个)

    信号量可以用在2个方面

    • 互斥
    • 条件同步(事件出现之后,等待的就可以执行了)

    用二进制信号量实现互斥

    mutex = new Semaphore(1);
    
    mutex->P();
    // 进入临界区
    mutex->V();
    

    用信号量实现条件同步

    image-20210912145945052

    条件同步设置一个信号量,其初值为0。

    condition = new Semaphore(0);
    

    只有在线程2执行完 X 操作后,线程1才可以执行 N 操作。

    如果是线程2 先执行完X操作,信号量变成1。

    线程1执行完M代码段之后,执行 p 操作把 Semaphore - 1 = 0,Semaphore < 0 不成立 ,所以此时 N代码可以直接运行,符合要求。

    如果是线程1 先执行完M操作,执行 p()操作,把 Semaphore - 1 = -1,Semaphore < 0 ,线程1被阻塞,无法执行 N 代码。

    这时如果线程2执行完 X 代码,执行完 v 操作, 把 Semaphore + 1 = 0,Semaphore <= 0 时,唤醒线程。此时线程1被唤醒,可以执行 N 操作了。

    展开全文
  • 整形信号量

    千次阅读 2020-03-06 22:36:57
    信号量可以理解为某一类可用资源的数目,信号量用来解决进程同步和互斥问题。整型信号量是整型量,仅能通过两个原子操作来访问。原子操作是不可中断的,原子操作就是原语的执行,这段代码不可中断,要执行都执行完,...

    信号量可以理解为某一类可用资源的数目,信号量用来解决进程同步和互斥问题。整型信号量是整型量,仅能通过两个原子操作来访问。原子操作是不可中断的,原子操作就是原语的执行,这段代码不可中断,要执行都执行完,若执行不完则回到这段代码起始位置重新执行。两个原子操作分别是P操作和V操作:

    // P操作 伪代码
    wait(S):
        While(S<=0) do no-op;
        S--;
    
    
    // V操作 伪代码
    signal(S):
        S++;
    
    // 这里的S就是整型信号量,wait(S)可以写作P(S),signal(S)可以写作V(S)。

    我们看如何使用PV操作实现互斥:

    // 用信号量机制实现互斥的模式
    semaphore  mutex = 1;
            begin
            parbegin
                process 1: begin
                               repeat
                                   wait(mutex);
                                   critical section
                                   signal(mutex);
                                   remainder section
                               until false;
                           end
                process 2: begin
                               repeat
                                   wait(mutex);
                                   critical section
                                   signal(mutex);
                                   remainder section
                                until false; 
                           end
            parend  
            end     
    
    

    mutex代表可用资源的数目,假设mutex=1,两个进程竞争使用mutex,那么防止不同步问题出现,每个进程中都要成对使用wait和signal。比如P1执行wait,mutex--代表使用资源,然后进行其他计算,这时P2执行wait无法通过判断条件,mutex=0表示当前没有可用资源,于是程序计数器调整指针回到wait起始代码。这样就很好的保证了P2不会参与P1的运行。P1执行完critical section临界区,执行signal,mutex++表示资源回收,这时P2才可以进行wait的执行。

    整形信号量也是存在缺点的,比如wait中如果条件不满足就要一直循环等待,这违背了同步机制的让权等待规则;并且整型信号量是在一种资源且只有一个的情形下使用的,实际情况没有这么简单。由此改进而来的记录型信号量出现了。

     

    传送门:记录型信号量AND型信号量信号量集

     

    展开全文
  • 程序的原子性指:整个程序中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个... 信号量是操作系统提供的一种协调共享资源访问的方法。和用软件实现的同步比较,软件同步是平等线程间的的一种同步...
  • 一.什么是信号量信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来...PV 原语是对信号量的操作:一次 P 操作使信号量减1,一次 V 操作使信号量加1。 二.信号量的分...
  • 信号量机制

    2019-09-16 20:10:34
    1.整形信号量 一个整数值,表示空闲资源总数,除初始化外,仅能通过两个标准的原子操作来访问,这两个操作为wait(s)和signal(s)。 其操作可被描述为: P(s): Wait(s): while ( s<=0 ) ; s - - ; V(s): Singal(s...
  • 信号量是一种数据结构(可以是整型数、整型数组、链表、记录型变量(结构体))。信号量的值与相应资源的使用情况有关。信号量的值仅由P、V操作改变。 整型信号量S(数据结构是一个整型数,用整型数代表信号可用不...
  • 10、信号量机制思维导图信号量机制的引入什么是信号量机制整形信号量记录型信号量 思维导图 信号量机制的引入 来看几个问题。 1、在用软件实现进程互斥的时候,双标志先检查算法由于 “上锁”“检查”操作无法...
  • 信号量的原理总结

    万次阅读 多人点赞 2016-09-22 22:40:55
    谈到信号量,大多数会立马想到PV操作,这个用荷兰语表示的专有名词。对比前面我们说的,四种实现临界区互斥的软件设计算法,PV操作更加的优良,现在这里就将对其原理进行详细的解释分析,以及如何在题目的场景中使用...
  • 信号量semaphore

    2018-12-03 09:57:25
    信号量semaphore 信号是事件发生时对进程的通知机制。有时可称之为软件中断。信号与硬件中断的相似之处在于打断了程序执行的正常流程。针对每个信号,都定义了一个唯一的(小)整数,从1开始顺序展开。信号分为两大...
  • 信号量与管程

    千次阅读 2015-06-04 21:19:03
    基本同步方法 信号量semaphore:操作系统提供的一种协调共享资源访问的方法:OS是管理者,地位高于进程;信号量表示系统资源的数目。是一种抽象的数据类型:一个整形变量sem和两个...信号量分类:二进制信号量、资源信
  • 文章目录1. 基本结构2. P,V操作3. 信号量的应用3.1 信号量实现进程互斥3.2 信号量实现前驱关系4....信号量整形信号量、记录型信号量、AND型信号量等,这里主要介绍我们常见的记录型信号量。 1...
  • 信号量

    2015-12-05 14:53:39
    这个信号量理解起来是有点不容易啊,我看书看了好几遍才知道怎么回事。在讲这一节信号量之前,我还是想先说几个小知识点,这也是我在学习完后最终理解的“精华”,哈哈! 信号量是干啥的? 信号量就是用来解决...
  • Linux:POSIX信号量

    2019-01-20 21:58:41
    POSIX信号量 POSIX信号量和SystemV信号量作⽤相同,都是⽤于同步操作,达到⽆冲突的访问共享资源目的。 但POSIX可以⽤于线程间同步。 信号量本质上是一个...因此信号量不能用整形计数器来代替,因为整形的++和- ...
  • 1965年,荷兰学者Djikstra提出信号量(Semaphores)机制是一种卓有成效的进程同步工具 ...在整形信号量机制中的wait操作,只要是信号量S≤0,就会不断测试。该机制并未遵守“让权等待”的准则,而是使进程...
  • 信号量机制是由dijkstra 1965年提出,是解决进程同步重要的工具下面方法适用与几个进程访问同一个临界整形信号量定义一个表示资源数目的整形信号量S,仅能同步P, V操作改变。原始值S=1,每一个进程来的时候会执行...
  • 信号量原理

    2021-01-23 21:01:12
    信号量是一个确定的二元组(s,q),其中s是一个具有非负初值的整形变量,q是一个初始状态为空的队列。 整型变量s表示系统中某类资源的数目, 1-当其值大于0时,表示系统中当前可用资源的数目; 2-当其值小于0时,...
  • 信号量(Semaphore)是Dijkstra在1965年提出的进程同步机制,表示临界资源的实体。用于解决进程的同步和互斥问题。 信号量的分类: 整形信号量、记录型信号量、AND型信号量信号量集 一、整形信号量 信号量定义...
  • 整形信号量信号量定义为一个整型量; 根据初始情况赋相应的值; 仅能通过两个原子操作来访问 - P 操作:wait(S): While S&amp;amp;lt;=0 do no-op; S:=S-1; - V 操作:signal(S): S:=S+1; 整型...
  • iOS 信号量

    2017-09-15 11:33:00
    一般情况是这样进行临界访问或互斥访问的: 设信号量值为1, 当一个进程1运行是,使用资源,进行P操作,即对信号量值减1,也就是资源数少了1个。这是信号量值为0。系统中规定当信号量值为0是,必须等待,知道信号...
  • 信号量 & 管程

    2020-12-08 07:42:29
    目录前提知识信号量管程 参考连接 锁原理 - 信号量 vs 管程:JDK 为什么选择管程 前提知识 临界资源 虽然多个进程可以共享系统中的各种资源,但其中许多 资源一次只能为一个进程所使用,我们把一次仅允许一个进程...
  • 一、整形信号量 1.S:一个用于表示资源数目的整型量; 2.与一般整型量不同,除初始化外,仅能通过两个标准的原子操作:wait(S)和signal(S);长期以来一直被称为P、V操作; 3.wait操作伪码描述: wait(S){ while(S&...
  • 2.12信号量机制

    千次阅读 2020-04-02 13:26:28
    一,为什么要引入信号量机制 1,为了解决在双标志先检查法,进入区检查和上锁操作无法一气呵成,从而导致两个进程有可能进入临界区的问题 2,上一小节所有方案都无法实现让权等待 3,1965年,荷兰学者Dijkstra提出...
  • OS-信号量管理

    2020-12-04 19:58:04
    信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了...整形信号量
  • 信号量、管程比锁更为完善的同步互斥的问题。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,848
精华内容 739
关键字:

整形信号量的临界条件