C언어로 간단하게 프로세스간 통신하는 프로그램을 만들어보자
Categories: C
#
C언어로 간단하게 프로세스간 통신하는 프로그램을 만들어보자
##
프로세스 간의 통신(IPC, inter-process communication)
-
프로세스는 필요한 경우 통신을 한다
IPC는 주고받는 데이터 크기가 매우 다양하며, 프로세스가 동일한 시스템에 있을 수도 잇고, 다른 시스템에 있을 수도 있다.
과제에서 요구하는 IPC는 동일한 시스템 내의 두 프로세스 간의 통신을 말한다
##
시그널
- 시그널(SIGNAL)은 보통 인터럽트(interrupt)의 종류 중 하나이며, 특정 이벤트가 발생했을 때, 프로세스에게 전달하는 신호이다. 우리가 Ctrl+C를 누를 경우 SIGINT가 반환되며 그 시그널로 프로그램을 종료할지 말지 결정할 수 있게 만들 수 있다.
##
시그널 핸들러(signal handler)
-
시그널 핸들러는 프로세스가 시그널을 받았을 때 호출되는 함수이다
프로세스가 시그널을 받으면 수행하던 작업을 멈추고 시그널 핸들러를 호출하고, 시그널 핸들러의 실행이 끝나면 멈추던 작업을 다시 시작하게 된다.
##
시그널과 관련된 시스템 콜
시스템 콜(system call) | 의미 |
---|---|
sigemptyset | 시그널 집합을 공집합으로 만든다 |
sigfillset | 시그널 집합을 모든 시그널이 포함된 전체 집합으로 만든다 |
sigaddset | 시그널 집합에 특정 시그널을 추가한다 |
sigdelset | 시그널 집합에서 특정 시그널을 제외시킨다 |
sigaction | 특정 시그널에 대한 프로세스의 행동을 설정한다 |
kill | 특정 프로세스에게 특정 시그널을 보낸다 |
raise | 자기 자신에게 특정 시그널을 보낸다 |
alarm | 설정된 시간이 경과한 후 SIGALRM 시그널을 받는다 |
pause | 시그널이 도착할 때까지 대기 상태가 된다 |
##
시그널관련 함수
-
signal 함수
- #include
- void (signal(int signo, void (handler)(int)))(int);
- 오류시 : -1(SIG_ERR)
-
주요 시그널
시그널 이름 설명 SIGHUP 터미널을 읽어버렸을때 발생한다 SIGABRT 프로그램의 비정상종료시 발생한다 SIGINT Ctl+C 나 DELETE 키를 입력했을때 발생한다 SIGIO 비동기적인 입출력이 발생했을때 SIGKILL 프로세스를 죽이기 위해서 SIGPIPE 단절된 파이프에 write 할 경우 발생 SIGEGV 잘못된 메모리 참조(주로 포인터를 잘못 썼을때) SIGSTOP 프로세스의 일시중단 (Ctrl + z) SIGSUSR1 사용자를 위해 정의된 시그널
- #include
-
kill()
- #include <sys/types.h>
- #include
- int kill(pid_t pid, int signo);
- 성공시 : 0
-
실패시 : -1
-
첫 번째 인수, 프로세스 ID 값에 따른 처리 분류
pid 의미 양의 정수 지정한 프로세스 ID에만 시그널을 전송 0 함수를 호출하는 포르세스와 같은 그룹에 있는 모든 프로세스에 시그널을 전송 -1 함수를 호출하는 프로세스가 전송할 수 있는 권한을 가진 모든 프로세스에 시그널을 전송 -1 이외의 음수 첫번째 인수 pid 의 절대값 프로세스 그룹에 속하는 모든 프로세스에 시그널을 전송
-
sigemptyset
- 시그널 집합의 모든 시그널을 삭제한다. 시그널 집합을 빈 집합으로 만든다
- 신호집합
- 여러 개의 신호를 하나의 신호집합으로 표현하는 자료형식이 존재한다
- sigset_t ss;
- #include
- int sigemptyset(sigset_t *set);
- 반환값
- 성공시 : 0
- 실패시 : -1
-
sigaction
- 시그널 처리를 다양하게 한다
-
sigaddset
- 시그널 집합에 시그널을 추가한다
- #include
- int sigaddset(sigset_t *set, int signo);
- 반환값
- 성공시 : 0
- 실패시 : -1
-
pause
- 시그널을 수신할 때까지 대기한다
- #include
- int pause(void);
- 반환값 : -1(errno가 EINTR로 설정된다)
-
sleep
- 지정한 시간 동안 대기 상태가 된다. 지정한 시간이 경과되었거나 시그널을 수신하면 대기에서 풀린다. 인수로 받는 시간 값은 초 단위이다
- #include
- unsigned int sleep(unsigned int seconds);
- 반환값 : 성공시 0, 남은 시간이 반환된다.
getpid()
- 해당프로세스의 아이디를 반환하는 함수
#
client.c
#include "minitalk.h"
static int ft_isdigit(char c)
{
if (c >= '0' && c <= '9')
return (1);
return (0);
}
static int ft_isspace(char c)
{
if ((c >= 9 && c <= 13) || c == ' ')
return (1);
return (0);
}
static int ft_atoi(const char *str)
{
size_t result;
int negative;
result = 0;
negative = 1;
while (*str != '\0' && (ft_isspace(*str)))
str++;
if (*str != '\0' && (*str == '-' || *str == '+'))
{
if (*str == '-')
negative = -1;
str++;
}
while (*str != '\0' && ft_isdigit(*str))
{
result = (result * 10) + (*str - '0');
str++;
}
return ((int)(result * negative));
}
void my_kill(int pid, char *str)
{
int bit_shift;
char tmp;
while (*str)
{
bit_shift = 8; /* 비트계산을 위해 8을 줌 */
tmp = *str; /* 메시지저장 예를 들면 string = "hello"라면 처음엔
tmp는 h를 저장 */
while (bit_shift--)
{
/* 현재의 문자를 보내기 위해서 비트를 시프트 연산을 하게 된다
이때 연산의 결과가 1이되면 SIGUSR1과 함께 값 1을 보냄
아니라면 SIGUSR2과 함께 0을 보냄 */
if (tmp >> bit_shift & 1)
/* 주어진 pid 번호에 kill함수로 SIGUSR로 0 또는 1 보내기 */
kill(pid, SIGUSR1);
else
kill(pid, SIGUSR2);
/* 시그널이 겹치지 않도록 시그널 사이에 50ms를 넣는다 */
usleep(50);
/* 이렇게 모든 문자에 조건들을 적용하고 서버에 문자열을 전송한다 */
}
str++;
}
bit_shift = 8;
/* 마지막 '\0'문자를 보내기 위한 코드이다 0000 0000 = '\0'*/
while (bit_shift--)
{
kill(pid, SIGUSR2);
usleep(50);
}
}
/* 메인에서는 3개의 인자를 받아야 실행하게 만들어야 한다
1. ./client
2. server의 pid 번호
3. 보낼 메시지
이렇게 3개의 인자를 받아서 실행해야 하는 프로그램이기 때문에 예외처리를
따로 했다
그리고 입력한 pid는 int형으로 보내야하기 때문에 atoi를 써서 int변환
하고 그 pid로 시그널을 보내기 위함이다
"\n"은 맨 끝에 있다면 ASCII 값을 찾을 수 없기 때문에 직접 보낸다 */
int main(int argc, char *argv[])
{
if (argc != 3)
{
ft_printf("\n");
return (0);
}
my_kill(ft_atoi(argv[1]), argv[2]);
my_kill(ft_atoi(argv[1]), "\n");
}
#
server.c
#include "minitalk.h"
void sig_function(int sig)
{
/* 여기서 static을 사용하는 이유는 새로운 데이터가 계속 들어오기 때문이다 */
static char tmp;
static int bit_shift;
if (sig == SIGUSR1)
/* 들어오는 데이터가 1일 경우에는 tmp 비트에 0을 준다
이유는 나중에 저장되어 있는 데이터를 초기화함에 있어서 좋은 역할을 한다 */
tmp = tmp | 1;
bit_shift++;
if (bit_shift == 8)
{
/* bit_shift가 8이 되면 이제 tmp에 저장되어 있는
값을 출력한다.*/
bit_shift = 0;
ft_printf("%c", tmp);
tmp = 0;
}
/* 받은 값이 1이 아니기 때문에 1비트를 옮기고,
옮긴 값은 기본적으로 0으로 설정이 된다 */
else
tmp <<= 1;
}
int main(void)
{
ft_printf("pid = %d\n", getpid());
/* 시그널 기능은 프로그램이 끝날때까지 계속 듣고 있게 된다 */
while (1)
{
signal(SIGUSR1, sig_function);
signal(SIGUSR2, sig_function);
/* 프로그램이 종료되지 않도록 하는 함수 pause() 를 호출 */
pause();
}
return (0);
}