(1) 주어진 파일을 Copy 하는 프로그램을 작성하되, copy SIGINT SIGQUIT을 받아도 죽지않고 COPY를 모두 마치닣후 종료하는 프로그램을 작성하라.

주어진 파일을 카피하는 과정이야 어떤 파일명을 받아서 그파일을 바이너리로 읽어 그대로 다른 파일명으로 써주면 된다. 쉽게 할 수 있는 과정인데 이꽈제는 SIGINT SIGQUIT의 기능을 먼저 이해를 해야 한다.

유닉스에서는 signal.h 헤더파일에 정의된 시그널 이름들이 있는데 그중에 SIGINT, SIGQUIT이 있다


#define SIGINT :
인터럽트를 위한 시그널로 유저가 인터럽트를 발생시키는 키를 입력했을 때 그와연결된 프로세스에세 커널이 보내는 시그널이다. 이 시그널은 프로세스를 종요할 때 많이 사용되는 시그널이다.이것은 터미널에서 ctrl+c와 같은 역활을 한다.

#define SIGQUIT : Quit
를 위한 시그널로 유저가 터미널에서 Quit키를 치면 커널이 프로세스에게 SIGQUIT 시그널을 보낸다


그러면 C언어상에서 이들 시그널을 어떻게 이용을하는가 하는것에 대한 궁금증이 생길텐데 한번 예제코드를 이용해서 살펴보도록하자.

signal()
이라는 함수가 있는데 이 함수는 이름그대로 시그널을 처리하는 함수 이다. signal 함수는 다음과 같이 나타낼 수 있다.

 int sigkind;
int function();
signal(sigKind, function);


여기서 sigKind에 바로 SIGINT, SIGQUIT같은 것이 들어가는것이다.
그리고 function sigKind의 함수가 호출 되었을때 실행되는 함수 이다
.

이제 이 signal함수를 이용해서 간단한 코드를 작성해 보자

 

 #include<stdio.h>
#include<signal.h>

