为了实现IPC的消息机制,需要为进程表增加很多成员。
struct proc {
struct stackframe regs; /* process registers saved in stack frame */
u16 ldt_sel; /* gdt selector giving ldt base and limit */
struct descriptor ldts[LDT_SIZE]; /* local descs for code and data */
int ticks; /* remained ticks */
int priority;
u32 pid; /* process id passed in from MM */
char name[16]; /* name of the process */
=========================================================
int p_flags; /** * process flags. * A proc is runnable iff p_flags==0 */
MESSAGE * p_msg;
int p_recvfrom;
int p_sendto;
int has_int_msg; /** * nonzero if an INTERRUPT occurred when * the task is not ready to deal with it. */
struct proc * q_sending; /** * queue of procs sending messages to * this proc */
struct proc * next_sending;/** * next proc in the sending * queue (q_sending) */
int nr_tty;
};
p_flags用于标明进程的状态。目前它的取值可以有三种:
–0进程正在运行或准备运行。
–SENDING进程处于发送消息的状态。由于消息还未送达,进程被阻塞。
–RECEIVING进程处于接收消息的状态。由于消息还未收到,进程被阻塞。
p_msg指向消息体的指针。
p_recvfrom假设进程P想要接收消息,但目前没有进程发消息给它,本成员记录P想要从谁那里接收消息。
p_sendto假设进程P想要发送消息,但目前没有进程接收它,本成员记录P想要发送消息给谁。
has_int_msg如果有一个中断需要某进程来处理,或者换句话说,某进程正在等待一个中断发生──比如硬盘驱动可能
会等待硬盘中断的发生,系统在得知中断发生后会将此位置为1。
q_sending如果有若干进程──比如A、B和C──都向同一个进程P发送消息,而P此时并未准备接收消息,那么A、B和C
将会排成一个队列。进程P的q_sending指向第一个试图发送消息的进程。
next_sending试图发送消息的A、B和C (依时间顺序)三进程排成的队列的实现方式是这样的:目的进程P的进程表的
q_sending指向A,进程A的进程表的next_sending指向B,进程B的进程表的next_sending指向C,进程C的进程表的
next_sending指向空。
不管是接收方还是发送方,都各自维护一个消息结构体,只不过发送方的结构体是携带了消息内容的而接收方是空的。由于我们使用同步IPC,一方的需求──发送或接收──只有被满足之后才会继续运行,所以操作系统不需要维护任何的消息缓冲,实现起来也就相对简单。
/*****************************************************************************
* msg_send
*****************************************************************************/
/**
* <Ring 0> Send a message to the dest proc. If dest is blocked waiting for
* the message, copy the message to it and unblock dest. Otherwise the caller
* will be blocked and appended to the dest's sending queue.
*
* @param current The caller, the sender.
* @param dest To whom the message is sent.
* @param m The message.
*
* @return Zero if success.
*****************************************************************************/
PRIVATE int msg_send(struct proc* current, int dest, MESSAGE* m)
{
struct proc* sender = current;
struct proc* p_dest = proc_table + dest; /* proc dest */
assert(proc2pid(sender) != dest);
/* check for deadlock here */
if (deadlock(proc2pid(sender), dest)) {
panic(">>DEADLOCK<< %s->%s", sender->name, p_dest->name);
}
if ((p_dest->p_flags & RECEIVING) && /* dest is waiting for the msg */
(p_dest->p_recvfrom == proc2pid(sender) ||
p_dest->p_recvfrom == ANY)) {
assert(p_dest->p_msg);
assert(m);
phys_copy(va2la(dest, p_dest->p_msg),
va2la(proc2pid(sender), m),
sizeof(MESSAGE));
p_dest->p_msg = 0;
p_dest->p_flags &= ~RECEIVING; /* dest has received the msg */
p_dest->p_recvfrom = NO_TASK;
unblock(p_dest);
assert(p_dest->p_flags == 0);
assert(p_dest->p_msg == 0);
assert(p_dest->p_recvfrom == NO_TASK);
assert(p_dest->p_sendto == NO_TASK);
assert(sender->p_flags == 0);
assert(sender->p_msg == 0);
assert(sender->p_recvfrom == NO_TASK);
assert(sender->p_sendto == NO_TASK);
}
else { /* dest is not waiting for the msg */
sender->p_flags |= SENDING;
assert(sender->p_flags == SENDING);
sender->p_sendto = dest;
sender->p_msg = m;
/* append to the sending queue */
struct proc * p;
if (p_dest->q_sending) {
p = p_dest->q_sending;
while (p->next_sending)
p = p->next_sending;
p->next_sending = sender;
}
else {
p_dest->q_sending = sender;
}
sender->next_sending = 0;
block(sender);
assert(sender->p_flags == SENDING);
assert(sender->p_msg != 0);
assert(sender->p_recvfrom == NO_TASK);
assert(sender->p_sendto == dest);
}
return 0;
}
/*****************************************************************************
* msg_receive
*****************************************************************************/
/**
* <Ring 0> Try to get a message from the src proc. If src is blocked sending
* the message, copy the message from it and unblock src. Otherwise the caller
* will be blocked.
*
* @param current The caller, the proc who wanna receive.
* @param src From whom the message will be received.
* @param m The message ptr to accept the message.
*
* @return Zero if success.
*****************************************************************************/
PRIVATE int msg_receive(struct proc* current, int src, MESSAGE* m)
{
struct proc* p_who_wanna_recv = current; /**
* This name is a little bit * wierd, but it makes me * think clearly, so I keep * it. */
struct proc* p_from = 0; /* from which the message will be fetched */
struct proc* prev = 0;
int copyok = 0;
assert(proc2pid(p_who_wanna_recv) != src);
if ((p_who_wanna_recv->has_int_msg) &&
((src == ANY) || (src == INTERRUPT))) {
/* There is an interrupt needs p_who_wanna_recv's handling and
* p_who_wanna_recv is ready to handle it.
*/
MESSAGE msg;
reset_msg(&msg);
msg.source = INTERRUPT;
msg.type = HARD_INT;
assert(m);
phys_copy(va2la(proc2pid(p_who_wanna_recv), m), &msg,
sizeof(MESSAGE));
p_who_wanna_recv->has_int_msg = 0;
assert(p_who_wanna_recv->p_flags == 0);
assert(p_who_wanna_recv->p_msg == 0);
assert(p_who_wanna_recv->p_sendto == NO_TASK);
assert(p_who_wanna_recv->has_int_msg == 0);
return 0;
}
/* Arrives here if no interrupt for p_who_wanna_recv. */
if (src == ANY) {
/* p_who_wanna_recv is ready to receive messages from
* ANY proc, we'll check the sending queue and pick the
* first proc in it.
*/
if (p_who_wanna_recv->q_sending) {
p_from = p_who_wanna_recv->q_sending;
copyok = 1;
assert(p_who_wanna_recv->p_flags == 0);
assert(p_who_wanna_recv->p_msg == 0);
assert(p_who_wanna_recv->p_recvfrom == NO_TASK);
assert(p_who_wanna_recv->p_sendto == NO_TASK);
assert(p_who_wanna_recv->q_sending != 0);
assert(p_from->p_flags == SENDING);
assert(p_from->p_msg != 0);
assert(p_from->p_recvfrom == NO_TASK);
assert(p_from->p_sendto == proc2pid(p_who_wanna_recv));
}
}
else {
/* p_who_wanna_recv wants to receive a message from
* a certain proc: src.
*/
p_from = &proc_table[src];
if ((p_from->p_flags & SENDING) &&
(p_from->p_sendto == proc2pid(p_who_wanna_recv))) {
/* Perfect, src is sending a message to
* p_who_wanna_recv.
*/
copyok = 1;
struct proc* p = p_who_wanna_recv->q_sending;
assert(p); /* p_from must have been appended to the
* queue, so the queue must not be NULL
*/
while (p) {
assert(p_from->p_flags & SENDING);
if (proc2pid(p) == src) { /* if p is the one */
p_from = p;
break;
}
prev = p;
p = p->next_sending;
}
assert(p_who_wanna_recv->p_flags == 0);
assert(p_who_wanna_recv->p_msg == 0);
assert(p_who_wanna_recv->p_recvfrom == NO_TASK);
assert(p_who_wanna_recv->p_sendto == NO_TASK);
assert(p_who_wanna_recv->q_sending != 0);
assert(p_from->p_flags == SENDING);
assert(p_from->p_msg != 0);
assert(p_from->p_recvfrom == NO_TASK);
assert(p_from->p_sendto == proc2pid(p_who_wanna_recv));
}
}
if (copyok) {
/* It's determined from which proc the message will
* be copied. Note that this proc must have been
* waiting for this moment in the queue, so we should
* remove it from the queue.
*/
if (p_from == p_who_wanna_recv->q_sending) { /* the 1st one */
assert(prev == 0);
p_who_wanna_recv->q_sending = p_from->next_sending;
p_from->next_sending = 0;
}
else {
assert(prev);
prev->next_sending = p_from->next_sending;
p_from->next_sending = 0;
}
assert(m);
assert(p_from->p_msg);
/* copy the message */
phys_copy(va2la(proc2pid(p_who_wanna_recv), m),
va2la(proc2pid(p_from), p_from->p_msg),
sizeof(MESSAGE));
p_from->p_msg = 0;
p_from->p_sendto = NO_TASK;
p_from->p_flags &= ~SENDING;
unblock(p_from);
}
else { /* nobody's sending any msg */
/* Set p_flags so that p_who_wanna_recv will not
* be scheduled until it is unblocked.
*/
p_who_wanna_recv->p_flags |= RECEIVING;
p_who_wanna_recv->p_msg = m;
if (src == ANY)
p_who_wanna_recv->p_recvfrom = ANY;
else
p_who_wanna_recv->p_recvfrom = proc2pid(p_from);
block(p_who_wanna_recv);
assert(p_who_wanna_recv->p_flags == RECEIVING);
assert(p_who_wanna_recv->p_msg != 0);
assert(p_who_wanna_recv->p_recvfrom != NO_TASK);
assert(p_who_wanna_recv->p_sendto == NO_TASK);
assert(p_who_wanna_recv->has_int_msg == 0);
}
return 0;
}