• 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操作、信号灯、共享内存

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

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

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

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

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

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

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

    1. P(S) 含义如下:
    2. if (信号量的值大于0) {
    3. 申请资源的任务继续运行;
    4. 信号量的值减一;
    5. }
    6. else { 申请资源的任务阻塞;}
    7. V(S) 含义如下:
    8. 信号量的值加一;
    9. if (有任务在等待资源) {
    10. 唤醒等待的任务,让其继续运行
    11. }

    3 三种信号灯

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

    信号灯的类型  

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

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

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

    3.1 有名信号灯

    3.1.1 打开

    1. sem_t *sem_open(const char *name, int oflag);
    2. 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

    1. #include /* For O_* constants */
    2. #include /* For mode constants */
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. //删除信号,以免程序退出(ctrl+c) 重新打开无法开启
    11. void delsemfile(int sig){
    12. sem_unlink("mysem_r");
    13. exit(0);
    14. }
    15. int main(){
    16. sem_t *sem_r,*sem_w;
    17. key_t key;
    18. int shmid;
    19. char *shmaddr;
    20. struct sigaction act;
    21. act.sa_handler = delsemfile;
    22. act.sa_flags = 0;
    23. sigemptyset(&act.sa_mask);
    24. sigaction(SIGINT,&act,NULL); //启用linux中信号退出回调函数delsemfile,用于删除信号量
    25. key = ftok(".",100);
    26. if(key<0){
    27. perror("ftok");
    28. return 0;
    29. }
    30. shmid = shmget(key,500,0666|IPC_CREAT); //创建信号灯
    31. if(shmid<0){
    32. perror("shmget");
    33. return 0;
    34. }
    35. shmaddr = shmat(shmid,NULL,0); //创建共享内存
    36. sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0); //创建初值为0的读信号灯
    37. sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1); //创建初值为1的写信号灯
    38. while(1){
    39. sem_wait(sem_r); //P操作,获取读资源,读资源-1
    40. printf("%s\n",shmaddr);
    41. sem_post(sem_w); //V操作,写资源+1
    42. }
    43. }

    sem_w.c

    1. #include /* For O_* constants */
    2. #include /* For mode constants */
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. void delsemfile(int sig){
    11. sem_unlink("mysem_w");
    12. exit(0);
    13. }
    14. int main(){
    15. sem_t *sem_r,*sem_w;
    16. key_t key;
    17. int shmid;
    18. char *shmaddr;
    19. struct sigaction act;
    20. act.sa_handler = delsemfile;
    21. act.sa_flags = 0;
    22. sigemptyset(&act.sa_mask);
    23. sigaction(SIGINT,&act,NULL);
    24. key = ftok(".",100);
    25. if(key<0){
    26. perror("ftok");
    27. return 0;
    28. }
    29. shmid = shmget(key,500,0666|IPC_CREAT); //创建信号灯,500个字节
    30. if(shmid<0){
    31. perror("shmget");
    32. return 0;
    33. }
    34. shmaddr = shmat(shmid,NULL,0);
    35. sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
    36. sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
    37. while(1){
    38. sem_wait(sem_w);
    39. printf(">");
    40. fgets(shmaddr,500,stdin);
    41. sem_post(sem_r);
    42. }
    43. }

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

    1. #include /* For O_* constants */
    2. #include /* For mode constants */
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. sem_t sem_r,sem_w;
    12. char *shmaddr;
    13. void destroysem(int sig){
    14. // sem_unlink("mysem_w");
    15. sem_destroy(&sem_r);
    16. sem_destroy(&sem_w);
    17. exit(0);
    18. }
    19. void *readmem(void *arg){
    20. while(1){
    21. sem_wait(&sem_r); //P操作
    22. printf("%s\n",shmaddr);
    23. sem_post(&sem_w); //V操作
    24. }
    25. }
    26. int main(){
    27. key_t key;
    28. int shmid;
    29. struct sigaction act;
    30. act.sa_handler = destroysem;
    31. act.sa_flags = 0;
    32. sigemptyset(&act.sa_mask);
    33. sigaction(SIGINT,&act,NULL);
    34. key = ftok(".",100);
    35. if(key<0){
    36. perror("ftok");
    37. return 0;
    38. }
    39. shmid = shmget(key,500,0666|IPC_CREAT);
    40. if(shmid<0){
    41. perror("shmget");
    42. return 0;
    43. }
    44. shmaddr = shmat(shmid,NULL,0);
    45. // sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
    46. // sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
    47. sem_init(&sem_r,0,0); //有名信号灯创建,读初值0
    48. sem_init(&sem_w,0,1); //有名信号灯创建,写初值1
    49. pthread_t tid;
    50. pthread_create(&tid,NULL,readmem,NULL); //创建线程
    51. while(1){
    52. sem_wait(&sem_w); //P操作
    53. printf(">");
    54. fgets(shmaddr,500,stdin);
    55. sem_post(&sem_r); //V操作
    56. }
    57. }

    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:   操作结构体

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

               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信号灯示例

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #define SEM_READ 0
    11. #define SEM_WRITE 1
    12. union semun {
    13. int val;
    14. };
    15. void Poperation(int semid,int semindex){
    16. struct sembuf sbuf;
    17. sbuf.sem_num = semindex;
    18. sbuf.sem_op = -1;
    19. sbuf.sem_flg = 0;
    20. semop(semid,&sbuf,1);
    21. }
    22. void Voperation(int semid,int semindex){
    23. struct sembuf sbuf;
    24. sbuf.sem_num = semindex;
    25. sbuf.sem_op = 1;
    26. sbuf.sem_flg = 0;
    27. semop(semid,&sbuf,1);
    28. }
    29. int main(){
    30. key_t key;
    31. char *shmaddr;
    32. int semid,shmid;
    33. key = ftok(".",100);
    34. if(key<0){
    35. perror("ftok");
    36. return 0;
    37. }
    38. semid = semget(key,2,IPC_CREAT |0666);
    39. if(semid<0){
    40. perror("semget");
    41. return 0;
    42. }
    43. shmid = shmget(key,500,IPC_CREAT |0666);
    44. shmaddr = shmat(shmid,NULL,0);
    45. union semun mysem;
    46. mysem.val = 0;
    47. semctl(semid,SEM_READ,SETVAL,mysem);
    48. mysem.val = 1;
    49. semctl(semid,SEM_WRITE,SETVAL,mysem);
    50. pid_t pid;
    51. pid = fork();
    52. if(pid<0){
    53. perror("fork");
    54. shmctl(shmid,IPC_RMID,NULL);
    55. semctl(semid,0,IPC_RMID);
    56. exit(-1);
    57. }else if(pid == 0){
    58. while(1){
    59. Poperation(semid,SEM_READ);
    60. printf("%s\n",shmaddr);
    61. Voperation(semid,SEM_WRITE);
    62. }
    63. }else{
    64. while(1){
    65. Poperation(semid,SEM_WRITE);
    66. printf(">");
    67. fgets(shmaddr,32,stdin);
    68. Voperation(semid,SEM_READ);
    69. }
    70. }
    71. }

  • 相关阅读:
    LQ0015 质因数个数【数论】
    MySQL基础篇之多表查询(内连接、外连接、自连接、子查询、union)
    Python爬虫入门基础学习(四)
    C++:map和set
    若依分离版——使用Knife4j 自动生成接口文档
    网工内推 | 上市公司网工,Base广东,思科DE/IE认证优先
    平衡车的建模与控制
    实例方法定义语法(四)
    有人开源全凭“为爱发电”,有人却用开源“搞到了钱”
    Neo4j在linux上的安装与Springboot的集成
  • 原文地址:https://blog.csdn.net/m0_60718520/article/details/133442878