Basic signal concept
- signal은 어떤 이벤트가 발생했을 때 알려주는 용도로 프로세스에게 전달되는 software notification이다
- signal의 lifetime은 해당 이벤트가 발생되었을 때 생성되며, 전달이 되어 수신을 하여 처리를 마치게 되면 해당 signal은 삭제가 된다.
- pending signal: 아직 타겟 프로젝트에게 시그널이 전달되지 않은 경우 ( 지금 안받을거야! )
- 바로 삭제 되는 것이 아니라, pending signal list에 유지된다.
- signal이 도착했을 때 수행해야 하는 task가 정의된 signal handler는 프로세스가 signal을 받았을 때 실행된다.
- 별도로 signal handler 역할을 수행하는 함수를 정의하지 않았다면 default signal handler가 실행된다.
- 프로그램에서 특정 시그널이 왔을 때 다른 task를 수행할 거야! 라는 함수를 정의할 수 있다는 것이다.
sigaction
function- 특정 signal이 도착했을 때 특정한 task를 수행하게 끔 개발자가 설정한 signal handler 를 등록하는 함수.
- 사용자가 정의한 signal handler function을 등록할 수도 있지만, constant 값을 설정할 수도 있다.
- SIG_DFL: default action
- SIG_IGN: ignore the signal
- Process signal mask
- block시키고 싶은 signal 번호들의 목록
- block이 되면, pending시키며 ( pending signal ), pending signal list에 대기하게 된다.
- ignore된 signal (SIG_IGN)는 대기하지 않고 바로 삭제된다.
sigprocmask
function ( 넣을 수도, 뺄 수도 )- 프로세스들은 기본적으로 signal이 발생했을 때 그 signal을 받지 않겠다는 객체인
signal mask
를 가지고 있다. - signal mask에 signal 번호를 등록하여 막도록 해줌.
Generating signals
- 모든 signal은 symbolic name ( SIG~~~ )을 가지고 있으며, signal 번호도 가지고 있다.
- defined in signal.h
- command line으로 시그널 생성
- kill command
- 시그널을 전송하는 명령어. 꼭 죽이는 것은 아님..
- 두개의 parameter를 넣을 수 있다.
- 어떤 시그널 / pid
- signal_name로 지정할 때에는 SIG를 빼고 전송한다.
- kill -s USR1 1234 (signal name: SIGUSR1 / -s: 이름을 쓰겠다)
- signal_number로 지정할 때에는 -{value} 형태로 사용한다.
- kill -9 1234
- -l 옵션으로 symbolic signal name들 리스트를 볼 수 있다.
kill
#include <signal.h> int kill(pid_t pid, int sig);
- pid parameter
- target process id이다.
- 만약 0이라면 호출한 프로세스와 같은 프로세스 그룹에 속한 모든 프로세스에게 이 시그널을 전달하겠다.
- 하나의 프로세스에만 시그널을 전달하는 것이 아니다.
- 만약 -1 이라면, 보낼 수 있는 권한이 있는 모든 프로세스에게 시그널을 전달하겠다.
- 또다른 음수라면, 절대값을 취한 값인 프로세스 그룹 아이디에 속한 모든 프로세스에게 시그널을 전달하겠다.
- ex) -10이라면, group id가 10인 프로세스 그룹에 속한 모든 프로세스들
- return value
- 성공했을 시 0 / 실패했을 시 -1
raise
#include <signal.h> int raise(int sig)
- 타겟 프로세스는 나 자신이다.
- return value
- 0일 경우 성공 / 실패했을 경우 0이 아님
alarm
#include <unistd.h> unsigned alarm(unsigned seconds)
- 파라미터로 받은 초가 지나게 되면, 이 함수를 호출한 프로세스에게 SIGALARM signal을 보낸다.
- 즉, 나 자신에게 보내는 signal 이다.
- 만약 seconds: 0이라면, 기존에 요청했던 alarm을 취소해라.
- SIGALARM signal을 받게되면, default action은 process를 종료한다.
- return value
- 타이머에 남아있는 초 값. ( 정상적으로 종료되었다면 0 )
- 에러를 따로 return 하지 않는다.
Signal sets
#include <signal.h> // signal set에 signal number를 추가하겠다. int sigaddset(sigset_t* set, int signo); // signal set에서 signal number를 빼겠다. int sigdelset(sigset_t* set, int signo); // signal set 내용을 비우겠다. int sigemptyset(sigset_t* set); // signal set에 모든 signal을 세팅한다. int sigfillset(sigset_t* set); // signal set에 signal number가 포함되어있냐 int sigismember(const sigset_t* set, int signo);
- Signal set
- 여러개의 signal을 담을 수 있는 data structure ( integer type )
- sigaddset, sigdelset을 통해 signal 번호를 넣거나 뺄 수 있다.
- 성공했을 시 0 / 실패했을 시 -1
- sigemptyset, sigfillset을 통해 initialize
- 성공했을 시 0, 실패했을 시 -1
- sigismember
- true 1 / false 0
Signal masks
#include <signal.h> int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
- signal mask는 특정 signal 들이 왔을 때, block하게 해줌
- sigset_t 타입의 signal masks를 수정하기 위해서 sigprocmask 함수를 사용한다.
- sigprocmask()
- parameter
- how
- 추가할지, 삭제할지
- SIG_BLOCK: 두번째 파라미터인 set을 signal mask에 추가하겠다.
- SIG_UNBLOCK: signal mask에서 두번째 파라미터인 set을 삭제하겠다. ( 있는 것들만 )
- SIG_SETMASK: signal mask를 두번째 파라미터인 set으로 설정해버리겠다. ( 기존값들 없어짐 )
- oset
- output parameter.
- 수정되기 이전의 signal mask 값을 세팅함.
- 잠깐 signal mask를 수정하여 중요한 일을 했다가, 끝나면 기존 signal mask로 설정하려고 쓰는 용도
- return: 성공했을 시 0 / 실패했을 시 -1
- single thread process에만 사용해야 한다.
- 어느 스레드로 갈지 애매해질 수 있기 때문이다.
- 스레드 레벨에서 signal mask를 조절하려면, pthread_sigmask()를 호출해야 한다.
- SIGSTOP, SIGKILL은 signal mask로 막을 수 없다.
Signal masks and sets example
- signal set에 SIGINT 추가
sigset_t newsigset; if((sigemptyset(&newsigset) == -1 || (sigaddset(&newsigset, SIGINT) == -1)) perror(“Failed to initialize the signal set”); else if( sigprocmask(SIG_BLOCK, &newsigset, NULL) == -1) perror(“Failed to block SIGINT”);
- signal set을 수정하고 나서 원복
sigset_t blockmask; sigset_t oldmask; … //add signals to blockmask if( sigprocmask(SIG_SETMASK, &blockmask, &oldmask) == -1) return -1; … if( sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) return -1; …
Catching and ignoring signals
#include <signal.h> int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact); struct sigaction{ // 함수 포인터. signal handler 함수. // return type void. parameter는 int형 하나 ( signal 번호 ) // SIG_DFL, SIG_IGN를 설정할 수도 있다. void (*sa_handler)(int); // signal handler가 호출될 동안, block시킬 signal이 있다면, 세팅 sigset_t sa_mask; // 추가적으로 설정하고 싶은 flag / 옵션 // 설정함에 따라 첫번째 / 네번째 인자 중 어떤 것을 실행할 지 정할 수 있다. // SA_SIGINFO가 없으면 첫번째 인자 / 있다면 4번째인자. int sa_flags; // signal handler를 등록할 수 있는 함수. // 첫 번째 인자보다 parameter가 더 많음. // realtime handler void (*sa_sigaction) (int, siginfo_t *, void *); }
- signal이 왔을 때 무엇을 할 것인가?
- parameter
- sig: 몇 번 시그널이 왔을 때 액션을 취할 것인가
- act: 어떤 액션을 취할 것인지와 설정 정보
- oact: 이전 액션 정보를 반환해주는 output parameter
- signal handler
- signal handler가 void return type / one integer parameter 이어야 한다.
- 그렇기 때문에 특정한 값을 넘겨줄 수 없다..
- POSIX Extension 버젼에서 sa_sigaction을 사용할 수 있게 하여 값을 넘길 수 있게 함
- sa_handler의 특별한 값
- SIG_DFL: default action을 수행해라
- SIG_IGN: 무시해라.
Catching/ignoring signals examples
- SIGINT가 도착했을 때 mysighand 함수를 signal handler로 세팅
struct sigaction newact; newact.sa_handler = mysighand; newact.sa_flags = 0; if(( sigemptyset(&newact.sa_mask) == -1) || ( sigaction(SIGINT, &newact, NULL) == -1)) perror(“Failed to install SIGINT signal handler”);
- 만약 SIGINT의 액션이 default action 이라면 SIGINT를 무시하고 싶은 경우
struct sigaction act; if(sigaction(SIGINT, NULL, &act) == -1) perror(“Failed to get old handler for SIGINT”); else if( act.sa_handler == SIG_DFL ){ act.sa_handler = SIG_IGN; if( sigaction(SIGINT, &act, NULL) == -1) perror(“Failed to ignore SIGINT”);
Catching/ignoring signals examples
- 반복문을 돌며 0과 1사이에 있는 sin(x)의 평균 값을 구하는 프로그램
- 사용자가 ctrl+c를 누르게 되면, signal handler를 등록하여 gracefully terminate 하게 됨. ⇒ flag 값을 설정하여 반복문을 나가게 하도록
#include <math.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> static volatile sig_atomic_t doneflag = 0; /* ARGSUSED */ static void setdoneflag(int signo){ doneflag = 1; } int main(void){ struct sigaction act; int count = 0; double sum = 0; double x; // signal handler 등록 act.sa_handler = setdoneflag; act.sa_flags = 0; // signal handler가 작동 중에는 별다른 signal을 막지 않는다. // sigaction 함수 호출 if((sigemptyset(&act.sa_mask) == -1) || (sigaction(SIGINT, &act, NULL) == -1)){ perror("Failed to set SIGINT handler"); return 1; } while(!doneflag){ x = (rand() + 0.5)/(RAND_MAX + 1.0); sum += sin(x); count++; printf("Count is %d and average is %f\n", count, sum/count); } printf("Program terminating ...\n"); if(count == 0) printf("No values calculated yet\n"); else printf("Count is %d and average is %f\n", count, sum/count); return 0; }
- 시그널이 도착하면, 실행이 jump하여 시그널 핸들러 함수가 실행된 뒤에 돌아오게 된다.
- doneflag은 critical section으로 처리해야 한다.
- 현재 단일 스레드 / 단일 프로세스 이지만, 반복문 접근하려는 중에 signal handler를 수행해버리면 뭐 문제가 될 수도 있다.. 뭐 크게 문제는 아니지만서도..
- 메인프로그램이 접근하는 영역과 signal handler가 동시에 access 할 수도 있는 부분은 critical section으로 만들어야 한다.
- 현재의 코드는 critical section을 만들었는데 어떻게 했는가?
- doneflag를 sig_atomic_t로 만들었다는 점.
- 해당 변수는 OS레벨에서 이 변수를 access를 하여 접근이 종료될 때 까지 다른 작업이 방해하는 것을 막아준다.
- volatile qualifier
- 변수의 값을 access할 때, c 컴파일러에게 비동기 작업으로 인해 값이 변경될 수도 있으니 항상 메모리에서 직접 참조하라는 말을 하는 것이다.
- 레지스터에 load된 값을 읽는 것이 아님.
Catching/ignoring signals examples
- 위의 예시와 비슷하게 작동한다
- 10000번째 iteration마다 버퍼에 중간 계산 결과를 저장한다.
- SIGUSR1 signal이 도착하면, signal handler가 버퍼의 내용을 화면에 출력해준다.
#include <errno.h> #include <limits.h> #include <math.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define BUFSIZE 100 static char buf[BUFSIZE]; static int buflen = 0; /* ARGSUSED */ static void handler(int signo){ int savederrno; savederrno = errno; // 버퍼에 쓰여진 값만큼 화면에 출력한다. // 왜 printf를 쓰지 않냐? => reentrant하게 작동하지 않기 때문에, write 함수를 사용한 것이다. // main에서도 printf를 쓰게 되면 꼬일 수 있어서. write(STDOUT_FILENO, buf, buflen); errno = savederrno; } // 메인 프로그램에서 10000번째 마다 결과를 버퍼에 쓰는 역할 static void results(int count, double sum){ double average; double calculated; double err; double errpercent; sigset_t oset; sigset_t sigset; if((sigemptyset(&sigset) == -1) || (sigaddset(&sigset, SIGUSR1) == -1) || (sigprocmask(SIG_BLOCK, &sigset, &oset) == -1)) perror("Failed to block signal in results"); if(count == 0) snprintf(buf, BUFSIZE, "No values calculated yet\n"); else{ calculated = 1.0 - cos(1.0); average = sum/count; err = average - calculated; errpercent = 100.0 * err / calculated; // 세번째 인자를 버퍼에 넣어주고 마지막에 NULL 추가 snprintf(buf, BUFSIZE, "Count = %d, sum = %f, average = %f, error = %f or %f%%\n", count, sum, average, err, errpercent); } buflen = strlen(buf); // 이전 상태로 되돌린다. 즉 USR1를 빼낸다 if(sigprocmask(SIG_SETMASK, &oset, NULL) == -1) perror("Failed to unblock signal in results"); } int main(void){ int count = 0; double sum = 0; double x; struct sigaction act; act.sa_handler = handler; act.sa_flags = 0; if((sigemptyset(&act.sa_mask) == -1) || (sigaction(SIGUSR1, &act, NULL) == -1)){ perror("Failed to set SIGUSR1 signal handler"); return 1; } fprintf(stderr, "Process %ld starting calculation\n", (long)getpid()); for(;;){ if((count % 10000) == 0) results(count, sum); x = (rand() + 0.5) / (RAND_MAX + 1.0); sum += sin(x); count++; if(count == INT_MAX) break; } results(count, sum); handler(0); return 0; }
- Critical section
- signal mask를 이용해서 block하게 된다. ( result 함수 내에 )
Waiting for signals
- 다음 작업을 시작하기 위한 시그널로서 특정 시그널을 기다린다.
- pause(), sigsuspend(), sigwait()
pause
#include <unistd.h> int pause(void);
- 시그널이 전달될 때까지 호출한 스레드가 suspend된다.
- user-defined handler가 호출될 수 있는 타겟 시그널이 오거나, 프로세스가 종료될 수 있는 시그널이 온다면 pause 함수가 return 된다.
- 뭐라도 시그널이 온다면 pause함수가 return 된다.
- 항상 -1을 return 한다.
- 시그널이 오면 해당 시그널 핸들러가 실행되고, 그 이후에 리턴되는 것이다.
- 내가 원하는 시그널이 왔다면 sigreceived를 1로 만든다.
static volatile sig_atomic_t sigreceived = 0; while(sigreceived == 0) pause();
- 만약 시그널이 while(sigreceived == 0)임을 확인하고 pause 함수를 호출하기 이전에 타겟 시그널이 왔다면?
- 타겟 시그널이 와도 씹히게 된다.
- pause 함수에 영향을 주지 못한다.
- 어떤 시그널이 추가로 와야 pause가 리턴 가능하다.
- 막았던 signal을 unblocking하는 것과 pause를 시작하는 것은 한번에 해야한다.
⇒ sigsuspend 함수가 이를 지원한다.
sigsuspend
#include <signal.h> int sigsuspend(const sigset_t* sigmask);
- 전달받은 sigmask로 signal mask를 set한다. 그리고 프로세스를 suspend하는 작업을 동시에 진행한다.
- signal이 도착할 때 까지 suspend
- sigmask는 어떤 시그널로 unblock할지를 확인하는 용도 이다.
- return 될 때, signal mask는 reset된다.
sigsuspend를 잘 사용한 예시
- 다른 시그널도 다 막는 상황
static volatile sig_atomic_t sigreceived = 0; // maskall => 모든 시그널 // maskmost => 타겟 시그널을 제외한 모든 시그널 // maskold => 이전 시그널 셋을 저장하기 위함 sigset_t maskall, maskmost, maskold; int signum = SIGUSR1; sigfillset(&maskall); sigfillset(&maskmost); sigdelset(&maskmost, signum); sigprocmask(SIG_SETMASK, &maskall, &maskold); if(sigreceived == 0) sigsuspend(&maskmost); sigprocmask(SIG_SETMASK, &maskold, NULL);
- 다른 시그널은 통과가 가능한 상황
static volatile sig_atmoic_t sigreceived = 0; // maskblocked => 막기 위해서 사용 // maskunblocked => sigsuspend 파라미터 sigset_t maskblocked, maskold, maskunblocked; int signum = SIGUSR1; sigprocmask(SIG_SETMASK, NULL, &maskblocked); sigprocmask(SIG_SETMASK, NULL, &maskunblocked); sigaddset(&maskblocked, signum); sigdelset(&maskunblocked, signum); // while문과 sigsuspend 사이를 막기 위해서 sigprocmask(SIG_BLOCK, &maskblocked, &maskold); while(sigreceived == 0) sigsuspend(&maskunblocked); // 기존의 상태는 maskblocked 상태였기 때문에 maskold를 세팅해주는 것이다. sigprocmask(SIG_SETMASK, &maskold, NULL);
sigwait
#include <signal.h> int sigwait(const sigset_t *restrict sigmask, int *restrict signo);
- 호출할 시 suspend가 된다.
- sigmask 중에 아무거나 하나 pending이 되면, 해당 시그널을 pending signal을 pending list에서 삭제하고 나서 return 한다.
- 삭제한 signal number를 signo에 설정해준다.
- 삭제된 signal의 signal handler는 호출되지 않는다.
- 성공했을 시 0 / 실패했을 시 -1
- sigsuspend와 다른 점
- sigsuspend에는 원하는 타겟 시그널을 뺀 signal set을 파라미터로 넣는다.
- sigwait은 파라미터에 원하는 signal number를 넣어야한다.
- 일단 sigmask 중 하나가 pending 상태가 되어야 한다.
- 직접 signal을 block하는 것이 아니다.
- signal mask를 건드리는 일은 없다.
#include <signal.h> #include <stdio.h> #include <unistd.h> int main(void){ int signalcount = 0; int signo; int signum = SIGUSR1; sigset_t sigset; if((sigemptyset(&sigset) == -1) || (sigaddset(&sigset, signum) == -1) || (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1 )) perror("Failed to block signals before sigwait"); fprintf(stderr, "This process has ID %ld\n", (long)getpid()); for(;;){ if(sigwait(&sigset, &signo) == -1){ perror("Failed to wait using sigwait"); return 1; } signalcount++; fprintf(stderr, "Number of signals so far: %d\n", signalcount); } }
Errors and Async-signal safety
- 시스템 콜 함수가 signal로 인해 interrupt가 되는 경우는 다시 시작을 해야 하는가?
- return 값이 -1이고, errno가 EINTR
- error를 explicitly하게 핸들링하여 다시 시작하도록 구현
- 내가 사용하는 함수가 interrupt될 수 있는 함수인지 확인해야한다.
- signal handler가 nonreentrant function을 호출하는 경우
- 함수의 동작이 중간에 멈추고 다시 실행하면 안되는 함수인 경우
- signal handler 함수 내에서 호출하는 함수는 reentrant한 함수를 호출해라.
- async-signal safe한 함수
- errno변수를 다룰 때
- main에서도 errno가 설정되고, signal handler 내에서도 errno가 설정되면 main의 errno가 없어질 수 있다.
- temp 변수에 기존 errno를 저장해두고, 함수가 완료 되면 다시 errno에 설정해두어라
Useful rules for signal handling
- 의심스러운 상황일때에는 ( 내가 호출했던 system call 함수가 signal에 의해 interrupt가 되었을 때에는 ), library 함수를 다시 시작을 하든지, restart library를 사용해라.
- signal handler에서 사용하는 library function을 확인하여 async-signal safe한 함수인지 확인해라
- signal handler와 main program code에서 동시에 access할 수 있는 변수, 코드 부분은 potential interactions를 확인해라
- errno를 적절하게 저장하고 원복해라
Program control
- 프로그램들은 때때로 signal을 이용해서 error handling이 가능하다
- 긴 계산작업을 실행하는데, 중간에 중단되는 것을 피하기 위해 ctrl-c는 처음 부분으로 돌아가서 restart 하는 것으로 실행 시퀀스를 변경하는 예시
- Indirect handling signals
- ctrl-c에 대해 응답은 flag 변수를 설정하여 값에 따라 어떤 구문을 실행할 지 프로그램을 짜라.
- 하지만 코드가 복잡해질 수 있다.
- Direct approach
- sigsetjmp, siglongjmp ( 특정 위치로 jump )
sigsetjmp and siglongjmp
#include <setjmp.h> void siglongjmp(sigjmp_buf env, int val); int sigsetjmp(sigjmp_buf env, int savemask);
- sigsetjmp()
- 호출을 하게 되면, 나중에 siglongjmp를 만나게 되면 sigsetjmp를 호출한 지점으로 jump하게 된다.
- parameters
- env: jump해서 왔을 때, 기존 함수를 실행했을 때 필요한 context들을 저장해놓는 변수
- savemask: 0이 아니라면 signal mask 상태 또한 같이 저장을 한다.
- return values
- 다음에 jump했을 때 돌아올 위치를 지정했을 때 0
- jump해서 돌아왔을 때 longjmp에서 지정한 값을 받음
- siglongjmp()
- env: 어디로 jump할 지 정한다.
- val: sigsetjmp를 통해 return 할 값을 정한다.
Example of siglongjmp/sigsetjmp
#include <setjmp.h> #include <signal.h> #include <stdio.h> #include <unistd.h> static sigjmp_buf jmpbuf; // 1이어야 jump를 해도 된다. static volatile sig_atomic_t jumpok = 0; /* ARGSUSED */ static void handler(int signo){ if (jumpok == 0) return; siglongjmp(jmpbuf, 1); } int main(void){ struct sigaction act; act.sa_flags = 0; act.sa_handler = handler; if((sigemptyset(&act.sa_mask) == -1) || (sigaction(SIGINT, &act, NULL) == -1)){ perror("Failed to set up SIGINT handler"); return 1; } fprintf(stderr, "This is process %ld\n", (long)getpid()); if(sigsetjmp(jmpbuf, 1)) fprintf(stderr, "Returned to main loop due to ^c\n"); jumpok = 1; for(;;); }
Programming with asynchronous I/O
Asynchronous I/O
- aio_read(), aio_write(), aio_return(), aio_error()
#include <aio.h> int aio_read(struct aiocb* aiocbp); int aio_write(struct aiocb* aiocbp);
- aio_read()
- 읽기 작업을 위한 요청을 queue에 넣어 background에서 진행 된다.
- 성공했을 시 0 / 실패했을 시 -1
- aio_write()
- 쓰기 작업을 위한 요청을 queue에 넣어 background에서 진행 된다.
- 성공했을 시 0 / 실패했을 시 -1
struct aiocb structure
- 기존 fd, buf, nbytes의 역할과 같은 필드들
- int aio_fildes;
- volatile void *aio_buf;
- size_t aio_nbytes;
- off_t aio_offset;
- I/O가 시작될 file offset
- int aio_reqprio;
- 요청의 우선순위를 변경 가능
- struct sigevent aio_sigevent;
- I/O가 완료되었을 때 OS가 signal을 보내줄 수 있다. 그 시그널을 받을 것인지
- 시그널을 받는다면 몇번 시그널로 받을 것인지
- aio_sigevent.sigev_notify가 SIGEV_NONE으로 설정한다면, signal로 통지받지 않는다.
- SIGEV_SIGNAL로 설정한다면 signal로 통지를 받으며, aio_sigevent.sigev_signo 에 몇 번 signal로 통지받을 지 등록 가능
- int aio_lio_opcode;
- 여러개의 비동기 I/O 를 한번에 요청할 때
return and error
#include <aio.h> ssize_t aio_return(struct aiocb* aiocbp); int aio_error(const struct aiocb* aiocbp);
- aio_return()
- 기존 비동기 I/O가 완료된 return 값을 받을 수 있다.
- aio_error()
- 기존 비동기 I/O의 진행사항을 monitoring할 수 있다.
- 0 ⇒ 완료
- EINPROGRESS ⇒ 진행 중
- error code ⇒ 에러
aio_suspend
#include <aio.h> int aio_suspend(const struct aiocb* const list[], int nent, const struct timespec* timeout);
- I/O 비동기 작업을 완료될 때까지 기다리는 함수
- 완료가 되면 return
- parameters
- list: 요청한 구조체의 array
- nent: list의 element 개수
- timeout: 해당 시간 까지만 기다린다. / NULL이면 계속 기다림
- aio_error는 더이상 EINPROGRESS를 반환하지 않는다.
- return values
- 성공했을 시 0 / 실패했을 시 -1
aio_cancel
#include <aio.h> int aio_cancel(int fildes, struct aiocb* aiocbp);
- I/O 비동기 요청을 취소하고 싶다.
- parameter
- aiocbp: 어떤 요청을 했었는지 사항 지정
- NULL이라면, fildes에 해당하여 요청했던 모든 I/O 비동기 요청을 모두 취소해라
- return values
- AIO_CANCELED: 성공
- AIO_NOTCANCELED: 요청한 I/O 중 하나라도 cancel되지 못한 요청이 있을 경우
- AIO_ALLDONE: 이미 다 완료가 되었을 경우
- -1: 실패했을 경우