[英]How to make children send info on a timer?
我需要创建一个项目,其中主程序创建给定数量的子进程,每个子进程启动 exec,有两种类型的子进程,它们使用消息队列进行通信。 每一秒钟,所有孩子都必须将他们的状态和情况(如果您想了解更多关于需要什么信息的信息,请告诉我)发送到主程序,我的问题是:所有孩子如何将信息发送到主程序而不会被“打断”系统调用”,因为孩子正在消息队列中等待/写入消息?
char *receiveMessage(int msgQId, long tipo) {
my_msg_buf msgBuf;
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, NULL );
while(1)
if (msgrcv(msgQId, (void *)&msgBuf, MAX_LINE_LENGTH, tipo, IPC_NOWAIT) == -1) {
if(errno != ENOMSG && errno != EINTR){
fprintf(stderr, "Ricevuto errore: %s\n", strerror(errno));
return NULL;
}
if(errno == ENOMSG)
return NULL;
}else break;
sigprocmask(SIG_UNBLOCK, &mask, NULL );
return strdup(msgBuf.testo);
}
我尝试使用循环等待消息并在 errno 等于 EINTR 时继续循环,但它似乎没有工作,孩子似乎永远不会结束。 我也在考虑在每个孩子内部设置一个内部计时器,但我不知道如何处理消息队列以及在执行期间在哪里检查时间。
之前在真正的商业产品中使用过IPC消息队列。
我不必使用信号。 我使用nanosleep
等待,所以这避免了EINTR
的混乱。
下面是一些框架代码。 它仅使用一个消息队列和几种消息类型(一种用于主要,一种用于每个港口,一种用于每艘船)。
它允许:
与所有船舶和港口进行通信的主要进程。
任何港口都可以与其他港口和所有船只相通。
任何船只都可以与其他船只和所有港口进行通信。
在下面的代码中,我展示了一些 harbor 代码。 船舶代码将是相似的。 此外,主要流程代码也类似。
该代码是骨架/不完整的。 但是,它显示了基本机制。
我为(例如)港口数据创建了一个结构。 这用于港口“当前状态”数组和消息中。
最好为港口电流 state 和港口特定数据设置不同的类型。 也就是说,我们可能有struct harbor_state
和struct harbor_msg
,而不是struct harbor
harbor。
无论如何,这是代码。 它编译但未经测试。 是这样注释的:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct ship {
long ship_mtyp; // msgtyp
int ship_idx; // index into ships array
pid_t ship_pid; // ship's process id
// ship specific data ...
int ship_cargo;
};
struct harbor {
long harbor_mtyp; // msgtyp
int harbor_idx; // index into harbors array
pid_t harbor_pid; // harbor's process id
// harbor specific data ...
int harbor_nship;
};
struct main {
long main_mtyp; // msgtyp
int main_total;
};
enum type {
TYPE_SHIP,
TYPE_HARBOR,
TYPE_MAIN,
};
struct msg {
long msg_mtyp;
enum type msg_type;
union {
struct ship ship;
struct harbor harbor;
struct main main;
} msg_data;
};
#define NSHIP 20
#define NHARBOR 5
struct harbor harbors[NHARBOR];
struct ship ships[NSHIP];
long mtyp_uniq = 0; // unique msgtyp
long main_mtyp;
struct main main_data;
int msqid; // message queue id
#define MSGLEN (sizeof(struct msg) - sizeof(long))
typedef long long tod_t;
#define TODNS 1000000000
#define INTVNS TODNS // wakeup every second
// todget -- get time of day in nanoseconds
tod_t
todget(void)
{
struct timespec ts;
tod_t tod;
clock_gettime(CLOCK_MONOTONIC,&ts);
tod = ts.tv_sec;
tod *= 100000000;
tod += ts.tv_nsec;
return tod;
}
tod_t todold;
// waitfor -- wait for next interval
void
waitfor(void)
{
tod_t todnow;
tod_t todfire;
tod_t todsleep;
struct timespec ts;
// get current time
todnow = todget();
// initialize previous firing time
if (todold == 0)
todold = todnow;
// get firing time of next batch of outgoing messages
todfire = todold + INTVNS;
// get amount to sleep
todsleep = todfire - todnow;
// sleep a bit
if (todsleep > 0) {
ts.tv_sec = todsleep / TODNS;
ts.tv_nsec = todsleep % TODNS;
nanosleep(&ts,NULL);
todnow = todget();
}
todold = todnow;
}
// harbor_rcv -- receive all current/pending message sent to this harbor
void
harbor_rcv(struct harbor *self)
{
struct msg msg;
int flags = IPC_NOWAIT;
ssize_t len;
struct ship *smsg;
struct ship *ship;
struct harbor *hmsg;
struct harbor *harbor;
struct main *mmsg;
while (1) {
len = msgrcv(msqid,&msg,MSGLEN,self->harbor_mtyp,flags);
// nothing to process
if (len == 0)
break;
// error
if (len < 0) {
// no message
if (errno == EAGAIN)
break;
perror("msgrcv");
exit(1);
}
// process the message
switch (msg.msg_type) {
case TYPE_SHIP:
smsg = &msg.msg_data.ship;
ship = &ships[smsg->ship_idx];
*ship = *smsg;
break;
case TYPE_HARBOR:
hmsg = &msg.msg_data.harbor;
harbor = &harbors[hmsg->harbor_idx];
*harbor = *hmsg;
break;
case TYPE_MAIN:
mmsg = &msg.msg_data.main;
main_data = *mmsg;
break;
}
}
}
// harbor_process -- perform all actions for given harbor
void
harbor_process(struct harbor *self)
{
struct msg msg;
int flags = 0;
while (1) {
// wait until we should send messages
waitfor();
// receive messages
harbor_rcv(self);
// send our harbor data to all _other_ harbors
for (int harboridx = 0; harboridx < NHARBOR; ++harboridx) {
struct harbor *harbor = &harbors[harboridx];
if (harbor == self)
continue;
// process incoming messages
harbor_rcv(self);
msg.msg_data.harbor = *self;
msg.msg_mtyp = harbor->harbor_mtyp;
msgsnd(msqid,&msg,MSGLEN,flags);
}
// send our harbor data to all ships
for (int shipidx = 0; shipidx < NSHIP; ++shipidx) {
struct ship *ship = &ships[shipidx];
// process incoming messages
harbor_rcv(self);
msg.msg_data.harbor = *self;
msg.msg_mtyp = ship->ship_mtyp;
msgsnd(msqid,&msg,MSGLEN,flags);
}
// send our harbor data to main
msg.msg_data.harbor = *self;
msg.msg_mtyp = main_mtyp;
msgsnd(msqid,&msg,MSGLEN,flags);
// process incoming messages
harbor_rcv(self);
}
exit(0);
}
// ship_process -- perform all actions for given ship
void
ship_process(struct ship *self)
{
// TODO -- similar to harbor_process
}
int
main(void)
{
pid_t pid;
msqid = msgget(IPC_PRIVATE,IPC_CREAT);
if (msqid < 0) {
perror("msgget");
exit(1);
}
main_mtyp = ++mtyp_uniq;
// initialize all ships
for (int shipidx = 0; shipidx < NSHIP; ++shipidx) {
struct ship *ship = &ships[shipidx];
ship->ship_mtyp = ++mtyp_uniq;
ship->ship_idx = shipidx;
}
// initialize all harbors
for (int harboridx = 0; harboridx < NHARBOR; ++harboridx) {
struct harbor *harbor = &harbors[harboridx];
harbor->harbor_mtyp = ++mtyp_uniq;
harbor->harbor_idx = harboridx;
}
// launch all ships
for (int shipidx = 0; shipidx < NSHIP; ++shipidx) {
struct ship *ship = &ships[shipidx];
pid = fork();
if (pid == 0)
ship_process(ship);
else
ship->ship_pid = pid;
}
// launch all harbors
for (int harboridx = 0; harboridx < NHARBOR; ++harboridx) {
struct harbor *harbor = &harbors[harboridx];
pid = fork();
if (pid == 0)
harbor_process(harbor);
else
harbor->harbor_pid = pid;
}
// process incoming messages, etc.
while (1) {
}
return 0;
}
更新:
但是每艘船都必须与每个港口进行通信,所以我最终可能会得到数千条管道,但是在每个“船”和“港口”过程中经过一定时间后我怎么能中断执行呢? – 瓦塞托
不幸的是,拥有“数千个管道”意味着拥有数千个进程。 这不会缩放。 对于超过一定数量(例如 16 个左右)的任意数量的线程/进程,系统将花费大部分时间在它们之间进行上下文切换。
所以,一个不同的model就是“工作线程”model。定义N
个处理消息的线程。 这里的N
相对较小(例如 16)。 每个都从同一个msgtyp
中提取,进行处理,然后循环。
使用线程而不是进程可能会更好。 或者,某些进程具有一定数量的线程。
这一切更多地取决于船舶拥有什么数据,港口拥有什么数据,以及它们如何相互作用。 因此,我们需要了解实际数据的详细信息,哪些操作会触发从船舶到船舶/港口的消息和/或从港口到船舶/港口的消息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.