系统脉冲
前面提到过,除了用户自己定义的脉冲外,系统(内核)也会向用户进程发送脉冲,用来通报某些状态的发生。在这些系统脉冲里,比较常见的应该是这几个:
- _PULSE_CODE_UNBLOCK
- _PULSE_CODE_DISCONNECT
- _PULSE_CODE_THREADDEATH
- _PULSE_CODE_COIDDEATH
- _PULSE_CODE_UNBLOCK
这个脉冲平时不一定用到。不过,这个UNBLOCK的概念还是很重要的。我们知道,在消息传递中,为了保证可靠性,当客户端向服务器发送消息时,客户端是被“阻塞”(BLOCK)住的。这种状态,要一直到服务器应答了以后才会被开放。这就造成了一个问题,有时候,如果服务器还没有应答,而客户端又希望从阻塞中恢复,那又怎么办呢?
最常见的,比如用户CTRL-C了客户端进程,按POSIX标准,一个SIGINT应该被发送到客户端进程,而客户端进程如果没有特别处理的话,应该被SIGINT终止。那么,如果这时,客户端正好在阻塞状态中,会怎么样呢?
如果客户端进程,就这样突然凭空消失的话,服务器端会混乱,因为服务器端的程序,是建立在客户端被阻塞的情形下的。试想一下,如果客户端突然消失了,而服务器端又要MsgRead(),或是MsgWrite()会发生什么事呢?
为了防止服务器端发生这种混乱,QNX规定如果一个进程,正REPLY BLOCK在别的进程上的时候,它不能自动脱离BLOCK状态;相反地,如果进程有什么理由要退出BLOCK状态,系统会先给服务器端发送一个 “UNBLOCK”脉冲,好象是说“REPLY BLOCK中的xx线程希望脱离阻塞状态”;而由服务器自己判断应该如何处理。服务器端可以在自己内部数据结构做出整理后,选择MsgError(rcvid, EINTR);以解除客户端的阻塞状态;也可以什么也不做,不理睬这个脉冲,这样客户端就不能退出BLOCK状态。有时候你会在QNX遇到Ctrl-c但进程却没有退出的情况,这就是因为进程正被阻塞在一个服务器上,而服务器没有正确处理 UNBLOCK PAULSE的结果。
/*
* Simple server
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/neutrino.h>
int main()
{
int chid, rcvid, status;
struct _pulse pulse;
/* Make sure the channel will handle unblock pulse */
if ((chid = ChannelCreate(_NTO_CHF_UNBLOCK)) == -1) {
perror("ChannelCreate");
return -1;
}
printf("Server is ready, pid = %d, chid = %d\n", getpid(), chid);
for (;;) {
if ((rcvid = MsgReceive(chid, &pulse, sizeof(pulse), NULL)) == -1) {
perror("MsgReceive");
return -1;
}
if (rcvid > 0) {
printf("Server: Received a message, rcvid is %d, do not reply\n", rcvid);
continue;
}
printf("Server: Received a Pulse, pulse code is %d\n", pulse.code);
if (pulse.code != _PULSE_CODE_UNBLOCK) {
continue;
}
printf("Server: client with rcvid %d want to unblock, let go\n",
pulse.value.sival_int);
MsgError(pulse.value.sival_int, EINTR);
}
ChannelDestroy(chid);
return 0;
}
最后,如果服务器端不希望接收UNBLOCK脉冲(非常不推荐),服务器在创建频道的时候,可以不设_NTO_CHF_UNBLOCK标志。在这种情况下,如果处于REPLY BLOCK状态下的客户端希望取消阻塞的话,内核将不再通知服务器,直接让客户端恢复为READY状态。