
内核同步对象(上)
如果为0状态,则表明已经有其他的线程(假设为线程x)正在执“synchronized()”,那么这个线程a将会暂时阻塞,让出cpu资源,进入到线程池中去等待,直到另外的线程x执行完相关的同步代码,线程x并将object对象(也叫做同步监视器)的标志位变为1状态,此时,线程a的阻塞就会被取消,线程a继续运行,该线程会将object对象(或同步监视器)的标志位(或者对象锁)变为0状态,防止其他的线程再次进入到相关的同步代码块中。(3)线程2的run)(方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁持有,线程2肯定是要等待线程1释放lock1的对象锁的。很多synchronized里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都加锁可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。
表4-1. 内核同步对象
对象数据类型描述
Event(事件)
KEVENT
阻塞一个线程直到其它线程检测到某事件发生
Semaphore(信号灯)
KSEMAPHORE
与事件对象相似,但可以满足任意数量的等待
Mutex(互斥)
KMUTEX
执行到关键代码段时,禁止其它线程执行该代码段
Timer(定时器)
KTIMER
推迟线程执行一段时期
Thread(线程)
KTHREAD
阻塞一个线程直到另一个线程结束
在下几段中,我将描述如何使用内核同步对象。我将从何时可以调用等待原语阻塞线程开始讲起,然后讨论用于每种对象的支持例程。最后讨论与线程警惕(thread alert)和提交APC(异步过程调用)相关的概念。
何时阻塞和怎样阻塞一个线程
为了理解WDM驱动程序何时以及如何利用内核同步对象阻塞一个线程,你必须先对线程有一些基本了解。通常,如果程执行时发生了软件或硬件中断,那么在内核处理中断期间,该线程仍然是“当前”线程。而内核模式代码执行时所在的上下文环境就是指这个“当前”线程的上下文。为了响应各种中断,Windows NT调度器可能会切换线程,这样,另一个线程将成为新的“当前”线程。
术语“任意线程上下文(arbitrary thread context)”和“非任意线程上下文(nonarbitrary thread context)”用于精确描述驱动程序例程执行时所处于的上下文种类。如果我们知道程序正处于初始化I/O请求线程的上下文中,则该上下文不是任意上下文。然而,在大部分时间里,WDM驱动程序无法知道这个事实,因为控制哪个线程应该激活的机会通常都是在中断发生时。当应用程序发出I/O请求时,将产生一个从用户模式到内核模式的转换,而创建并发送该IRP的I/O管理器例程将继续运行在非任意线程的上下文中。我们用术语“最高级驱动程序”来描述第一个收到该IRP的驱动程序。
通常,只有给定设备的最高级驱动程序才能确切地知道它执行在一个非任意线程的上下文中。这是因为驱动程序派遣例程通常把请求放入队列后立即返回调用者。之后通过回调函数,请求被提出队列并下传到低级驱动程序。一旦派遣例程挂起某个请求,所有对该请求的后期处理必须发生在任意线程上下文中。
解释完线程上下文后,我们可以陈诉出关于线程阻塞的简单规则:
当我们处理某个请求时,仅能阻塞产生该请求的线程。
仔细想想,在i/o请求的时候,当一个线程因i/o请求阻塞,是可以调用另一个线程来继续发起对另一个url的请求。继续上面的那个例子,不论是排队还是使用号码等待通知,如果在这个等待的过程中,等待者除了等待消息通知之外不能做其它的事情,那么该机制就是阻塞的,表现在程序中,也就是该程序一直阻塞在该函数调用处不能继续往下执行。mongos:集群请求的入口,所有请求需要经过mongos进行协调,无需在应用层面利用程序来进行路由选择,mongos其自身是一个请求分发中心,负责将外部的请求分发到对应的shard服务器上,mongos作为统一的请求入口,为防止mongos单节点故障,一般需要对其做ha。
下面规则表明在提升的IRQL级上不可能发生线程切换:
执行在高于或等于DISPATCH_LEVEL级上的代码不能阻塞线程。
这个规则表明你只能在DriverEntry函数、AddDevice函数,或驱动程序的派遣函数中阻塞当前线程。因为这些函数都执行在PASSIVE_LEVEL级上。没有必要在DriverEntry或AddDevice函数中阻塞当前线程,因为这些函数的工作仅仅是初始化一些数据结构。
在单同步对象上等待
你可以按下面方法调用KeWaitForSingleObject函数:
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); LARGE_INTEGER timeout; NTSTATUS status = KeWaitForSingleObject(object, WaitReason, WaitMode, Alertable, &timeout);
ASSERT语句指出必须在低于或等于DISPATCH_LEVEL级上调用该例程。
在前面一文中已经知道this,其指向不是指向函数自身,也不是指向函数的作用域,一旦创建一函数,系统会默认的生成一个名为this的关键字,它的指向与它所运行的坏境,也就是上下文有关,this是在运行时进行绑定的,它的上下文取决于函数调用的各种条件(函数调用(全局的),方法调用,构造函数调用,间接调用),this的绑定与函数声明的位置没有任何关系,只取决于函数的调用方式,也就是说,它链接到运行该函数的对象,我们之所以频繁的用this,其根本目的就是在找准对象,并对其进行dom相关操作,往往是为了找某个指定的元素,但是又由于this的使用比较特殊,除非特意,大多数时候,我们是不希望找出window对象的,所以非期望对象值总会令你困扰, 使用this时,什么时候出现问题。堆(或 内存堆 heap) 一种常规用途的内存池 也在 ram 内 所有 java 对象都保存在里面 和堆栈不同 内存堆 或 堆heap 最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间 也不必知道存储的数据要在堆里呆多长的时间 因此用堆保存数据时会得到更大的灵活性 要创建一个对象时 只需用 new 命令编制相关的代码即可执行这些代码时 就会在堆里自动进行数据的保存不过 为了获得这种灵活性 我们也必然需要付出一定的代价 假如在内存堆里分配存储空间 和分配规格存储空间相比 前者要花掉更长的时间 和c++不同 java 事实上是不允许在堆栈里创建对象的 这样说 只是为了进行理论上的一种比较。而数组和对象本身在堆中分配,即使程序运行到使用 new产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。
WaitReason是一个纯粹建议性的值,它是KWAIT_REASON枚举类型。实际上,除非你指定了WrQueue参数,否则任何内核代码都不关心此值。线程阻塞的原因被保存到一个不透明的数据结构中,如果你了解这个数据结构,那么在调试某种死锁时,你也许会从这个原因代码中获得一些线索。通常,驱动程序应把该参数指定为Executive,代表无原因。
WaitMode是MODE枚举类型,该枚举类型仅有两个值:KernelMode和UserMode。
Alertable是一个布尔类型的值。它不同于WaitReason,这个参数以另一种方式影响系统行为,它决定等待是否可以提前终止以提交一个APC。如果等待发生在用户模式中,那么内存管理器就可以把线程的内核模式堆栈换出。如果驱动程序以自动变量(在堆栈中)形式创建事件对象,并且某个线程又在提升的IRQL级上调用了KeSetEvent,而此时该事件对象刚好又被换出内存,结果将产生一个bug check。所以我们应该总把alertable参数指定为FALSE,即在内核模式中等待。
说明:指示调用进程的超时时钟在指定的时间后向调用进程发送一个sigalrm信号.设置超时时钟时时间值不会被放入堆栈中,后一次设置会把前一次(还未到超时时间)冲掉.。web socket 心跳包的实现方案05/30/2010 现在网络环境错综复杂,socket心跳包是获得健康强壮的连接的有效解决方案,今天,我们就在web socket中实现心跳包方案,是的,尽管我们只是做一个简单的,但我们让他稳定可靠一些一点也没有错. 我的心跳包方案很是简单,原理就是间隔发送心跳包数据给服务器,服务器在一定时间内发回心跳包响应,对比超时限定,如果超过设定的超时时间,则认为当前与服务器的websocket连接已经断开,关闭当前web socket连接,善后处理,例如重新。 // 释放对象上的锁并阻止当前线程,直到它重新获取该锁 // 如果指定的超时间隔已过,则线程进入就绪队列 // 该方法只有程重新获得锁时才有返回值 // 在超时等待时间内就获得了锁,返回结果为 true,否则为 false // 有时可以利用这个返回结果进行一个代码的分支处理 monitor.wait(monitor, 1000)。
为什么是1601年1月1日
为了弥补这个差值,历法中规定,4年设一闰,即能被4整除的年份为闰年,另附加规定,凡遇世纪年(末尾数字为两个零的年份),必然被400所整除才算闰年。为了弥补这个差值,历法中规定,4年设一闰,即能被4整除的年份为闰年,另附加规定,凡遇世纪年(末尾数字为两个零的年份),必然被400所整除才算闰年。为了弥补这个差值ccriticalsection 调用 2次,历法中规定,4年设一闰,即能被4整除的年份为闰年,另附加规定,凡遇世纪年(末尾数字为2个零的年份),必然被400所整除才算闰年。
指定0超时将使KeWaitForSingleObject函数立即返回,返回的状态代码指出对象是否处于信号态。如果你的代码执行在DISPATCH_LEVEL级上,则必须指定0超时,因为在这个IRQL上不允许阻塞。每个内核同步对象都提供一组KeReadStateXxx服务函数,使用这些函数可以直接获得对象的状态。然而,取对象状态与0超时等待不完全等价:当KeWaitForSingleObject发现等待被满足后,它执行特殊对象要求的附加动作。相比之下,取对象状态不执行任何附加动作,即使对象已经处于信号态。
超时参数也可以指定为NULL指针,这代表无限期等待。
该函数的返回值指出几种可能的结果。STATUS_SUCCESS结果是你所希望的,表示等待被满足。即在你调用KeWaitForSingleObject时,对象或者已经进入信号态,或者后来进入信号态。如果等待以第二种情况满足,则有必要在同步对象上执行附加动作。当然,这个附加动作还要参考对象的类型,我将在后面讨论具体对象类型时再解释这一点。(例如,一个同步类型的事件在你的等待满足后需要重置该事件)
返回值STATUS_TIMEOUT指出在指定的超时期限内对象未进入信号态。如果指定0超时,则函数将立即返回。返回代码为STATUS_TIMEOUT,代表对象处于非信号态,返回代码为STATUS_SUCCESS,代表对象处于信号态。如果指定NULL超时,则不可能有返回值。
发送终止信号给thread线程,如果成功则返回0,否则为非0值。主要用于挂起正在运行的进程进入等待状态,直到有一个子进程终止参数主要用于获取终止进程的退出状态成功返回终止进程的进程号,失败返回-1。这个程序在某些应用中可能会导致程序锁死,比如要将连续、大量接收到的数据进行实时显示或存盘时会发生这种情况,原因是:串口通信线程每接收到一个字符,都要用sendmessage通知主线程,而sendmail是阻塞式的,如果此时主线程正在关闭串口,会用do...while循环连续向串口通信线程,直到串口通信线程中止为止,这个过程也是阻塞式的,此时主线程在不断判断串口通信线程是否中止,通信线程发来的sendmessage消息进行处理,而通信线程则在等待sendmessage的返回,不会对主线程发来的中止信号进行处理,从而导致死锁,进入漫长的超时等待状态。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-112548-1.html
1000次911美国消失得差不多了
台灣問題幕後黑手浮現