[리눅스]메세지 큐를 이용한 serial & key & timer check

/**
 * 부모와 자식 프로세서로 나누고,
 * 부모프로세서에서는 시리얼로 부터 데이터를 받아 들이고,
 * 자식프로세서는 time out체크와, CANCEL키 체크를 하도록 했다.
 * 두 프로세서 간의 통신은 메세지 큐를 이용했다.
 *
 * 이 프로그램은 자식 프로세서에 걸린 timer가 지정된 시간을 초과시
 * 부모 프로세서에게 "TIMEOUT"메세지를 보내면, 부모 프로세서가 자식
 * 프로세서에게 SIGTERM을 보내서 자식 프로세서를 끝내고,
 * 부모 프로세서는 자식 프로세서가 종료 됐다는 것을 알게 되면(waitpid)
 * 프로그램을 종료 하도록 만들었다.
 */
#include "include.h"

#define MOL
#include "app_pub.h"
#include "app_def.h"
#include "app_func.h"

#define SIZE    10
#define KEY     1234

struct {
    long mtype;
    char data[SIZE];
} msg_data;/**< 메세지 큐에서 사용할 메세지 구조체*/


int main(void)
{
    struct pollfd Event;
    sigset_t set,oldset,pend;
    int pid,cnt,status,child,i,j;
    char kch;
    int msgid;/**< msgget()으로 가져온 메세지 큐의 id를 저장할 변수*/

    i = j = 0;
    
    if ((pid=fork()) < 0)
    {
        perror("fork() Error");
        exit(1);
    }

    if (pid)
    {   //parent
        // 부모 프로세서에서 메세지큐를 생성한다.
        // 메세지 큐를 만든다.(mode 0666)
        // 동일한 key의 메세지 큐가 있으면, 식별자만 반환(IPC_CREAT)
        if ((msgid = msgget((key_t)KEY,IPC_CREAT|0666)) == -1)
        {
            perror("msgget() Error");
            exit(1);
        }
        printf("parent msgid %d\n",msgid);

        sio_set(PRIVATE_PORT,B9600);

        memset(tempbuf,0,sizeof(tempbuf));
        rlen = 0;

        Event.fd = fd;
        Event.events = POLLIN | POLLERR;

        while(1)
        {
            child = waitpid(pid,&status, WNOHANG);
            cnt = poll((struct pollfd *)&Event,1,1000);
            if (cnt < 0)
            {
                perror("parent poll() Error");
                exit(1);
            }
            else
            if (!cnt)
            {
                printf("Parent %d\n",i++);
                // 1초마다.메세지 큐에 있는 메세지를 읽는다.
                // 메세지가 없으면, 기다리지 않고 -1을 리턴하고 넘어간다.
                // msg_data.mtype = 1인 것만 읽어온다.
                // msg_data.mtype 에 상관없이 맨 앞에것만 읽어온다.
                if (msgrcv(msgid,&msg_data,SIZE,0,IPC_NOWAIT) > 0)
                {   // 메세지가 있으면,
                    printf("msg_data mtype = %ld\n",msg_data.mtype);
                    printf("msg_data = %s\n",msg_data.data);
                    if (strstr(msg_data.data,"TIMEOUT"))
                    {
                        // 메세지 큐(msg_data.data)가 "TIMEOUT"인 경우에는
                        // 프로그램을 종료한다.
                        if (msgctl(msgid,IPC_RMID,0) == -1)
                        {
                            perror("child msgctl() Error");
                            release_timer();
                            exit(1);
                        }
                        printf("msgid(%d) Delete\n",msgid);
                        // child process에는 테스트를 위해 종료 부분을 뺐다
                        kill(pid,SIGTERM);
                    }
                }
                if (child>0)
                {
                    printf("Child Process Term\n");
                    exit(1);
                }
            }
            else
            if (cnt)
            {
                RCH = com_getc();
                tempbuf[rlen++] = RCH;
                printf("tempbuf[]>> \n");
                printf("%s \n",tempbuf);

            }
        }//while
    }//parent
    else
    {   //child
        // KEY를 갖도록 메세지큐를 생성 또는 기존에 있으면, 메세지ID(msgid)
        // 를 얻어온다.(기존에 메세지 큐가 있으면, 접근 모드는 무시된다.)
        if ((msgid = msgget((key_t)KEY,IPC_CREAT|0666)) == -1)
        {
            perror("child msgget() Error");
            exit(1);
        }

        keyhandle = open("/dev/key",O_RDWR);

        printf("child msgid %d",msgid);
        Event.fd = keyhandle;
        Event.events = POLLIN | POLLERR;

        sigemptyset(&set);
        sigaddset(&set, SIGALRM);
        sigprocmask(SIG_BLOCK,&set,&oldset);

        set_timer(10);
        while(1)
        {
            cnt = poll((struct pollfd *)&Event,1,1000);
            if (cnt < 0)
            {
                perror("child poll() Error");
                exit(1);
            }
            else
            if (!cnt)
            {
                printf("Child %d\n",j++);
                sigpending(&pend);
                if (sigismember(&pend,SIGALRM))
                {
                    //struct msgbuf에 type(long)값을 꼭 줘야 한다.
                    //(msgsnd()시, type에 값이 없으면, argument error가 난다.
                    msg_data.mtype = 1;
                    strcpy(msg_data.data,"TIMEOUT");
                    if (msgsnd(msgid,&msg_data,strlen(msg_data.data),0) ==-1)
                    {
                        perror("child msgsnd() Error");
                        release_timer();
                        exit(1);
                    }
                    release_timer();
                    // set_timer()의 타임아웃을 체크했으면,
                    // 시그널들을 원래대로 되돌린다.
                    sigprocmask(SIG_SETMASK,&oldset,NULL);
                }
            }
            else
            if (cnt)
            {
                kch = GetKey();
                if (kch == CANCEL)
                {
                    msg_data.mtype = 1;
                    strcpy(msg_data.data,"CANCEL");
                    if (msgsnd(msgid,&msg_data,strlen(msg_data.data),0) == -1)
                    {
                        perror("child msgsnd()2 Error");
                        exit(1);
                    }
                    //release_timer();
                    //exit(1);
                }                
            }
        }//while
    }//chile
}