lv5 嵌入式开发-12 信号灯

目录

1 信号量/灯(semaphore)基本概念

2 信号量-P/V操作概念

3 三种信号灯

3.1 有名信号灯

3.1.1 打开

3.1.2 关闭

3.1.3 删除

3.2 无名信号灯

3.2.1 初始化

3.2.2 销毁

3.3 信号灯P操作

3.4 信号灯V操作

3.5 示例

3.5.1 有名信号示例

3.5.2 无名信号灯示例(逻辑同上)

3.5 System V 信号灯使用:

3.5.1 创建/打开信号灯

3.5.2 进行P - V操作

3.5.3 初始化/删除

3.5.4 System V IPC - 信号灯特点

3.5.5  System V信号灯使用步骤

3.6 System V信号灯示例


掌握:信号灯机制、信号灯初始化、打开/创建信号灯、信号灯-P操作、信号灯-V操作、信号灯、共享内存

1 信号量/灯(semaphore)基本概念

信号量代表某一类资源,其值表示系统中该资源的数量。

概念:是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的生产者和消费者场景。

类似之前讲过的条件变量。

信号量是一个受保护的变量,只能通过三种操作来访问

  •         初始化
  •         P操作(申请资源)
  •        V操作(释放资源)

2 信号量-P/V操作概念

P(S) 含义如下:
    if  (信号量的值大于0) {   
          申请资源的任务继续运行;
          信号量的值减一;
    }
    else {   申请资源的任务阻塞;} 
           

V(S) 含义如下:
     信号量的值加一;
     if (有任务在等待资源) {
          唤醒等待的任务,让其继续运行
     }

3 三种信号灯

信号灯也叫信号量,用于进程/线程同步或互斥的机制

信号灯的类型  

  • Posix 无名信号灯  
  • Posix有名信号灯    (linux只支持线程同步)
  • System V  信号灯

Posix 有名信号灯和无名信号灯使用:

lv5 嵌入式开发-12 信号灯_第1张图片

wait 相当于p操作,post相当于v操作 

3.1 有名信号灯

3.1.1 打开

sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);

参数:

name:name是给信号灯起的名字

oflag:打开方式,常用O_CREAT

mode:文件权限。常用0666

value:信号量值。二元信号灯值为1,普通表示资源数目

信号灯文件位置:/dev/shm

3.1.2 关闭

int sem_close(sem_t *sem);

3.1.3 删除

int sem_unlink(const char* name);

3.2 无名信号灯

3.2.1 初始化

int sem_init(sem_t *sem, int shared, unsigned int value);

 参数:

sem:需要初始化的信号灯变量

shared: shared指定为0,表示信号量只能由初始化这个信号量的进程使用,不能在进程间使用,linux 不支持进程间同步。0 – 线程间   1 – 进程间  

Value:信号量的值

3.2.2 销毁

int sem_destroy(sem_t* sem);

成功时返回0,失败时返回EOF  

sem  指向要操作的信号量对象

3.3 信号灯P操作

int sem_wait(sem_t *sem);

成功时返回0,失败时返回EOF  

sem  指向要操作的信号量对象

获取资源,如果信号量为0,表示这时没有相应资源空闲,那么调用线程就将挂起,直到有空闲资源可以获取

3.4 信号灯V操作

int sem_post(sem_t *sem);

释放资源,如果没有线程阻塞在该sem上,表示没有线程等待该资源,这时该函数就对信号量的值进行增1操作,表示同类资源多增加了一个。如果至少有一个线程阻塞在该sem上,表示有线程等待资源,信号量为0,这时该函数保持信号量为0不变,并使某个阻塞在该sem上的线程从sem_wait函数中返回

注意:编译posix信号灯需要加pthread动态库。

3.5 示例

3.5.1 有名信号示例

避免读写混乱:规定写资源初值为1,读资源初值为0。获取写入权限,后写-1,读+1,获取读权限后,读-1,写+1。

sem_r.c

#include            /* For O_* constants */
#include         /* For mode constants */
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//删除信号,以免程序退出(ctrl+c) 重新打开无法开启
void delsemfile(int sig){
    sem_unlink("mysem_r");
    exit(0);
}

int main(){

   sem_t *sem_r,*sem_w;
   key_t key;
   int shmid;
   char *shmaddr;

   struct sigaction act;
   act.sa_handler = delsemfile;
   act.sa_flags = 0;

   sigemptyset(&act.sa_mask);

   sigaction(SIGINT,&act,NULL); //启用linux中信号退出回调函数delsemfile,用于删除信号量

   key = ftok(".",100);
   if(key<0){
       perror("ftok");   
       return 0;
   }

   shmid = shmget(key,500,0666|IPC_CREAT);  //创建信号灯
   if(shmid<0){
       perror("shmget");
       return 0;
   }

   shmaddr = shmat(shmid,NULL,0);           //创建共享内存
   
   sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);   //创建初值为0的读信号灯
   sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);   //创建初值为1的写信号灯

   while(1){
        sem_wait(sem_r);             //P操作,获取读资源,读资源-1
        printf("%s\n",shmaddr);
        sem_post(sem_w);             //V操作,写资源+1
   }
}

sem_w.c

#include            /* For O_* constants */
#include         /* For mode constants */
#include 

#include 
#include 
#include 
#include 
#include 
#include 

void delsemfile(int sig){
    sem_unlink("mysem_w");
    exit(0);
}

int main(){


   sem_t *sem_r,*sem_w;
   key_t key;
   int shmid;
   char *shmaddr;

   struct sigaction act;
   act.sa_handler = delsemfile;
   act.sa_flags = 0;
   sigemptyset(&act.sa_mask);

   sigaction(SIGINT,&act,NULL);

   key = ftok(".",100);
   if(key<0){
       perror("ftok");
       return 0;
   }

   shmid = shmget(key,500,0666|IPC_CREAT);  //创建信号灯,500个字节
   if(shmid<0){
       perror("shmget");
       return 0;
   }

   shmaddr = shmat(shmid,NULL,0);
   
   sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
   sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);

   while(1){
        sem_wait(sem_w);
        printf(">");
        fgets(shmaddr,500,stdin);
        sem_post(sem_r);
   }

}

3.5.2 无名信号灯示例(逻辑同上)

#include            /* For O_* constants */
#include         /* For mode constants */
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 


sem_t sem_r,sem_w;
char *shmaddr;

void destroysem(int sig){
//    sem_unlink("mysem_w");
   sem_destroy(&sem_r);
   sem_destroy(&sem_w);
   exit(0);

}

void *readmem(void *arg){
    while(1){
        sem_wait(&sem_r);         //P操作
        printf("%s\n",shmaddr);
        sem_post(&sem_w);         //V操作

    }

}


int main(){

   key_t key;
   int shmid;
   struct sigaction act;

   act.sa_handler = destroysem;
   act.sa_flags = 0;
   sigemptyset(&act.sa_mask);

   sigaction(SIGINT,&act,NULL);

   key = ftok(".",100);
   if(key<0){
       perror("ftok");
       return 0;
   }

   shmid = shmget(key,500,0666|IPC_CREAT);
   if(shmid<0){
       perror("shmget");
       return 0;
   }

   shmaddr = shmat(shmid,NULL,0);
   
//   sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
//   sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
   sem_init(&sem_r,0,0);     //有名信号灯创建,读初值0
   sem_init(&sem_w,0,1);     //有名信号灯创建,写初值1

   pthread_t tid;
   pthread_create(&tid,NULL,readmem,NULL);   //创建线程
    
   while(1){
        sem_wait(&sem_w);    //P操作
        printf(">");
        fgets(shmaddr,500,stdin);
        sem_post(&sem_r);    //V操作
   }

}