int handler()
{

//필요한 작업을 처리한후에 프로그램을 종요한다.
printf("\n\nSIGINT
핸들러 호출
\n");
printf("\n<<
작업 종료 시작
>>>\n");
sleep(1);
printf("\n\n
실행되는 모든 프로세스 종료
\n\n");
printf("All open file closed\n\n\n");
exit(1);

}

int main()
{

int result, step =0;
//signal
함수를 사용하여 SIGINT 핸들러와 SIGQUIT핸들러를 등록한다.
printf("signal
함수를 사용하여 SIGINT 핸들러를 SIGQUIT핸들러를 등록한다
.\n\n");
result = signal(SIGINT, handler);
result = signal(SIGQUIT, handler);

//
무한루프 프로그램을 돌린다
.
printf("
메인 프로세스 실행
.\n");
while(1)
{

step++;
printf("%d
번째 작업 수행\n", step);
sleep(1);

}
return 1;

}




위의 코드를 실행하면 무한 루프대로 작업수행 메시지가 뜰것이고 시그널이 들어오면 핸들러 함수가 동작 하고 프로그램이 종료가 될 것이다. 실행을 시켜보자. 프로그램 실행도중 ctrl + c 또는 ctrl + \ 키를 눌러보자.

 ktss1023@ktss1023-desktop:~/바탕화면$ ./a.out
signal
함수를 사용하여 SIGINT 핸들러를 SIGQUIT핸들러를 등록한다
.
 
메인 프로세스 실행
.
1
번째 작업 수행

2
번째 작업 수행
3
번째 작업 수행


SIGINT
핸들러 호출

<<
작업 종료 시작>>>


실행되는 모든 프로세스 종료


All open file closed



다음과 같이 잘 실행이 될 것이다.

그런데 이것으로는 과제의 내용에 합당하는 문제를 풀수가 없다. 그러면 어떤 시그널이 들어올때 그시그널을 미리 block을 시킬수 있는 함수가 있다면 좋을 것이다. 바로 그것이 문제를 해결 하기위한 함수이다. 어떤함수가 있는지 한번 살펴 보자.

int sigemptyset(sigset_t * set)

set 이 가리키고 있는 시그널 집합을 초기화 한다.

성공시에 0 return 하고, 실패시에 -1 return 한다.

int sigaddset(sigset_t * set, int signum)

set 이 가리키고 있는 시그널 집합에 signum을 추가한다.

성공시에 0 return 하고, 실패시에 -1 return 한다.

int sigdelset(sigset_t * set, int signum)

set 이 가리키고 있는 시그널 집합에 signum을 삭제한다.

성공시에 0 return 하고, 실패시에 -1 return 한다.

int sigprocmask(int how, const sigset_t * set, sigset_t * oldset)

시그널 마스크를 검사하고 변경하기 위해서 사용된다. 간단히 말해서 해당 시그널에 대해서

BLOCK, UNBLOCK 를 하기 위해서 사용한다.

 

how option

SIG_BLOCK

새로운 시그널 마스크는 현재의 시그널 마스크와 set에 의해 지정된 시그널 마스크의 합집합이다.

, set는 블록 시키고자 하는 추가적인 시그널들을 포함한다.

SIG_UNBLOCK

새로운 시그널 마스크는 현재의 시그널 마스크와 set로 지정된 시그널 마스크의 보수의 교집합이다.

, set는 블럭에서 해제시킬 시그널들을 포함한다.

SIG_SETMASK

새로운 시그널 마스크는 set로 지정된 시그널 마스크이다.

시그널 마스크를 변경하였다가 이전 시그널 마스크로 복귀시키고자 할 때, 원래의 시그널 마스크를

저장하였다가 SIG_SETMASK 옵션을 사용해야 한다

sigprocmask 는 성공시에 0 return 하고, 실패시에 -1 return 한다.


바로 이런 함수이다. sigset_t * set 집합을 하나 선언하고 집합에 SIGINT, SIGQUIT을 추가 한뒤int sigprocmask(int how, const sigset_t * set, sigset_t * oldset) 함수를 이용해서 SIG_BLOCK을 해주면 된다.

문제를 풀기 전에 이 함수를 이용해서 아까의 프로그램을 약간 변형하는 형태의 프로그램을 작성해보자.

 

 #include<stdio.h>
#include<signal.h>

int main(void)
{
int result, step =0;
sigset_t* set, oldset, pendset;//BLOCK
signal set의 변수를 선언,

//
셋을 초기화 시킨다
.
if(sigemptyset(&set)<0)
{
printf("sigemptyset error\n");
exit(1);
}

//
블록할 시그널 등록

if(sigaddset(&set, SIGINT)<0)
{
printf("sigaddset error\n");
exit(1);
}

//
블록할 시그널 등록
if(sigaddset(&set, SIGQUIT)<0)
{
printf("sigaddset error\n");
exit(1);

}

//
시그널 블록을 해준다.
printf("
시그널 블록 시작
\n\n");
printf("5
초만기다려봐
\n");
if(sigprocmask(SIG_BLOCK, &set, &oldset)<0)
{
printf("
블록 에러
");
exit(1);
}



sleep(5);

if(sigpending(&pendset)<0)
{
printf("sigpending error");
exit(1);
}

if(sigismember(&pendset, SIGINT)||sigismember(&pendset, SIGQUIT))
{
printf("
작업이 진행중이다
.\n");
}


//
무한루프 프로그램을 돌린다
.
printf("
메인 프로세스 실행
.\n");
while(1)
{
step++;
printf("%d
번째 작업 수행
\n", step);
sleep(1);
if(step==20)break;

}


if(sigprocmask(SIG_SETMASK, &oldset, NULL) <0)
{
printf("unblock error");
exit(1);
}
printf("SIGQUIT,SIGINT
가 언블록되었다
\n\n");


return;
}



위의 프로그램은 처음 5초간 기다리면서 신호를 받아 그신호가 SIGINT, SIGQUIT이면 메시지를 출력할려고 그랬는데 이걸지속적으로 할려면 타이머나 스레드를 돌려서 해야 될 듯 하다. 그리고 sigprocmask 함수를 이용해서 SIGINT,SIGQUIT의 신호를 막았다. 그러므로 실행하면 작업이 끝나기전에는 신호가 블록이 되어 동작하지 않는다.
자 그렇다면 이제 위를 토대로 문제를 해결하면 된다. 작업에 파일을 복사하는 작업을 하면 되는것이다. 처음에 폴더내에있는 파일명들을 출력해주고 파일명을 입력 받고 복사해주면 되는 형식으로 작성을 해보자.

#include<stdio.h>
#include<signal.h>
#include<fcntl.h>

#define LENGTH 256

static void sig_int(int);

int main()
{
char filename[LENGTH];//
파일이름은 100자를 초과할 수 없다.

char c_filename[LENGTH];//
복사할 파일 이름


char buf[LENGTH];//
버퍼


int result= 0;
int readCnt, writeCnt, orgFile, newFile;

sigset_t* set,oldset;//BLOCK
signal set의 변수를 선언,    

//
복사할 파일 오픈

printf("
복사할 파일 이름을 입력하시오 : ");
scanf("%s",filename);
orgFile = open(filename, O_RDONLY);

if(orgFile<0)
{
    printf("
없는 파일입니다
.\n");
    return -1;
}

//
복사 당할 파일 생성

printf("
생성할 파일 이름을 입력하시오 : ");
scanf("%s",c_filename);
newFile = open(c_filename, O_WRONLY|O_CREAT|O_APPEND);

if (signal(SIGINT, sig_int) == SIG_ERR)
{
printf("signal error\n");
return -1;
}
if (signal(SIGQUIT, sig_int) == SIG_ERR)
{
printf("signal error\n");
return -1;
}

//
셋을 초기화 시킨다
.
if(sigemptyset(&set)<0)
{
printf("sigemptyset error\n");
return -1;
}

//
블록할 시그널 등록

if(sigaddset(&set, SIGINT)<0)
{
printf("sigaddset error\n");
return -1;
}

//
블록할 시그널 등록
if(sigaddset(&set, SIGQUIT)<0)
{
printf("sigaddset error\n");
return -1;

}

//
시그널 블록을 해준다.



/*
sleep(5);

if(sigpending(&pendset)<0)
{
printf("sigpending error");
exit(1);
}

if(sigismember(&pendset, SIGINT)||sigismember(&pendset, SIGQUIT))
{
printf("
작업이 진행중이다
.\n");
}
*/
printf("
시그널 블록 시작
\n\n");
if(sigprocmask(SIG_BLOCK, &set, &oldset)<0)
{
printf("
블록 에러
");
return -1;
}


//
프로그램을 돌린다
.
printf("
복사 시작
!\n");

for(readCnt=1;readCnt>0;)
{

    readCnt = read(orgFile, buf, LENGTH);
    writeCnt = write(newFile, buf, LENGTH);
    
    /*
    step++;
    if(step%2==0)
    {
        printf("1
초만쉬어요
.\n");
        sleep(1);
    }
*/
    printf("%d, %d\n", readCnt, writeCnt);


}

printf("SIGQUIT,SIGINT
가 언블록되었다
\n\n");
}

printf("
복사끝
\n");

close(orgFile);
close(newFile);

if(sigprocmask(SIG_SETMASK, &oldset, NULL) <0)
{
printf("unblock error");
return -1;
}

static void sig_int(int signo)
{
    printf("
신호확인
\n");
    return;
}

하지만 이코드로는 왠일인지 파일 복사가 진행이 되지 않는다. 대체 무슨 연유일까? 이것만으로는 해결하기가 어렵다는 결론으로 sigaction이라는 함수를 찾아 보았다.

sigaction()

sigaction은 시그널을 취급할 방법을 선택 할 수 있다.

헤더파일

#include <signal.h>

원형

int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);

인자

첫번째 : 행동을 지정할 개개의 시그널

두번째 : 지정하고 싶은 행동

세번째 : 나중에 복구를 위해 현재 값을 저장한다.

다음과 같이 시그널의을 취급하는 방법을 선택하는 함수이라고 하는데.
일단, sigaction 구조체에 대해서도 알아봐야 할 것이다.

struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}
sa_handler

signum번호를 가지는 시그널이 발생했을 때 실행된 함수를 설치한다. 함수외에도 SIG_DFL SIG_IGN을 지정할 수 있다. 전자는 시그널에 대한 기본행동을 후자는 시그널을 무시하기 위해서 사용한다.

sa_mask 

sa_handler에 등록된 시그널 핸들러 함수가 실행되는 동안 블럭되어야 하는 시그널의 마스크를 제공한다. SA_NOMASK가 적용되어 있지 않다면

sa_flags 

시그널 처리 프로세스의 행위를 수정하는 일련의 플래그들을 명시한다. 다음중 하나 이상의 것들에 의해서 만들어 진다.

SA_NOCLDSTOP 

만약 signum SIGCHLD라면, 자식 프로세스가 SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU등을 받아서 중단되었을 때 이를 통지 받을 수 없게 된다.

SA_ONESHOT, SA_RESETHAND

일단 시그널 처리기가 호출되면, 기본 상태에 대한 시그널 액션을 재 저장한다. 이는 signal(2)호출에 대한 기본 행위이다.

SA_RESTART 

일부 시스템 호출들이 시그널을 통해 재시작할 수 있도록 함으로서 BSD 시그널과 호환되도록 한다.

SA_NOMASK, SA_NODEFER

시그널이 자체 시그널 처리기로부터 수신 받지 않도록 한다.

SA_SIGINFO 

시그널 처리기가 하나가 아닌 3개의 인자를 취할경우 sa_handler대신 sigaction siginfo_t를 이용할 수 있다. siginto_t는 다음과 같이 정의된 구조체이다.

[

 

다음과 같은 구조체를 지니는데 각구조체의 역확은 예제 코드에서 알아보자.

-------------------------------------------------------

---------------------test.c----------------------------

-------------------------------------------------------

//sigaction() 사용하기
//
시그널을 취급할 방법을 선택할 수 있다.
#include <stdio.h>
#include <signal.h>
//sigaction()

int main(void)
{
 static struct sigaction act;
//행동을 지정할 구조체

 void catchint(int); //행동할 함수(함수를 바꿈으로 인터럽트시 행동을 조절 할 수 있다.)

 act.sa_handler = catchint; //구조체 변수에 취해질 행동을 지정한다.
 
 sigfillset(&(act.sa_mask));
//시그널을 포함하도록 집합을 완전 채운다.(?)
 
 //SIGINT : 인터럽트를 프로세스에게 보낸다.
 //&act
지정된 행동
 //
현재값은 0으로(나중에 복구하기 위해 지정할 수 있다.)
 sigaction(SIGINT, &act, NULL);
 
 //프로세스 실행
 printf("sleep call #1\n"); sleep(5);
 printf("sleep call #2\n"); sleep(5);
 printf("sleep call #3\n"); sleep(5);
 printf("sleep call #4\n"); sleep(5);

 printf("Exlting\n");
 exit(0);
}

//실행 함수
//
인터럽트를 칠때 커널에 의해 프로세스에게 보내진다.
void catchint(int signo)
{

 printf("\nCATCHINT : signo = %d\n", signo);
 printf("CATCHINT : returning\n\n");
}

==================================================================


다른 블로그의 글을 퍼온 코드인데 이해 하기가 쉬울것이다. 그러니까 sigaction signal 함수를 좀더 발전? 시킨것이다. 이를 토대로 코드를 한번 작성해보자.

#include<stdio.h>
#include<signal.h>
#include<fcntl.h>
#include<stdlib.h>

#define LENGTH 256

void sigcatch(int); //
행동할 함수(함수를 바꿈으로 인터럽트시 행동을 조절 할 수 있다.)

int main(void)
{
    char filename[LENGTH];//
파일이름은 100자를 초과할 수 없다
.

    char c_filename[LENGTH];//
복사할 파일 이름


    char buf[LENGTH];//
버퍼

    int result= 0;
    int readCnt, writeCnt, orgFile, newFile;

    static struct sigaction act; //
행동을 지정할 구조체
    
    act.sa_handler = sigcatch; //
구조체 변수에 취해질 해동을 지정한다.
 
    sigfillset(&(act.sa_mask)); //sigaction
구조체의 sigset을 모든 신호를 다 채운다
.
    //
이는 sigaddset함수를 이용해 SIGINT SIGQUIT만 선택해도 된다
.
    act.sa_flags = 0;
   
    //SIGINT :
인터럽트를 프로세스에게 보낸다
.
    //&act
지정된 행동

    //
현재값은 0으로(나중에 복구하기 위해 지정할 수 있다.)
    if(sigaction(SIGINT, &act, NULL)==-1)
    {
        perror("sigaction error\n");
    }
    if(sigaction(SIGQUIT, &act, NULL)==-1)
    {
        perror("sigaction error\n");
    }
    //


    //
복사할 파일 오픈

    printf("
복사할 파일 이름을 입력하시오 : ");
    scanf("%s",filename);
    orgFile = open(filename, O_RDONLY);
    
    if(orgFile<0)
    {
        printf("
없는 파일입니다
.\n");
        return -1;
    }
    
    //
복사 당할 파일 생성

    printf("
생성할 파일 이름을 입력하시오 : ");
    scanf("%s",c_filename);
    newFile = open(c_filename, O_WRONLY|O_CREAT|O_APPEND);


    //
프로그램을 돌린다
.
    printf("
복사 시작
!\n");

    for(readCnt=1;readCnt>0;)
    {
        readCnt = read(orgFile, buf, LENGTH);
        writeCnt = write(newFile, buf, LENGTH);
    
           /*
             step++;
            if(step%2==0)
            {
                printf("1
초만쉬어요
.\n");
                sleep(1);
            }
        */
        printf("%d, %d\n", readCnt, writeCnt);

    
    }

    printf("
복사끝
\n");

    close(orgFile);
    close(newFile);
}

//
실행 함수

//
인터럽트를 칠때 커널에 의해 프로세스에게 보내진다.
void sigcatch(int signo)
{

 printf("\nsigno = %d
신호가 들어왔습니다
.\n", signo);
 printf("
파일 복사가 진행중입니다.\n\n");

}

단순히 텍스트 파일 복사를 시험하였고 복사가 빨리 진행되므로 255바이트 마다 1초간 sleep함수를 실행했다.
결과는




(2) alram() SIGALRAM을 사용하여 timeout을 가지고 file read하는 함수를 작성하시오.

일단 alarm() SIGALRM에 대해서 알아 보아야 할것이다. 우선 alarm함수는 시그널을 전달하기 위해서 사용하는 함수로 전달되는 시그널이 SIGALRM이다.

alarm seconds 초 후에 프로세스에 SIGALRM 을 전달한다. 만약 seconds 0이라면 결코SIGALRM 이 전달되지 않을것이다. 만약 alarm 이 여러개 쓰인다면 기존에 설정되었던 alarm 설정값은 취소되고가정최근의 alarm 설정값으로 지정된다.

그러므로 alarm 을 사용할때는 alarm 이 겹치지 않도록 주의해야 한다.

SIGALRM 의 기본 행동은 프로세스 종료이다.

이렇게 나와 있는 예제프로그램을 한번 보자.

#include <unistd.h>
#include <signal.h>

void myalarm()
{
   
printf("ding dong dang\n");
}

int main()
{
   
printf("alarm setting\n");
    // SIGALRM
이 발생하면 myalarm() 함수를 실행한다.
   
signal(SIGALRM, myalarm);
    //
알람을 1초로 설정한다.
   
alarm(1);
  

 while(1)
    {
       
printf("ok\n");
        //
신호를 기다린다.
       
pause();
        // alarm
2초로 설정한다.
       
alarm(2);
    }
}

다음과 같이 signal 함수를 이용해서 SIGALRM이 발생할 경우 myalarm함수가 발생하도록 했다.
위코드를 토대로 타임아웃을 주는 파일을 리드하는 프로그램을 작성하자. 읽은 파일은 화면에 출력하는것으로 한다.

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

void myalarm()
{
    printf("\n
파일리드를 종료합니다.\n");
    exit(0);
}

int main()
{
    FILE *fp;//
리드할 파일포인터

    char f_name[200];//
파일이름
    char buf;//1
바이트짜리 버퍼
    int limit;//alarm
의 수를 결정

    printf("
읽을 파일이름을 입력하세요 : ");
    scanf("%s",f_name);
    
    if(!(fp = fopen(f_name,"r")))
    {    
    printf("
파일이 잘못되었습니다
.\n");
    return -1;
    }    

    printf("
몇초후 타임아웃하실건가요
: ");
    scanf("%d",&limit);

    signal(SIGALRM, myalarm);
    
    alarm(limit);
    printf("alarm setting\n");
    // SIGALRM
이 발생하면 myalarm() 함수를 실행한다
.
   

    while(!feof(fp))//
파일은 1바이트씩 읽고 출력한다
.
    {
        printf("%c",fgetc(fp));
        sleep(1);
    }
    return 0;    
}

위 프로그램은 미리 alram을 설정하고 그동안 1바이트씩을 읽고 출력한뒤 sleep(1)을해주는 프로그램으로 20초동안만 파일을읽고 출력이 된다.

 

 

 

'CSE(컴퓨터 공학) > 유닉스 시스템' 카테고리의 다른 글

유닉스 과제 #4  (0) 2009.05.30
유닉스 과제2  (0) 2009.04.08
Posted by 태씽