文档章节

linux 并发控制

z
 zsaniu
发布于 2017/05/16 11:22
字数 2119
阅读 7
收藏 0
点赞 0
评论 0

现代操作系统有三大特性:中断处理、多任务处理和多处理器。这些特性导致当多个进程、线程或者CPU同时访问一个资源时,可能发生错误,这些错误是操作系统运行所不允许的。在操作系统中,内核需要提供并发控制机制,对共享资源进行保护。

  在操作系统中,并发是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。并发容易导致竞争的问题。竞争就是两个或两个以上的进程同时访问一个资源,同时引起资源的错误。并发控制机制有以下几种:

1.原子变量操作:
  原子变量操作(分为原子整型操作和原子位操作)就是绝不会在执行完毕前被任何其他任务和时间打断,不会执行一半,又去执行其他代码。原子操作需要硬件的支持,因此是架构相关的,其API和原子类型的定义都在include/asm/atomic.h中,使用汇编语言实现。
  在linux中,原子变量的定义如下:

typedef struct {
        volatile int counter;
    } atomic_t;

  关键字volatile用来暗示GCC不要对该类型做数据优化,所以对这个变量counte的访问都是基于内存的,不要将其缓冲到寄存器中。存储到寄存器中,可能导致内存中的数据已经改变,而寄存其中的数据没有改变。

(1)原子整型操作:
1)定义atomic_t变量: 

#define ATOMIC_INIT(i) ( (atomic_t) { (i) } )
atomic_t v = ATOMIC_INIT(0);    //定义原子变量v并初始化为0

2)设置原子变量的值:

#define atomic_set(v,i) ((v)->counter = (i))
void atomic_set(atomic_t *v, int i);//设置原子变量的值为i 

3)获取原子变量的值:

#define atomic_read(v) ((v)->counter + 0)
atomic_read(atomic_t *v);//返回原子变量的值

4)原子变量加/减:

static __inline__ void atomic_add(int i, atomic_t * v); //原子变量增加i 
static __inline__ void atomic_sub(int i, atomic_t * v); //原子变量减少i

5)原子变量自增/自减:

#define atomic_inc(v) atomic_add(1, v); //原子变量加1 
#define atomic_dec(v) atomic_sub(1, v); //原子变量减1

6)操作并测试:

//这些操作对原子变量执行自增,自减,减操作后测试是否为0,是返回true,否则返回false 
#define atomic_inc_and_test(v) (atomic_add_return(1, (v)) == 0)
static inline int atomic_add_return(int i, atomic_t *v)

 

(2)原子位操作(根据数据的每一位单独进行操作):

View Code


  原子操作的优点编写简单;缺点是功能太简单,只能做计数操作,保护的东西太少。

2.自旋锁
  自旋锁是专为防止多处理器并发而引入的一种锁,它应用于中断处理等部分。对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁。
  自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。
(1)自旋锁的使用:

spinlock_t spin; //定义自旋锁
spin_lock_init(lock); //初始化自旋锁
spin_lock(lock); //成功获得自旋锁立即返回,否则自旋在那里直到该自旋锁的保持者释放
      spin_trylock(lock); //成功获得自旋锁立即返回真,否则返回假,而不是像上一个那样"在原地打转"
spin_unlock(lock);//释放自旋锁

使用代码:

spinlock_t lock; 
spin_lock_init(&lock); 
spin_lock (&lock); 
....//临界资源区 
spin_unlock(&lock);

(2)注意事项:
  1)自旋锁是一种忙等待。它是一种适合短时间锁定的轻量级的加锁机制。
  2)自旋锁不能递归使用。自旋锁被设计成在不同线程或者函数之间同步。这是因为,如果一个线程在已经持有自旋锁时,其处于忙等待状态,则已经没有机会释放自己持有的锁了。如果这时再调用自身,则自旋锁永远没有执行的机会了。

3.信号量
  linux中,提供了两种信号量:一种用于内核程序中,一种用于应用程序中。这里只讲属前者。
  信号量和自旋锁的使用方法基本一样。与自旋锁相比,信号量只有当得到信号量的进程或者线程时才能够进入临界区,执行临界代码。信号量和自旋锁的最大区别在于:当一个进程试图去获得一个已经锁定的信号量时,进程不会像自旋锁一样在远处忙等待。
  信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。

(1)信号量的实现:
  在linux中,信号量的定义如下:

复制代码

struct semaphore {
    spinlock_t        lock;      //用来对count变量起保护作用。
    unsigned int        count;     //    大于0,资源空闲;等于0,资源忙,但没有进程等待这个保护的资源;小于0,资源不可用,并至少有一个进程等待资源。
    struct list_head    wait_list; //存放等待队列链表的地址,当前等待资源的所有睡眠进程都会放在这个链表中。
};

复制代码

(2)信号量的使用:

 

1)定义信号量:

struct semaphore sem;

 

2)初始化信号量 :

static inline void sema_init(struct semaphore *sem, int val); //设置sem为val

#define init_MUTEX(sem) sema_init(sem, 1) //初始化一个用户互斥的信号量sem设置为1
#define init_MUTEX_LOCKED(sem) sema_init(sem, 0) //初始化一个用户互斥的信号量sem设置为0

定义和初始化可以一步完成:

DECLARE_MUTEX(name); //该宏定义信号量name并初始化1
DECLARE_MUTEX_LOCKED(name); //该宏定义信号量name并初始化0

   当信号量用于互斥时(即避免多个进程同是在一个临界区运行),信号量的值应初始化为1。这种信号量在任何给定时刻只能由单个进程或线程拥有。在这种使用模式下,一个信号量有事也称为一个“互斥体(mutex)”,它是互斥(mutual exclusion)的简称。Linux内核中几乎所有的信号量均用于互斥。

  使用信号量,内核代码必须包含<asm/semaphore.h> 。

 

3)获取(锁定)信号量:

void down(struct semaphore *sem);

View Code

int down_interruptible(struct semaphore *sem);

View Code

int down_killable(struct semaphore *sem);

View Code

 
 
4)释放信号量
void up(struct semaphore *sem); //释放信号量sem,唤醒等待者

 

4.完成量
  它用于一个执行单元等待另一个执行单元执行完某事;

struct completion {
    unsigned int done;    //大于0,表示完成量的函数可以立即执行,不要要等待;等于0,将拥有完成量的线程置于等待状态。
    wait_queue_head_t wait;
};

  使用方法:

1)定义完成量:

struct completion com;

2)初始化:

init_completion(&com); //要是觉得这两步麻烦,就再给你来个宏即定义又初始化DECLARE_COMPLETION(com);

3)等待完成量:

void __sched wait_for_completion(struct completion *x); //等待一个completion被唤醒
 

View Code

int __sched wait_for_completion_interruptible(struct completion *x);//可中断的wait_for_completion

View Code

unsigned long __sched wait_for_completion_timeout(struct completion *x, unsigned long timeout);//带超时处理的wait_for_completion

View Code

4)唤醒完成量

void complete(struct completion *x); //只唤醒一个等待的进程或线程。
void complete_all(struct completion *x); //唤醒所有等待这个完成量的进程或者线程。

 

后记:除了上述几种广泛使用的的并发控制机制外,还有中断屏蔽顺序锁(seqlock)、RCU(Read-Copy-Update)等等,做个简单总结如下图:

本文转载自:http://blog.163.com/cl2006ky@126/blog/static/871951732013324103335312/

共有 人打赏支持
z
粉丝 0
博文 36
码字总数 20723
作品 0
深圳
程序员
【转载】配置开发支持高并发TCP连接的Linux应用程序全攻略

