当前位置: 首页 > news >正文

保定市制作网站公司软件工程专业招聘网站

保定市制作网站公司,软件工程专业招聘网站,网站首页命名,网页素材及网站架构制作内核同步方法1 原子操作原子整数操作原子性与顺序性的比较原子位操作2 自旋锁自旋锁是不可递归的其他针对自旋锁的操作自旋锁和下半部3 读-写自旋锁4 信号量创建和初始化信号量使用信号量5 读-写信号量6 自旋锁和信号量7 完成变量8 互斥锁互斥锁API9 禁止抢占10 顺序和屏障1 原… 内核同步方法1 原子操作原子整数操作原子性与顺序性的比较原子位操作2 自旋锁自旋锁是不可递归的其他针对自旋锁的操作自旋锁和下半部3 读-写自旋锁4 信号量创建和初始化信号量使用信号量5 读-写信号量6 自旋锁和信号量7 完成变量8 互斥锁互斥锁API9 禁止抢占10 顺序和屏障1 原子操作 原子操作是保证指令以原子的方式执行执行过程不被打断。内核提供了两组原子接口一组针对整数进行操作另一组针对单独的位进行操作。在Linux支持的所有体系结构上都实现了这两组接口。 原子整数操作 针对整数的原子操作只能对atomic_t类型的数据进行处理。Linux支持的所有机器上的整数数据都是32位的但是使用atomic_t的代码只能将该类型的数据当做24位来用。这个限制完全是因为在SPARC体系结构上原子操作的实现不同于其他体系结构32位int类型的低8位嵌入了一个锁 因为SPARC体系结构对原子操作缺乏指令级的支持所有只能利用该锁来避免对原子类型数据的并发访问。 原子操作的声明在asm/atomic.h文件中。所有的体系结构内核会提供一些相同的方法有些体系结构会提供一些在该体系结构上使用的额外原子操作方法。 定义一个atomic_t类型的数据还可以在定义时给它设定初值 atomic_t v; /* 定义v */ atomic_t u ATOMIC_INIT(0); /* 定义u并把u初始化为0 */原子整数操作列表如下 原子性与顺序性的比较 原子性确保指令执行期间不被打断要么全部执行完要么根本不执行。而顺序性确保即使两条或多条指令出现在独立的执行线程中它们要执行顺序要按规定的执行。例如给一个整数初始化为10要么初始化成功要么初始化失败这就是原子性。接着又有一个操作给整数初始化为20原子性不管是先初始化为10还是先初始化为20这是顺序性的责任。 原子位操作 原子性操作是与体系结构相关的操作定义在文件asm/bitops.h中。位操作函数是对普通的内存地址进行操作的它的参数是一个指针和一个尾号。原子位操作的列表如下 内核还提供了两个例程用来从指定的地址开始搜素第一个被设置未被设置的位 int find_first_bit(unsigned long *addr,unsigned int size); int find_first_zero_bit(unsigned long *addr,unsigned int size);2 自旋锁 Linux内核最常见的锁是自旋锁spin lock。自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被争用已经被使用的自旋锁那么该线程就会一直进行忙循环等待锁重新可用。在任何时刻自旋锁都可以防止多余一个的执行线程同时进入临界区。 如果自旋锁已经被争用了那么请求它的线程在等待锁重新可用时将一直自旋所以特别浪费处理器时间因此自旋锁不应该被长时间持有。 自旋锁的实现和体系结构密切相关代码往往通过汇编实现。这些与体系结构相关的代码定义在文件asm/spinlock.h中实际需要用到的接口定义在文件linux/spinlock.h中。自旋锁的基本使用形式如下 spinlock_t mr_lock SPIN_LOCK_UNLOCKED;spin_lock(mr_lock); /* 临界区 */ spin_unlock(mr_lock);因为自旋锁在同一时刻至多被一个执行线程持有所以一个时刻只能有一个线程位于临界区内这就为多处理器机器提供了防止并发访问所需的保护机制。注意在单处理器机器上编译的时候并不会加入自旋锁它仅仅被当做一个设置内核抢占机制是否被启用的开关。如果禁止内核抢占那么在编译时自旋锁会被完全剔除内核。 自旋锁是不可递归的 Linux内核实现的自旋锁是不可递归的如果你请求一个你已经持有的自旋锁那么你将会自旋等待释放这个锁由于自旋释放这个锁的操作不会执行所有会一直处于自旋忙等待中于是你被自己锁死了。 自旋锁可以使用在中断处理程序中。在中断处理程序中使用自旋锁时一定要在获取锁之前首先禁止本地中断当前处理器上的中断请求否则中断处理程序就会打断正持有锁的内核代码有可能试图去争用这个已经被持有的自旋锁。这样一来中断处理程序就会自旋但是锁的持有者在这个中断处理程序执行完毕前不可能运行会造成死锁。注意需要关闭的只是当前处理器上的中断如果中断发生在不同的处理器上即使中断处理程序在同一锁上自旋也不会妨碍锁的持有者最终释放锁。 内核提供的禁止中断同时请求锁的接口 spinlock_t mr_lock SPIN_LOCK_UNLOCKED; unsigned long flags;spin_lock_irqsave(mr_lock,flags); /* 临界区 */ spin_lock_irqrestore(mr_lock,flags);函数spin_lock_irqsave保存中断的当前状态并禁止中断然后再去获取指定的锁。反过来spin_lock_irqrestore对指定的锁解锁然后让中断恢复到加锁前的状态。 配置选项CONFIG_DEBUG_SPINLOCK为使用自旋锁的代码加入了许多调试检测手段。 其他针对自旋锁的操作 spin_lock_init()用来初始化动态创建的自旋锁。spin_try_lock试图获得某个特定的自旋锁其他自旋锁操作如下 自旋锁和下半部 由于下半部可以抢占进程上下文中的代码所以当下半部和进程上下文共享数据时必须对进程上下文中的共享数据进行保护所以需要加锁的同时还要禁止下半部执行。同样由于中断程序程序可以抢占下半部所以如果中断处理程序和下半部共享数据那么就必须在获取恰当的锁的同时还要禁止中断。 3 读-写自旋锁 Linux提供了专门的读写自旋锁这种自旋锁为读和写分别提供了不同的锁一个或多个读任务可以并发的持有读者锁相反用于写的锁最多只能被一个写任务持有而且此时不能有并发的读操作。有时把读写锁叫做共享排斥锁或者并发排斥锁因为这种锁以共享对读者而言和排斥对写着而言的形式获得使用。 加锁逻辑 假设临界区内没有任何的thread这时候任何read thread或者write thread可以进入假设临界区内有一个read thread这时候新来的read thread可以任意进入但是write thread不可以进入假设临界区有一个write thread这时候任何的read thread或者write thread 都不可以进入假设临界区内有一个或者多个read threadwrite thread当然不可以进入临界区但是该write thread也无法阻止后续read thread的进入他要一直等到临界区一个read thread也没有的时候才可以进入。可见rw spinlock给reader赋予了更高的权限。 读写自旋锁的使用方式类似于普通自旋锁 rwlock_t my_rwlock RW_LOCK_UNLOCKED; /* 初始化 */ read_lock(my_rwlock); /* 临界区 只读*/ read_unlock(my_lock);在可以写的临界区加上如下代码 write_lock(my_rwlock); /* 临界区写 */ write_unlock(my_rwlock);不能同时请求读锁和写锁 read_lock(my_rwlock); write_lock(my_rwlock);这样将会带来死锁因为写锁会不断自旋而读锁得不到释放。 针对读写自旋锁的操作如下 在使用Linux读-写自旋锁时最后要考虑的一点是这种锁照顾读比照顾写要多一点当读锁被持有时写操作为了互斥访问只能等待但是读者却可以继续成功地占用锁。而自旋等待的写者在所有读者释放锁之前是无法获得锁的。 自旋锁提供了一种快速简单的锁实现方式如果加锁时间不长并且代码不会休眠利用自旋锁时最佳选择。 4 信号量 Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已经被占用的信号量时信号量会将其放到一个等待队列然后让其睡眠。这时处理器能去执行其他代码。当持有信号量的进程将信号量释放后处于等待队列中的那个任务将被唤醒并获得该信号量。 信号量可以同时允许任意数量的锁持有者而自旋锁在一个时刻最多允许一个任务持有它。信号量同时允许的持有者数量可以在声明信号量时指定这个值称为使用者数量。通常情况下信号量和自旋锁一样在一个时刻仅允许有一个锁持有者。当数量等于1这样的信号量被称为二值信号量或者被称为互斥信号量初始化时也可以把数量设置为大于1的非0值这种情况信号量被称为计数信号量它允许在一个时刻至多有count个锁持有者。 信号量支持两个原子操作P()和V()。前者叫做测试操作后者叫做增加操作后来系统把这两种操作分别叫做down()和up()Linux也遵从这种叫法。down()通过对信号量减1来请求一个信号量如果减1结果是0或者大于0那么就获得信号量锁任务就可以进入临界区如果结果是负的那么任务会被放入等待队列。相反当临界区的操作完成后up()操作用来释放信号量如果在该信号量上的等待队列不为空那么处于队列中等待的任务被唤醒。 创建和初始化信号量 信号量的实现是与体系结构有关的具体实现定义在文件asm/semaphore.h中。struct semaphore类型表示信号量。可以通过以下方式静态声明信号量 static DECLARE_SEMAPHORE_GENERIC(name,count);其中name是信号量变量名count是信号量的使用者数量。创建更为普通的互斥信号量可以使用以下方式 static DECLARE_MUTEX(name);我们可以使用sema_init对信号量进行动态初始化 sema_init(sem,count);sem是指针count是信号量的使用者数量。初始化一个动态创建的互斥信号量时使用以下函数 sema_MUTEX(sem)使用信号量 函数down_interruptible()试图获取指定的信号量如果获取失败它将以TASK_INTERRUPTIBLE状态进入睡眠。如果进程在等待获取信号量的时候接受到了信号那么该进程就会被唤醒而函数down_interruptible()会返回EINTR。另外一个函数down()获取信号量失败会让进程在TASK_UNINTERRUPTIBLE状态下睡眠我们应该避免这种情况因为进程等待信号量的时候就不再响应信号了。 使用down_trylock()函数可以尝试获取指定的信号量在信号量被占用时它立刻返回非0值否则返回0并且成功获取信号量锁。 要释放指定的信号需要调用up()函数。 针对信号量的操作如下表 5 读-写信号量 与自旋锁一样信号量也有区分读写访问的可能。读写信号量在内核中是由rw_semaphore结构表示的定义在文件linux/rwsem.h中。通过以下语句可以创建静态声明的读写信号量 static DECLARE_RWSEM(name);动态创建读写信号量可以通过下面的函数 init_rwsem(struct rw_semaphore *sem)所有的读写信号量都是互斥信号量它们的引用计数等于1。只要没有写着并发持有读锁的读者数不限。相反只有唯一的写者没有读者时可以获得写锁。所有的读写锁的睡眠都不会被信号打断它只有一个down()操作 6 自旋锁和信号量 在中断上下文中只能使用自旋锁在任务睡眠时只能使用信号量。 7 完成变量 如果在内核中一个任务需要发出信号通知另一个任务发生了某个特定事件利用完成变量是使两个任务以同步的简单方法。如果一个任务要执行一些工作时另一任务就会在完成变量上等待当这个任务完成后会使用完成变量去唤醒在等待的任务。 完成变量由结构体completion表示定义在linux/cmpletion.h中。可以通过以下方式创建 DECLARE_COMPLETION(mr_comp) /* 静态创建 */ init_completion() /* 动态创建 */在一个指定的完成变量上需要等待的任务调用wait_for_completion()来等待特定事件。当特定事件发生后产生事件的任务调用complete(来发送信号唤醒正在等待的任务。 8 互斥锁 Mutex(互斥锁)是较常用的锁机制为了理解它是怎么工作的来看一看它在include/linux/mutex.h中的结构定义 /** Simple, straightforward mutexes with strict semantics:** - only one task can hold the mutex at a time* - only the owner can unlock the mutex* - multiple unlocks are not permitted* - recursive locking is not permitted* - a mutex object must be initialized via the API* - a mutex object must not be initialized via memset or copying* - task may not exit with mutex held* - memory areas where held locks reside must not be freed* - held mutexes must not be reinitialized* - mutexes may not be used in hardware or software interrupt* contexts such as tasklets and timers** These semantics are fully enforced when DEBUG_MUTEXES is* enabled. Furthermore, besides enforcing the above rules, the mutex* debugging code also implements a number of additional features* that make lock debugging easier and faster:** - uses symbolic names of mutexes, whenever they are printed in debug output* - point-of-acquire tracking, symbolic lookup of function names* - list of all locks held in the system, printout of them* - owner tracking* - detects self-recursing locks and prints out all relevant info* - detects multi-task circular deadlocks and prints out all affected* locks and tasks (and only those tasks)*/ struct mutex {atomic_long_t owner;raw_spinlock_t wait_lock; #ifdef CONFIG_MUTEX_SPIN_ON_OWNERstruct optimistic_spin_queue osq; /* Spinner MCS lock */ #endifstruct list_head wait_list; #ifdef CONFIG_DEBUG_MUTEXESvoid *magic; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lockdep_map dep_map; #endif };其结构中有一个链表类型字段wait_list当竞争者无法获取资源时就会加入到该链表中竞争者从调度器的运行列表中删除放入处于睡眠状态的等待链表wait_list中。然后内核调度并执行其他任务当锁被释放时等待队列中的等待者被唤醒从wait_list移除然后重新被调度。 互斥锁API 声明 静态声明 #define DEFINE_MUTEX(mutexname) \struct mutex mutexname __MUTEX_INITIALIZER(mutexname)动态声明 struct mutex my_mutex; mutex_init(my_mutex);获取互斥锁 void mutex_lock(struct mutex *lock); int mutex_lock_interruptible(struct mutex *lock); int mutex_lock_killable(struct mutex *lock); int mutex_trylock(struct mutex *lock);mutex_lock在不能获得互斥锁时会把任务加入到睡眠队列中必须要保证互斥锁能够释放。mutex_lock_interruptible在不能获得互斥锁时会把任务加入到睡眠队列中但是任务在睡眠队列时有信号到达mutex_lock_interruptible会返回 -EINTR程序会自动往下运行。 mutex_lock_killable在不能获得互斥锁时会把任务加入到睡眠队列中只有杀死任务的信号才能中断驱动程序程序会继续往下运行。 mutex_trylock如果不能获得互斥锁不会把任务加入到睡眠列表中直接返回。 3. 释放互斥锁 void mutex_unlock(struct mutex *lock);有时需要检查互斥锁是否锁定。为此使用mutex_is_locked函数 /*** mutex_is_locked - is the mutex locked* lock: the mutex to be queried** Returns true if the mutex is locked, false if unlocked.*/ extern bool mutex_is_locked(struct mutex *lock);已经被其他任务获取了返回true没有被使用返回false 互斥锁没有轮询机制每次在互斥锁上调用mutex_unlock时内核都会检查wait_list中的等待任务如果有等待者则其中的一个将被唤醒它们唤醒的顺序域它们入睡的顺序相同。 9 禁止抢占 由于内核时抢占性的内核中的进程在任何时候都可能停下来以便另一个更高优先级的进程运行。这意味着一个任务与被抢占的任务可能会在同一个临界区内运行为了避免这种情况内核抢占代码使用自旋锁作为非抢占区域的标记。如果一个自旋锁被持有内核便不能进行抢占。因为内核抢占和SMP面对相同的并发问题并且内核已经是SMP安全的因此这种简单的变化使得内核也是抢占安全的。 实际中某些情况下并不需要自旋锁但是仍然需要关闭内核抢占出现最频繁的情况就是每个处理器上的数据。如果数据对每个处理器是唯一的那么这样的数据可能就不需要使用锁来保护因为数据只能被一个处理器访问如果自旋锁没有被持有内核又是抢占的那么一个新调度的任务就可能访问同一个变量如下所示 这样即使这是一个单处理器变量foo也会被多个进程以伪并发的方式访问。通常这个变量会请求得到一个自旋锁防止多处理器上的真并发。但是如果这是每个处理器上独立的变量可能就不需要锁。 为了解决这个问题可以通过preempt_disable()禁止内核抢占。这是一个可以嵌套调用的函数可以调用任意次。每次调用都必须有一个相应的preempt_enable()调用。当最后一次preempt_enable()被调用后内核抢占才重新启用。 抢占计数存放着持有锁的数量和preempt_disable()的调用次数如果计数是0那么内核可以进行抢占如果为1或更大的值那么内核就不会进行抢占。函数preempt_count()返回这个值。 为了更简洁的方法解决每个处理器上的数据访问问题可以通过get_cpu获得处理器编号。这个函数在返回当前处理器号前会首先关闭内核抢占。put_cpu()会恢复内核抢占 10 顺序和屏障 屏障是告诉编译器不要对给定点周围的指令序列进行重新排序。 rmb()方法提供了一个读内存屏障它确保在rmb()之前的载入操作不会被重新排在该调用之后在rmb()之后的载入操作不会被重新排在该调用之前。 wmb()提供了一个写内存屏障这个函数的功能和rmb()类似区别仅仅是它是针对存储而非载入。 mb()方法即提供了读屏障也提供了写屏障。 内核和编译器屏障方法如下 注意对于不同体系结构屏障的实际效果差别很大。例如如果一个体系结构不执行打乱存储比如intel x86芯片那么wmb()就什么也不做。
http://wiki.neutronadmin.com/news/315970/

相关文章:

  • 网站两个域名简单广告设计软件
  • 站群cms系统区块链
  • 网站公司注册流程淘宝客网站源码html
  • 网站开发需求文件进口网站建设
  • 浙江网站建设抖音seo优化建材网站建设 南宁
  • 一个空间只能放一个网站吗福田住房和建设局网站官网
  • 滁州新手跨境电商建站哪家好wordpress评论头像
  • 求好的设计网站福州网站开发风格
  • 新闻宣传培训网站内容建设网站建设需要的资料
  • 网站建设需不需要编程江苏建设教育协会网站
  • 南海做网站公司网站开发合同注意事项
  • 网站的技术支持汶上手机网站建设
  • 网站建设什么行业天津购物网站搭建
  • 上高做网站公司做配单ic去什么网站好
  • 做网站用百度地图和天地图东莞百域网站建设公司
  • 《网站平台建设》课程实训wordpress无法连接数据库连接
  • 网页版微信二维码登录方法做优化的网站
  • 购买网站模板盘锦兴隆台住房和城乡建设网站
  • 多平台网站设计实例电子商务网站建设功能
  • 网站建设朋友圈广告桥头镇网站仿做
  • 学网站开发的能找什么工作WordPress如何恢复最初
  • 站长之家网站建设酒店找人做网站
  • 用wordpress做淘宝客应用商店关键词优化
  • 天津微网站建设seo软件哪个好
  • 成都网站建设网络公司php网站后台密码忘记了怎么办
  • 就业创业网站建设潮州 做网站 有钱
  • 网站建设服务网络服务wordpress百度网盘插件
  • element-ui网站开发广告推广方式
  • 安徽住房和建设厅网站搜狗网
  • 网站做装修x3型虚拟主机 wordpress