signal함수들을 이용해서 server와 client가 통신하는 시스템을 만들어보자
= software interrupt. 즉, 프로그램이 예상하지 못한 외부의 event를 다루기 위한 매커니즘.
(interrupt = unexpected, external event, Asynchronous event.)
interrupt handling은 process가 동작 중일 때 (1)interrupt가 발생하면 (2)kernel이 process를 중단시키고 (3)적절하게 interrupt를 처리하는 것을 말한다.
이때 처리는 무시할 건지 service를 제공할 건지를 결정한다.
typedef void (*sighandler_t)(int)
sighandler_t signal (int signum, sighandler_t handler)
sigaction함수의 인자로 들어가는 sigaction구조체를 살펴보자.
sa_sigaction 핸들러는 세 개의 인자를 갖는다.
첫번째는 signal's symbolic constant.
-> SIGUSR1, SIGUSR2를 사용.
두번째는 siginfo_t structure.
siginfo_t {
int si_signo; /* 시그널 넘버 */
int si_errno; /* errno 값 */
int si_code; /* 시그널 코드 */
pid_t si_pid; // signal을 보낸 pid
uid_t si_uid; /* 프로세스를 전송하는 실제 사용자 ID */
int si_status; /* Exit 값 또는 시그널 */
clock_t si_utime; /* 소모된 사용자 시간 */
clock_t si_stime; /* 소모된 시스템 시간 */
sigval_t si_value; /* 시그널 값 */
int si_int; /* POSIX.1b 시그널 */
void * si_ptr; /* POSIX.1b 시그널 */
void * si_addr; /* 실패를 초래한 메모리 위치 */
int si_band; /* 밴드 이벤트 */
int si_fd; /* 파일 기술자 */
}
세 번째는
void *context
이것은 이제 signal발생 시 문맥 정보가 포함된 ucontext_t 를 가리키는 역할을 한다고 한다.
sa_flags
sa_sigaction을 사용하기 위해선 sa_flags = SA_SIFINFO를 넣어야 handling process의 동작 변경이 된다.
sa_mask
블록시킬 signal을 지정하는 변수이다. 다른 일을 우선적으로 처리하기 위해 해당 시그널을 처리하지 않고 막겠다는 의미!
restcit를 사용하면 그 포인터가 가리키는 객체를 다른 포인터가 가리키지 못하게 한다.
sig : signal's symbolic constant
oact : 이전의 사용했던 sigaction구조체를 가리키는 변수. 우리는 사용하지 않을 것이다.
mandatory 구현
1. kill함수에서의 딜레이.
# include "minitalk.h"
void bin_to_send(int pid, int bit)
{
if (bit == 0)
kill(pid, SIGUSR1);
else if (bit == 1)
kill(pid, SIGUSR2);
usleep(100);
}
void ten_to_bin(int pid, int n, int cnt)
{
if (n > 0)
{
ten_to_bin(pid, n / 2, cnt + 1);
bin_to_send(pid, n % 2);
}
else if (n == 0)
{
while (cnt < 8)
{
bin_to_send(pid, 0);
cnt++;
}
}
}
void str_to_ten(int pid, char *string)
{
int i;
int data;
int bit_sh;
i = 0;
while (string[i])
{
data = (int)string[i];
ten_to_bin(pid, data, 0);
i++;
}
ten_to_bin(pid, 10, 0);
ten_to_bin(pid, 0, 0);
}
int main(int ac, char **av)
{
if (ac != 3)
{
printf("usage : ./a.out [server's pid] \"[string]\"\n");
exit(1);
}
else
{
str_to_ten(atoi(av[1]), av[2]);
}
}
void bin_to_char(int sig, siginfo_t *info, void *context)
{
static char temp;
static int bit;
if (sig == SIGUSR1 && bit < 8)
temp <<= 1;
else if (sig == SIGUSR2 && bit < 8)
{
temp <<= 1;
temp |= 1;
}
bit++;
if (bit == 8)
{
write(1, &temp, 1);
bit = 0;
temp = 0;
}
}
size_t ft_strlen(char *str)
{
size_t i;
i = 0;
while (str[i])
i++;
return (i);
}
void print_msg(char *msg)
{
write(1, msg, ft_strlen(msg));
exit(1);
}
int main(int ac, char *av[])
{
struct sigaction test;
(void)av;
if (ac != 1)
print_msg("format error is occured\n");
printf("server's pid는 %d\n", getpid());
test.sa_flags = SA_SIGINFO;
test.sa_sigaction = bin_to_char;
sigemptyset(&test.sa_mask);
if (sigaction(SIGUSR1, &test, 0) == -1)
print_msg("error is occured in receiving SIGUSR1\n");
if (sigaction(SIGUSR2, &test, 0) == -1)
print_msg("error is occured in receiving SIGUSR2\n");
while (1)
pause();
}
bonus
mandatory에서 재귀로 2진법을 만들어서 비트를 보내주었는데 유니코드같은 경우 값이 너무 크기 때문에 오버플로우 등등의 문제가 있어서 정상적으로 동작하지 않았다 따라서 재귀로 보내는 것이 아닌 그냥 비트연산을 통해 서버측으로 보내게 되었다.
유니코드에 관한 코드를 따로 짜야하나 했는데 신기하게도 쉘 터미널이 알아서 처리를 해주었다.
# include "minitalk.h"
void handler(int sig)
{
if (sig == SIGUSR1)
printf("signal received well\n");
exit(0);
}
void bin_to_send(int pid, int bit)
{
if (bit == 0)
kill(pid, SIGUSR1);
else if (bit == 1)
kill(pid, SIGUSR2);
usleep(100);
}
void ten_to_bin(int pid, int n, int cnt)
{
if (n > 0)
{
ten_to_bin(pid, n / 2, cnt + 1);
bin_to_send(pid, n % 2);
}
else if (n == 0)
{
while (cnt < 8)
{
bin_to_send(pid, 0);
cnt++;
}
}
}
void str_to_ten(int pid, char *string)
{
int i;
int data;
int bit_sh;
i = 0;
while (string[i])
{
data = (int)string[i];
ten_to_bin(pid, data, 0);
i++;
}
ten_to_bin(pid, 10, 0);
ten_to_bin(pid, 0, 0);
}
int main(int ac, char **av)
{
if (ac != 3)
{
printf("usage : ./a.out [server's pid] \"[string]\"\n");
exit(1);
}
else
{
signal(SIGUSR1, handler);
str_to_ten(atoi(av[1]), av[2]);
}
while (1)
pause();
}
void bin_to_char(int sig, siginfo_t *info, void *context)
{
static char temp;
static int bit;
if (sig == SIGUSR1 && bit < 8)
temp <<= 1;
else if (sig == SIGUSR2 && bit < 8)
{
temp <<= 1;
temp |= 1;
}
bit++;
if (bit == 8)
{
if (temp != 0)
write(1, &temp, 1);
else
{
printf("%d\n", info->si_pid);
kill(info->si_pid, SIGUSR1);
}
bit = 0;
temp = 0;
}
}
size_t ft_strlen(char *str)
{
size_t i;
i = 0;
while (str[i])
i++;
return (i);
}
void print_msg(char *msg)
{
write(1, msg, ft_strlen(msg));
exit(1);
}
int main(int ac, char *av[])
{
struct sigaction test;
(void)av;
if (ac != 1)
print_msg("format error is occured\n");
printf("server's pid는 %d\n", getpid());
test.sa_flags = SA_SIGINFO;
test.sa_sigaction = bin_to_char;
sigemptyset(&test.sa_mask);
if (sigaction(SIGUSR1, &test, 0) == -1)
print_msg("error is occured in receiving SIGUSR1\n");
if (sigaction(SIGUSR2, &test, 0) == -1)
print_msg("error is occured in receiving SIGUSR2\n");
while (1)
pause();
}