1、修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量 的限制(这是...

晨曦之光
2012/03/09
0
0
Linux下高并发socket最大连接数

1、修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是...

fengyunsen
06/26
0
0
Linux下高并发socket最大连接数所受的各种限制

1、修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是...

guru13
2013/12/10
0
0
配置开发支持高并发TCP连接的Linux应用程序全攻略

1、修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量 的限制(这是...

囧南风囧
2010/10/22
0
1
Linux多线程并发服务器编程(线程池,FTP服务器)

分享网盘下载:https://pan.baidu.com/s/1gfNCcXt 密码: irfk 内容简介 本课程从最基础的进程、线程概念讲起逐步深入,通过理论与实践结合的方式,使学员快说掌握linux多线程网络编程技术,并...

人气王子333
06/26
0
0
利用HTML5分片上传超大文件

在网页中直接上传大文件一直是个比较头疼的问题,主要面临的问题一般包括两类:一是上传时间长中途一旦出错会导致前功尽弃;二是服务端配置复杂,要考虑接收超大表单和超时问题,如果是托管主...

miscellanea
2014/09/22
0
0
为什么要学一学并发编程

世界上,很多事情在同时发生。 咖啡厅里同时坐着很多人,门口的马路上同时有多辆车通过。而盯着屏幕的你,还听着耳机里迷人的音乐,写代码的同时,还浏览新闻。 但不知道你有没有注意到,这里...

润着
2017/11/15
0
0
Linux设备驱动开发学习(2):Linux设备驱动简介

2.1 设备驱动的角色 设备驱动是介于应用软件和硬件设备(或其他虚拟设备)之间的程序,驱动完成对硬件设备(或其他虚 拟设备)的管理,应用软件对硬件的访问通过驱动程序来完成。当环境变得复...

lengxujun
06/26
0
0
squid代理服务器详细配置及介绍

转载 http://kangshuo.blog.51cto.com 简介: squid 是 Linux系统中最常用的一款开源代理服务软件 (官方网站:http//www.squid-cache.org) 可以很好地实现HTTP和FTP,以及DNS查询、SSL等应...

李伟铭k
07/09
0
0
Linux 多线程

线程的优点: 减少系统调度开销,不占有独立的资源,切换速度快,执行效率高。 线程间通信方便,可共享资源。 改善程序设计结构,功能复杂的进程可以分为多个独立的线程分别执行,模块性更强...

文艺小青年
2017/11/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

组件及路由理论知识

一、 组件component 1. 什么是组件? 组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码组件是自定义元素(对象) 2. 定义组件的方式 方式1:先创建...

一个yuanbeth
刚刚
0
0
Saltstack配置之 nodegroups

#cd /etc/salt #mkdir master.d #vim node.conf //按组写入文件 nodegroups: client_all: 'L@192.168._._,192.168._._' clienta: 'L@192.168.192._' clientb: 'L@192.168.192._' #/etc/init......

硅谷课堂
6分钟前
0
0
expect(spawn) 自动化git提交和scp拷贝---centos(linux)

**在进行SCP文件拷贝中,往往需要进行用户密码的输入,即用户交互。若采用自动化脚本的方式进行,则可用以下方式: ** #!/usr/bin/expect #设置参数 set src [lindex $argv 0] set dest [lin...

helplove
10分钟前
1
0
用Build来构建对象的写法

如果一个类的属性过多,用构造器来构建对象很难写,因此我们时用Build方式来构建对象。写法大致如下。 import java.io.Serializable;import java.util.Date;public class Log impleme...

算法之名
12分钟前
11
0
利用 acme.sh 获取网站证书并配置https访问

acme.sh 实现了 acme 协议, 可以从 letsencrypt 生成免费的证书.(https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) 主要步骤: 安装 acme.sh 生成证书 copy 证书到 nginx/ap...

haoyuehong
26分钟前
2
0
微擎框架内如何根据media_id获取到微信图片的路径

微擎的框架内,图片选择后,获取的是那个字符串是media_id,相当于你这张图片在微信的图片服务器里面的id 要求是:获取https://mmbiz.qpic.cn/mmbiz_jpg/…… 微信图片的路径 而微信并没有根据m...

老bia同学
30分钟前
2
0
Spring boot中日期的json格式化

Model 在model层中,类的日期属性上面添加如下注解: @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") 参考 Jackson Date格式化教程...

亚林瓜子
31分钟前
2
0
Eclipse:Failed to load the JNI shared library

1.问题背景: 由于我之前使用jdk1.9学习,当使用Luke的时候发现jdk版本过高,需要向下配置jdk,就向朋友拷了一个安装包。重新配置路径后,便开始报错。 2.问题描述: Failed to load the JNI...

tinder_boy
34分钟前
1
0
少儿学习编程课程是否真的适合七八岁的低龄儿童[图]

少儿学习编程课程是否真的适合七八岁的低龄儿童[图]: 天下熙熙皆为利来,天下攘攘皆为利往。 这几年来,乐高教育机构在国内如同雨后春笋般出现,当然关闭/转手的也很多。从教师角度来看,部...

原创小博客
39分钟前
1
0
ES12-词项查询

1.词项查询介绍 全文查询将在执行之前分析查询字符串,但词项级别查询将按照存储在倒排索引中的词项进行精确操作。这些查询通常用于数字,日期和枚举等结构化数据,而不是全文本字段。 或者,...

贾峰uk
47分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部