3.5 System V 信号灯使用:

3.5.1 创建/打开信号灯

int semget(key_t key, int nsems, int semflg);

参数:key:ftok产生的key值(和信号灯关联的key值)

          nsems:信号灯集中包含的信号灯数目

          semflg:信号灯集的访问权限,通常为IPC_CREAT |0666   IPC_EXCL

返回值:成功:信号灯集ID ; 失败:-1

3.5.2 进行P - V操作

int semop ( int semid, struct sembuf *opsptr, size_t nops);

功能:对信号灯集合中的信号量进行P - V操作

参数:semid:信号灯集ID        

          opsptr:   操作结构体

struct sembuf {
    short sem_num; // 要操作的信号灯的编号
    short sem_op;  // 1 : 释放资源,V操作
                   // -1 : 分配资源,P操作  
    short sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO
};//对某一个信号灯的操作,如果同时对多个操作,则需要定义这种结构体数组

           nops: 要操作的信号灯的个数 ,1个

返回值:成功 :0 ; 失败:-1

3.5.3 初始化/删除

int semctl ( int semid, int semnum, int cmd…/*union semun arg*/);

功能:信号灯集合的控制(初始化/删除)

参数:semid:信号灯集ID

           semnum: 要操作的集合中的信号灯编号

           cmd:

           GETVAL:获取信号灯的值,返回值是获得值

           SETVAL:设置信号灯的值,需要用到第四个参数:共用体

           IPC_RMID:从系统中删除信号灯集合

返回值:成功 0 ; 失败 -1

3.5.4 System V IPC - 信号灯特点

System V 信号灯是一个或多个计数信号灯的集合

可同时操作集合中的多个信号灯

申请多个资源时避免死锁

3.5.5  System V信号灯使用步骤

  • 打开/创建信号灯  
  • semget 信号灯初始化  
  • semctl P/V操作   semop
  • 删除信号灯  semctl

3.6 System V信号灯示例

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 



#define SEM_READ   0
#define SEM_WRITE  1

union semun {
   int val;
};               

void Poperation(int semid,int semindex){
   struct sembuf sbuf;
   sbuf.sem_num =  semindex;
   sbuf.sem_op = -1;
   sbuf.sem_flg = 0;

   semop(semid,&sbuf,1);


}
void Voperation(int semid,int semindex){
   struct sembuf sbuf;
   sbuf.sem_num =  semindex;
   sbuf.sem_op = 1;
   sbuf.sem_flg = 0;
              
   semop(semid,&sbuf,1);


}




int main(){

    key_t key;
    char *shmaddr;
    int semid,shmid;
    key = ftok(".",100);
    if(key<0){
        perror("ftok");
        return 0;
    }
    
    semid = semget(key,2,IPC_CREAT |0666);
    if(semid<0){
        perror("semget");
        return 0;
    }
    shmid = shmget(key,500,IPC_CREAT |0666);
    shmaddr = shmat(shmid,NULL,0);
    union semun mysem;
    mysem.val = 0;
    semctl(semid,SEM_READ,SETVAL,mysem);
    mysem.val = 1;
    semctl(semid,SEM_WRITE,SETVAL,mysem);

    pid_t pid;
    pid = fork();
    if(pid<0){
        perror("fork");
        shmctl(shmid,IPC_RMID,NULL);
        semctl(semid,0,IPC_RMID);
        exit(-1);
    }else if(pid == 0){
        while(1){
            Poperation(semid,SEM_READ);
            printf("%s\n",shmaddr);
            Voperation(semid,SEM_WRITE);

    }
 

    }else{
        while(1){
            Poperation(semid,SEM_WRITE);
            printf(">");
            fgets(shmaddr,32,stdin);
            Voperation(semid,SEM_READ);
        }

    }
}

你可能感兴趣的:(嵌入式开发,linux)