繁体   English   中英

如何让孩子在计时器上发送信息?

[英]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的混乱。

下面是一些框架代码。 它仅使用一个消息队列和几种消息类型(一种用于主要,一种用于每个港口,一种用于每艘船)。

它允许:

  1. 与所有船舶和港口进行通信的主要进程。

  2. 任何港口都可以与其他港口和所有船只相通。

  3. 任何船只都可以与其他船只和所有港口进行通信。


在下面的代码中,我展示了一些 harbor 代码。 船舶代码将是相似的。 此外,主要流程代码也类似。

该代码是骨架/不完整的。 但是,它显示了基本机制。

我为(例如)港口数据创建了一个结构。 这用于港口“当前状态”数组消息中。

最好为港口电流 state 和港口特定数据设置不同的类型。 也就是说,我们可能有struct harbor_statestruct 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM