前言

8. POSIX信号量

8.1 概念

  • 信号量(Semaphore)是一种实现进程/线程间通信的机制,可以实现进程/线程之间同步或临界资源的互斥访问, 常用于协助一组相互竞争的进程/线程来访问临界资源。
  • 在POSIX标准中分无名信号量和有名信号量:
    • 无名信号量

      • 一般用于线程间同步或互斥
      • 无名信号量保存于内存中
    • 有名信号量
      • 一般用于进程间同步或互斥
      • 有名信号量保持于文件中
  • 信号量P、V操作
    • P操作(申请资源)

      • 如果有可用资源,则申请成功,信号量减一
      • 如果没有可用资源,则申请失败,进入阻塞或返回
    • V操作(释放资源)
      • 如果信号量的等待队列中有进程/线程在等待,则唤醒一个阻塞的进程/线程
      • 如果没有进程/线程等待阻塞,则信号量加一

8.2 POSIX无名信号量

  • 无名信号量直接存于内存中,不同进程之间不能互相访问。fork进程中的无名信号量于父进程中的无名信号量是两个独立的信号量。若非要无名信号量用于进程间,则可把信号量放在共享内存中。
  • 包含头文件 #include semaphore.h
  • 相关函数
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);
  • int sem_init(sem_t *sem, int pshared, unsigned int value); :初始化信号量
  • int sem_destroy(sem_t *sem); :销毁信号量
  • int sem_wait(sem_t *sem); :P操作,带阻塞。成功返回0,失败返回-1
  • int sem_trywait(sem_t *sem); :P操作,不阻塞。成功返回0,失败返回EAGAIN
  • int sem_post(sem_t *sem); :V操作。成功返回0,失败返回-1

8.3 POSIX有名信号量

  • 有名信号量保存于文件中,一般用于进程间同步或互斥。其文件名类似 sem.[信号量名字] ,创建该信号量成功后,系统会将其存放在 /dev/shm
    中。进程退出后,该信号量不会消失,需要手动删除并释放资源。
  • 包含头文件 #include semaphore.h
  • 相关函数
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_close(sem_t *sem);
int sem_unlink(const char *name);
  • sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); :打开或创建一个有名信号量
  • int sem_wait(sem_t *sem); :P操作,带阻塞。成功返回0,失败返回-1
  • int sem_trywait(sem_t *sem); :P操作,不阻塞。成功返回0,失败返回EAGAIN
  • int sem_post(sem_t *sem); :V操作。成功返回0,失败返回-1
  • int sem_close(sem_t *sem); :关闭信号量,表示当前进程取消对该信号量的使用权。不影响其它进程/线程对其继续使用。
  • int sem_unlink(const char *name); :删除信号量,其它进程/线程也访问不了了。

8.4 POPSIX信号量与system V信号量的区别

  • 先简单了解一下信号量分类:

    1. 二值信号量:其值为0或为1。
    2. 计数信号量:其值为0至某个限制值(POSIX信号量最大为32767)
    3. 计数信号量集:一个或多个计数信号量构成一个集合。
  • system V信号量所指的是计数信号量集,POSIX信号量所指的是单个计数信号量
  • system V信号量,可以控制每次自增或自减的信号量计数,而POSIX信号量每次只能自增或自减1
  • system V信号量提供的API是没有下划线的:semctl()、semget() 和 semop()
  • system V信号量是内核持续的;POSIX无名信号量是进程持续的;POSIX有名信号量是内核持续

9. POSIX互斥锁

9.1 概念

  • 当不同进程/线程去访问某个临界资源的时候,就需要进行互斥保护,这种互斥保护可以看做是一种锁机制。好比上厕所,锁住门,不让别人进。
  • 互斥锁和信号量不同的是,它具有互斥锁所有权、递归访问等特性,常用于实现对临界资源的独占式处理,任意时刻互斥锁的状态只有两种,开锁或闭锁。
    • 互斥锁所有权就是互斥锁被线程持有时,互斥锁处于闭锁状态,线程获得互斥锁的所有权;当该线程释放互斥锁时,该互斥锁处于开锁状态,线程失去该互斥锁的所有权。
    • 互斥锁递归访问,持有该互斥锁的线程具有对该互斥锁进行递归访问。
  • 避免死锁需要遵循的规则
    • 对共享资源操作前一定要获得锁
    • 完成操作后一定要释放锁
    • 尽量短时间占用锁
    • 如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。
  • 互斥锁比信号量更适合的应用场景:
    • 保护临界资源
    • 线程可能会多次获取互斥锁的情况下。这样可以避免同一线程多次递归持有而造成死锁的问题。
  • 这里的POSIX互斥锁用于线程间

9.2 初始化互斥锁

  • 包含头文件 #include <pthread.h>
  • 互斥锁静态初始化

    选择以下其一即可
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER; // 快速互斥锁
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; // 递归互斥锁
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; // 检错互斥锁
  • 快速互斥锁:具有阻塞机制,不具备递归特性。
  • 递归互斥锁:递归获取互斥锁时,持有互斥锁的计数加1
  • 检错互斥锁:快速互斥锁的非阻塞版本
  • 互斥锁动态初始化
    • mutex 初始化互斥锁结构的指针
    • mutexattr 属性参数,如果该参数为 NULL,则表示选择默认配置,为快速互斥锁。
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

9.3 获取互斥锁与释放互斥锁

  • 注:非递归互斥锁不具备递归特性。
  • int pthread_mutex_lock(pthread_mutex_t *mutex); :获取互斥锁并上锁,具有阻塞功能
  • int pthread_mutex_lock(pthread_mutex_t *mutex); :获取互斥锁并上锁,不阻塞,发现锁被占用后会返回EBUSY错误
  • int pthread_mutex_unlock(pthread_mutex_t *mutex); :解锁并释放互斥锁

9.4 销毁互斥锁

  • int pthread_mutex_destroy(pthread_mutex_t *mutex); :销毁互斥锁

参考

  * 野火

【linux】系统编程-6-POSIX标准下的信号量与互斥锁的更多相关文章

  1. linux系统编程----统计一个目录下的普通文件个数

    主要是为了统计linux系统下一个指定目录下面的普通文件个数,运用目录操作的一些函数,配合递归调用来实现该功能. 首先介绍一下函数原型: 打开一个空目录                    DIR ...

  2. Linux系统编程——线程私有数据

    在多线程程序中.常常要用全局变量来实现多个函数间的数据共享.因为数据空间是共享的,因此全局变量也为全部线程共同拥有. 測试代码例如以下: #include <stdio.h> #inclu ...

  3. Linux系统编程@进程通信(一)

    进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...

  4. 读书笔记之Linux系统编程与深入理解Linux内核

    前言 本人再看深入理解Linux内核的时候发现比较难懂,看了Linux系统编程一说后,觉得Linux系统编程还是简单易懂些,并且两本书都是讲Linux比较底层的东西,只不过侧重点不同,本文就以Linu ...

  5. Linux系统编程【转】

    转自:https://blog.csdn.net/majiakun1/article/details/8558308 一.Linux系统编程概论 1.1 系统编程基石 syscall: libc:标准 ...

  6. 《Linux系统编程(第2版)》

    <Linux系统编程(第2版)> 基本信息 作者: (美)Robert Love 译者: 祝洪凯 李妹芳 付途 出版社:人民邮电出版社 ISBN:9787115346353 上架时间:20 ...

  7. Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号

    Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号 背景 上一讲我们介绍了Unix IPC中的2种管道. 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道 ...

  8. Linux 系统编程 学习:11-线程:线程同步

    Linux 系统编程 学习:11-线程:线程同步 背景 上一讲 我们介绍了线程的属性 有关设置.这一讲我们来看线程之间是如何同步的. 额外安装有关的man手册: sudo apt-get instal ...

  9. Linux系统编程温故知新系列 --- 01

    1.大端法与小端法 大端法:按照从最高有效字节到最低有效字节的顺序存储,称为大端法 小端法:按照从最低有效字节到最高有效字节的顺序存储,称为小端法 网际协议使用大端字节序来传送TCP分节中的多字节整数 ...

随机推荐

  1. GaussDB(DWS)磁盘维护:vacuum full执行慢怎么办?

    摘要:在数据库中用于维护数据库磁盘空间的工具是VACUUM,其重要的作用是删除那些已经标示为删除的数据并释放空间. vacuum的功能 回收空间 数据库总是不断地在执行删除,更新等操作.良好的空间管理 ...

  2. IIS应用程序池配置详解及优化

    参数说明 1.常规 属性名称 属性详解 NET CLR 版本 配置应用程序池,以加载特定版本的 .NET CLR.选定的 CLR版本应与应用程序所使用的相应版本的 .NET Framework 对应. ...

  3. 我用go-zero开发了第一个线上项目

    作者:结冰 前言 ​ 说在最前面,我是一个外表谦让,内心狂热,外表斯文,内心贪玩的一个普通人.我的职业是程序员,是一个golang语言爱好者,一半是因为golang好用,一半是因为其他语言学不好.我是 ...

  4. Python爬取房天下二手房信息

    一.相关知识 BeautifulSoup4使用 python将信息写入csv import csv with open("11.csv","w") as csv ...

  5. 顶会两篇论文连发,华为云医疗AI低调中崭露头角

    摘要:2020年国际医学图像计算和计算机辅助干预会议(MICCAI 2020),论文接收结果已经公布.华为云医疗AI团队和华中科技大学合作的2篇研究成果入选. 同时两篇研究成果被行业顶会收录,华为云医 ...

  6. Winform 去掉 最大化 最小化 关闭按钮(不是关闭按钮变灰)终极解决办法

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  7. 基于nginx负载均衡及frp的内网穿透实例3-多用户多网站共用80端口

    原文地址:点击跳转 最近frp用户量有点多,而且很多用户都是想把部署于本地或者内网的web服务暴露至公网,之前提到过,暴露到公网之后如果一般都需要用域名:端口的方法来访问,但是没有人会喜欢用这种方式访 ...

  8. springboot项目中使用jsp

    在pom文件中 1.方法一 <!-- 引入tomcate内嵌的jsp解析包--> <dependency> <groupId>org.apache.tomcat.e ...

  9. [leetcode]49. Group Anagrams重排列字符串分组

    是之前的重排列字符串的延伸,判断是重排列后存到HashMap中进行分组 这种HashMap进行分组的方式很常用 public List<List<String>> groupA ...

  10. java线程与内核线程的关系,及怎么定义ThreadPoolExecutor相关参数

    p.p1 { margin: 0; font: 12px Menlo } p.p1 { margin: 0; font: 12px Menlo } p.p2 { margin: 0; font: 12 ...