[Minitalk] Minitalk 과제 요구 사항 및 함수 정리

bolee·2022년 6월 22일
0

42seoul

목록 보기
18/27

Mandatory Part

  • 클라이언트와 서버의 형태로 통신 프로그램을 생성해야 한다.
  • 서버를 먼저 시작해야 하며, 시작된 후에는 해당 PID를 표시해야 한다.
  • 클라이언트는 다음을 매개변수로 사용한다.
    • 서버의 PID
    • 보내야 하는 문자열
  • 클라이언트는 매개변수로 전달된 문자열을 서버에 전달해야 한다. 문자열이 수신되면 서버는 문자열을 표시해야 한다.
  • 프로그램 간의 통신은 UNIX 신호를 통해서만 이루어져야 한다.
  • 서버는 문자열을 매우 빠르게 표시할 수 있어야 한다. 너무 길다고 생각하면 너무 길 수 있다는 뜻이다. (힌트: 100자당 1초는 어마어마하다.)
  • 서버는 다시 시작할 필요 없이 여러 클라이언트로부터 문자열을 연속적으로 수신할 수 있어야 한다.
  • SIGUSR1 및 SIGUSR2 신호만 사용할 수 있다.

이미 보류 중인 경우 Linux 시스템은 신호를 대기열에 넣지 않는다.
이 유형의 신호! 보너스 타임?

Bonus Part

  • 서버는 클라이언트에 신호를 보내 수신된 모든 신호를 확인합니다.
  • 유니코드 문자 지원!

주의 사항 및 허용 함수들

  • 실행 파일의 이름은 clientserver로 지정해야 한다.

  • 오류를 민감하게 처리해야 한다. 프로그램이 예기치 않게 종료되면 안된다. (세그먼테이션 오류, 버스 오류, 이중 자유 등)

  • 프로그램에 메모리 누수가 있으면 안된다.

  • 하나의 전역 변수를 사용할 수 있지만 이유가 합리적이어야 한다.

  • Manadatory part에서 다음 함수 및 기능들을 사용할 수 있다.

write

#include <unistd.h>

ssize_t write(int fildes, const void *buf, size_t nbyte);

파일에 데이터를 출력하는 함수

  • fildes: 데이터 전송 영역을 나타내는 파일 디스크립터
  • buf: 전송할 데이터를 가지고 있는 버퍼의 포인터
  • nbyte: 전송할 데이터의 바이트 수

반환값

  • 성공: 전달한 바이트 수
  • 실패: -1 반환 및 적당한 errno 값 설정

ft_printf and any equivalent YOU coded

ft_printf 과제에서 만든 ft_printf 함수 또는 그와 동일한 기능을 하는 자체 함수

signal

#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);

시그널(signal)을 받아 해당 함수를 실행시키는 함수

  • sig: 시그널 번호
  • func: 시그널을 처리할 핸들러 함수
    • 핸들러 함수 프로토타입: void handler(int signo);

반환값

  • 이전의 시그널 핸들러의 포인터

에러

  • SIG_ERR 리턴

Singal

  • Software interrupt로, process에 무엇인가 발생했음을 알리는 간단한 메시지를 비동기적으로 보내는 것
  • Signal을 받은 프로세스는 Signal에 따른 미리 지정된 기본 동작(default action)을 수행할 수도 있고, 사용자가 미리 정의해 놓은 함수에 의해서 무시하거나, 특별한 처리를 할 수 있다.

시그널은 <signal.h> 헤더 파일에 정의되어있고, 시그널에 따른 기본 처리는 프로세스를 종료하거나, 코어 덤프를 생성한다.

NoNameDefault ActionDescription
1SIGHUPterminate processterminal line hangup
2SIGINTterminate processinterrupt program
3SIGQUITcreate core imagequit program
4SIGILLcreate core imageillegal instruction
5SIGTRAPcreate core imagetrace trap
6SIGABRTcreate core imageabort program (formerly SIGIOT)
7SIGEMTcreate core imageemulate instruction executed
8SIGFPEcreate core imagefloating-point exception
9SIGKILLterminate processkill program
10SIGBUScreate core imagebus error
11SIGSEGVcreate core imagesegmentation violation
12SIGSYScreate core imagenon-existent system call invoked
13SIGPIPEterminate processwrite on a pipe with no reader
14SIGALRMterminate processreal-time timer expired
15SIGTERMterminate processsoftware termination signal
16SIGURGdiscard signalurgent condition present on socket
17SIGSTOPstop processstop (cannot be caught or ignored)
18SIGTSTPstop processstop signal generated from keyboard
19SIGCONTdiscard signalcontinue after stop
20SIGCHLDdiscard signalchild status has changed
21SIGTTINstop processbackground read attempted from control terminal
22SIGTTOUstop processbackground write attempted to control terminal
23SIGIOdiscard signalI/O is possible on a descriptor (see fcntl(2))
24SIGXCPUterminate processcpu time limit exceeded (see setrlimit(2))
25SIGXFSZterminate processfile size limit exceeded (see setrlimit(2))
26SIGVTALRMterminate processvirtual time alarm (see setitimer(2))
27SIGPROFterminate processprofiling timer alarm (see setitimer(2))
28SIGWINCHdiscard signalWindow size change
29SIGINFOdiscard signalstatus request from keyboard
30SIGUSR1terminate processUser defined signal 1
31SIGUSR2terminate processUser defined signal 2

sigemptyset, sigaddset

#include <signal.h>

int sigempty(sigset_t *set);
int sigaddset(sigset_t *set, int signo);

sigempty()

  • 시그널 집합 비우기
  • set: 비우려는 시그널 집합의 주소

반환값

  • 성공: 0
  • 실패: -1

sigaddset()

  • 시그널 집합에 시그널 설정 추가
  • set: 시그널을 추가하려는 시그널 집합의 주소
  • signo: 시그널 집합에 추가로 설정하려는 시그널

반환값

  • 성공: 0
  • 실패: -1

시그널 집합 (signal set)

시그널 집합은 시그널을 비트 마스크로 표현한다. 즉, 시그널 하나가 각 비트 하나를 가리킨다.
각 비트는 특정 시그널과 1:1로 연결되어 있다.

유닉스에서는 시그널 집합의 처리를 위해 sigset_t라는 구조체를 제공한다

#include <sys/signal.h>

typedef struct {
	unsigned int _sigbits[4];
} sigset_t;

sigaction

#include <signal.h>
     
// 성공: 0, 실패: -1
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);

signal() 함수 보다 향상된 기능을 제공하는 시그널 처리를 결정하는 함수

  • sig: 시그널 번호
  • act: 설정한 행동. 즉, 새롭게 지정할 처리 행동
  • oact: 이전 행동. 즉, 이 함수를 호출하기 전에 지정된 행동 정보

반환값

  • 성공: 0
  • 실패: -1

에러

  • EINVAL: 부적절한 시그널이 지정되거나, 무시할 수 없는 SIGKILL SIGSTOP에 대한 액션을 변경하고자 할 경우
  • EFAULT: act, oact, set, oset 이 타당하지 않은 메모리 영역을 가리킬 경우
  • EINTR: 시스템 호출이 인터럽트(interrupt) 되었다.

signal()에서는 처리할 행동 정보로 시그널이 발생하면 호출이 될 함수 포인터를 넘겨주었다.
그러나 sigaction()에서는 sigaction 구조체 값을 사용해 좀 더 다양한 지정이 가능하다.

#include <signal.h>

struct  sigaction {
	/* signal handler */
	union __sigaction_u __sigaction_u;  
    /* signal mask to apply */
    sigset_t sa_mask;
    /* see signal options below */
    int     sa_flags;
};

union __sigaction_u {
	void    (*__sa_handler)(int);
    void    (*__sa_sigaction)(int, siginfo_t *, void *);
};

#define sa_handler		__sigaction_u.__sa_handler
#define sa_sigaction    __sigaction_u.__sa_sigaction
  • __sigaction_u
    • 기본적으로 sa_handler로 사용되며, sig에 대한 동작을 나타내는 함수의 포인터로서 사용된다. 설정되지 않으면 기본동작을 의미하는 SIG_DFL이다.
    • sa_flagsSA_SIGINFO를 사용하면 sa_handler 대신에 sa_sigaction으로 설정할 수 있다. 신호 처리부(신호를 처리하는 함수)에 두가지 정보를 더 담아 보낸다. siginfo_t와 프로세스 문맥의 식별자가 그것이다.
      • 신호 처리 함수 프로토타입: void sig_handler(int sig, siginfo_t *sip, void *ucp);
  • sa_mask: 차단할 신호의 집합이다. sigprocmask를 통해 특정 신호를 BLOCK 시킬지, 말지를 정한다.
  • sa_flags: 시그널 처리 프로세스의 행위를 수정하는 일련의 플래그들을 명시한다. 다음 중 하나 이상의 것들에 의해 만들어진다.
    • SA_NOCLDSTOP: 만약 sigSIGCHLD라면, 자식 프로세스가 SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU등을 받아서 중단되었을 때 이를 통지 받을 수 없게 된다.
    • SA_NOCLDWAIT: sigSIGCHLD일 때, 자식 프로세스가 종료되었을 때 시스템이 좀비 프로세스를 만들지 않게 한다.
    • SA_ONSTACK: sigaltstack으로 대체 스택을 선언해두었다면 신호가 대안 스택의 프로세스에 전달된다.
    • SA_NODEFER: 신호가 잡혀서 신호 처리 함수가 실행되는 도중에 다시 같은 신호가 발생됐을때, 시스템이 자동으로 차단하지 않는다.
    • SA_RESETHAND: 신호 처리 함수에 진입할 때 이 신호의 처리 방식을 SIG_DFL로 재설정하고 SA_SIGINFO 플래그를 지운다.
    • SA_RESTART: 시그널 핸들러에 의해 중지된 시스템 호출을 자동적으로 재시작한다.
    • SA_SIGINFO: 시그널이 발생한 원인을 알 수 있다. SA_SIGINFO를 지정하면 sigaction 구조체에서 시그널 핸들러를 지정할 때 sa_handler 대신 sa_sigaction을 사용한다. SA_SIGINFO 플래그를 설정하면 시그널 핸들러는 다음과 같은 인자 세 개를 받는 형태로 정의된다.
      void handler(int sig, siginfo_t *sip, ucontext_t *ucp);
      • sig: 시그널 핸들러를 호출할 시그널
      • sip: 시그널이 발생한 원인을 담은 siginfo_t 구조체 포인터
      • 시그널이 전달될 때 시그널을 받는 프로세스의 내부 상태를 담은 ucontext_t 구조체 포인터
siginfo_t {
    int      si_signo;  /* 시그널 넘버 */
    int      si_errno;  /* errno 값 */
    int      si_code;   /* 시그널 코드 */
    pid_t    si_pid;    /* 프로세스 ID 보내기 */
    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;     /* 파일 기술자 */
}

kill

#include <signal.h>

int kill(pid_t pid, int sig);
  • pid: kill 대상 프로세스 ID
  • sig: 시그널 번호

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 값 설정

에러

  • EINVAL: 잘못된 시그널 번호를 지정했을 경우
  • ENOTSOCK: s가 소켓가 아닌 파일일 경우
  • ENOTCONN: 소켓이 연결되어 있지 않을 경우
  • ENOBUFS: 시스템에 연산을 위해 이용할 수 있는 자원 부족

kill() 함수는 쉘에서 프로세스를 죽이는 kill 명령과는 달리 프로세스에 시그널을 전송한다. 물론, 프로세스에 SIGKILL을 보내면 쉘 명령의 kill과 같은 역할을 할 수도 있다.

특정 프로세스 뿐만 아니라 같은 그룹 ID가 같은 모든 프로세스에게 동시에 시그널을 전송할 수 있으며, 권한 안에 있는 모든 프로세스에게도 시그널을 전송할 수 있다.

pid 값에 따라 다음과 같이 처리된다.

pid의미
양수지정한 프로세스 ID에만 시그널을 전송
0함수를 호출하는 프로세스와 같은 그룹에 있는 모든 프로세스에 시그널을 전송
-1함수를 호출하는 프로세스가 전송할 수 있는 권한을 가진 모든 프로세스에 시그널을 전송
-1 이외의 음수첫 번째 인수 pid 의 절대값 프로세스 그룹에 속하는 모든 프로세스에 시그널을 전송

getpid

#include <unistd.h>

pid_t getpid(void);

실행중인 프로세스 ID를 구해 반환하는 함수

42seoul 클러스터 맥에서 쉘 스크립트를 이용해 확인한 결과 숫자가 증가하며 pid가 배정된다.
최소 100 에서 최대 99998 사이에서 숫자가 배정되는 것을 확인하였다.

malloc, free

#include <stdlib.h>

void * malloc(size_t size);
void free(void *ptr);

malloc()

  • 원하는 만큼 동적으로 메모리를 할당하여 주소값을 반환하는 함수
  • 동적으로 메모리를 할당할때 Heap(힙)영역에 할당 한다.

반환값

  • 성공: 할당한 메모리의 첫번째 주소
  • 실패: NULL

free()

  • 동적으로 할당된 메모리를 해제한다.

반환값

  • 없음

pause

#include <unistd.h>

int pause(void);
  • 시그널을 수신할 때까지 대기 상태로 빠진다.
  • 어떤 시그널이 발생하기 전까지 대기 상태를 유지할 때 유용하다.

반환값

  • 항상 -1 반환

에러

  • EINTR: 시스템 호출이 인터럽트(interrupt) 되었다.

sleep, usleep

#include <unistd.h>

unsigned int sleep(unsigned int seconds);
int usleep(useconds_t microseconds);

sleep()

  • 지정한 시간(seconds)만큼 대기한다.

반환값

  • 성공: 0
  • 실패: 대기하지 않은 시간(즉, 대기하기 원하는 시간 - 실제 대기한 시간)

usleep()

  • 지정한 시간(microseconds)만큼 대기한다.

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 값 설정

exit

#include <stdlib.h>

void exit(int status);
  • 프로세스를 종료시키는 함수
  • 종료하기 전에 모든 열려진 파일들을 자동으로 닫는다.
  • 출력 버퍼 속에 데이터가 있으면 그것을 쓰기 완료시킨다.
  • 주로 에러가 났을 때 강제 종료시키기 위해 if문 속에서 사용된다.
  • 입력 인자로 전달하는 status는 운영체제에 전달하며 main함수의 return값과 같은 역할이다.
  • status 값은 에러 코드와 같으므로 정상 종료시 0을, 에러로 인한 종료시 0이 아닌 숫자, 대체로 1을 반환한다.

참고 자료
https://blockdmask.tistory.com/23
https://m.blog.naver.com/skssim/121268634
https://reakwon.tistory.com/215
https://www.joinc.co.kr/w/man/2/
https://badayak.com/category/%EC%BB%B4%ED%93%A8%ED%84%B0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D
https://blog.naver.com/PostView.nhn?isHttpsRedirect=true&blogId=mokdonjr&logNo=220813555163
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=skssim&logNo=121271980

0개의 댓글