|
System Program/I/O 2014. 3. 13. 11:08
1. epoll() 함수
epoll() 함수는 select()와 poll() 함수와 동일하게 I/O 멀티플렉싱으로 사용된다.
단 epoll() 함수는 리눅스에서만 호환성을 갖는 한계가 있다.
epoll() 함수는 기존 두 함수에 비해서 파일 디스크립터의 준비 상태를 알리는 방식에서
더 유연성을 갖는다.
1.1. 상태 변화를 알리는 방식
- Level-Triggered Notification
- 상태 변화를 감지하고자 하는 파일 디스크립터가 준비되면 무조건 알리는 방식
- Edge-Triggered Notification
- 항상 새로 준비된 파일 디스크립터에 대해서만 알리는 방식
두 알림 방식이 마음에 잘 왔다지 않을 것이다. 쉽게 구분하는 방법은 두 번째 방식은 일단 A라는
파일 디스크립터에 대해서 알림을 받았는데 다른 새로운 파일 디스크립터가 준비되기 전에
다시 같은 A가 알림을 받을 준비가 되면 생략한다. 하지만 첫번째는 중복과 상관없이 무조건 알린다.
1.2. 각 함수별로 알림 지원 여부
I/O 모델 |
Level-Triggered |
Edge-Triggered |
select(), poll() |
O |
X |
시그널 기반 I/O |
X |
O |
epoll() |
O |
O |
시그널 기반 I/O에 대한 설명은 생략한다. 개인적으로 그 다지 좋은 방법은 아닌 것 같아서 ...
2. epoll() API 함수들
2.1. epoll_create() 함수
#include <sys/epoll.h>
int epoll_create(int size);
성공하면 파일 디스크립터를 리턴하고, 에러가 발생하면 '-1'을 리턴
매개 변수 size에는 감시할 파일 디스크립터의 수를 명시한다.
성공하면 epoll_create()는 자신의 다른 함수에서 사용할 새로운 파일 디스크립터를 리턴한다.
물론 dup() 함수 같은 것으로 다른 파일 디스크립터처럼 복사하고 이용할 수 있다.
2.2. epoll_ctl() 함수 : 관심 목록 변경 및 알림 방식 변경
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
[주의 사항] 디스크 파일 디스크립터는 대상에서 제외된다.
op |
설명 |
EPOLL_CTL_ADD |
관심있는 파일디스크립터를 추가 |
EPOLL_CTL_MOD |
기존 파일 디스크립터를 수정 |
EPOLL_CTL_DEL |
기존 파일 디스크립터를 관심 목록에서 삭제 |
struct epoll_event{
uint32_t events; /* epoll 이벤트 (비트 마스트) */
epoll_data_t data; /* 사용자 데이터 */
};
typedef union epoll_data {
void *ptr; /* 사용자 정의 데이터 포인터 */
int fd; /* 파일 디스크립터 */
uint32_t u32; /* 32비트 정수 */
uint64_t u64; /* 64비트 정수 */
} epoll_data_t;
2.3. epoll_wait() : 알림 기다림.
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);
성공하면 준비된 파일 디스크립터 수 리턴, '0'은 타임아웃 경우, '-1'은 에러를 말한다.
준비된 파일 디스크립터를 배열로 리턴한다. maxevents는 리턴한 배열의 갯수이다.
timeout 값의 의미는 앞의 select(), poll()과 동일하므로 생략한다.
- epoll events 필드 가능한 비트 마스크
비트 |
입력 |
리턴 |
설명 |
EPOLLIN |
O |
O |
높은 순위 데이터를 제외한 데이터를 읽을 수 있다. |
EPOLLPRI |
O |
O |
높은 순위 데이터를 읽을 수 있다. |
EPOLLRDHUP |
O |
O |
상대편 소켓 shutdown 확인 |
EPOLLOUT |
O |
O |
일반 데이터를 기록할 수 있다. |
EPOLLET |
O |
X |
에지 트리거를 알림 방식으로 선택 (기본은 레벨트리거 임) |
EPOLLONESHOT |
O |
X |
이벤트를 알린 후에 이벤트 감시를 비활성화 |
EPOLLERR |
X |
O |
에러가 발생함. |
EPOLLHUP |
X |
O |
행업 발생 |
많은 면에서 poll() 함수의 이벤트와 유사하다.
3. 예제
/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/epoll.h>
#define MAX_BUFFER_LEN 256
#define MAX_EVENT_NUM 1
int main(int argc, char *argv[])
{
int retval, m_read, m_write;
int i;
int fd;
int epfd;
struct epoll_event ev;
struct epoll_event evlist[MAX_EVENT_NUM];
char buffer[MAX_BUFFER_LEN];
if((fd = open("./temp.txt", O_RDWR)) == -1)
{
perror("open : ");
exit(EXIT_FAILURE);
}
/* epoll create */
epfd = epoll_create(1);
if(epdf == -1)
{
perror("epoll_create : ");
exit(EXIT_FAILURE);
}
/* epoll control */
ev.events = EPOLLIN;
ev.data.fd = STDIN_FILENO;
if(epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
{
perror("epoll_ctl : ");
exit(EXIT_FAILURE);
}
while(1)
{
memset(buffer, 0x00, MAX_BUFFER_LEN);
retval = epoll_wait(epfd, evlist, MAX_EVENT_NUM, -1);
if(retval == -1)
{
/* signal interrupt */
if(errno == EINTR)
{
continue;
}
else
{
perror("epoll_wait : ");
exit(EXIT_FAILURE);
}
}
printf("retval = %d \n", retval);
for(i = 0; i < MAX_EVENT_NUM; i++)
{
if(evlist[i].events & EPOLLIN)
{
m_read = read(STDIN_FILENO, buffer, MAX_BUFFER_LEN);
printf("[read] m_read = %d \n", m_read);
m_write = write(fd, buffer, m_read);
printf("[write] m_write = %d \n", m_write);
}
}
usleep(1000);
}
close(fd);
exit(EXIT_SUCCESS);
}
System Program/I/O 2014. 3. 12. 20:44
1. poll() 시스템 함수
select()와 비슷한 작업을 수행한다. select() 함수가 BSD로부터 기원한다면 poll() 함수는 시스템 V로부터
기원한다. 두 함수간에 차이점으로는 파일디스크립터를 관리하는 방식면에서 서로 다르다.
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
준비된 파일 디스크립터 수를 리턴한다.
타임 아웃일 경우 '0'을, 에러가 발생하면 '-1'을 리턴한다.
pollfd 구조체는 아래와 같다. nfds는 pollfd 배열의 갯수를 의미한다.
struct pollfd {
int fd; /* 파일 디스크립터 */
short events; /* 확인하기를 원하는 이벤트에 대한 비트마스크 */
short revents; /* 리턴된 이벤트 마스크 */
};
1.1. timeout 값
값 |
설명 |
-1 |
pollfd 구조체에 포함된 파일 디스크립터 중 하나가 준비되거나 이벤트를 수신할 때까지
블록한다. (블록의 정도는 파일디스크립터의 종류에 따라 다르다) |
0 |
블록하지 않고 파일디스크립터가 준비 상태인지만을 확인한다. |
0 보다 큰값 |
최대 설정된 타임아웃 기간만큼 블록한다. |
1.2. 리턴값
값 |
설명 |
-1 |
에러가 발생 |
0 |
파일 디스크립터가 준비되기 전에 타임아웃된 경우 |
양수 |
준비된 파일 디스크립터의 갯수 |
2. events/revents에 대한 비트 마스크 값
비트 |
events |
revents |
설명 |
POLLIN |
O |
O |
높은 우선순위 데이터 외의 데이터를 읽을 수 있다. |
POLLRDNORM |
O |
O |
상동 (리눅스는 POLLIN과 동일하게 동작) |
POLLRDBAND |
O |
O |
우선순위 데이터를 읽을 수 있다. (리눅스 사용 안함) |
POLLPRI |
O |
O |
높은 우선순위 데이터를 읽을 수 있다. |
POLLRDHUP |
O |
O |
상대편 소켓 셧다운 |
POLLOUT |
O |
O |
일반 데이터를 기록할 수 있다. |
POLLWRNORM |
O |
O |
상동 (리눅스는 POLLOUT과 동일하게 동작) |
POLLWRBAND |
O |
O |
우선순위 데이터를 기록할 수 있다. |
POLLERR |
X |
O |
에러가 발생했다. |
POLLHUP |
X |
O |
장애가 발생했다. |
POLLNVAL |
X |
O |
파일 디스크립터가 열리지 않는다. |
POLLMSG |
X |
X |
리눅스에서는 사용하지 않음. |
3. 파일 디스크립터의 준비 상태
select()와 poll() 함수는 모든 파일 디스크립터 종류에서 블록되지는 않는다. 파일 디스크립터의 종류마다
블록의 동작이 다르다. 예로 일반 파일에 대한 파일 디스크립터는 블록하지 않고 바로 리턴한다.
이 예제가 select() 함수편의 예제에서 select() 함수의 리턴값을 찍어보면 알 수 있다.
따라서 I/O 멀티플렉싱 기법을 사용 시에는 파일 디스크립터의 종류에 대한 블록 동작에 대해서 잘 살펴봐야 한다.
4. 예제
select() 함수 예제 편에서 디스크 파일 디스크립터를 대상으로 했다면, 여기서는 표준 입력을 블록 대상으로 하였다.
select() 함수 편에서 retval값을 보면 실제 블록되지 않는다. 항상 준비된 상태가 된다.
파일 디스크립터 종류에 따른 블록되는 현상 차이를 보기위해서 poll() 예제에서는 표준 입력을 대상으로 예제를
작성하였다. 두 예제 간의 차이점을 보는 것도 도움이 될 것이다.
/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <poll.h>
#define MAX_BUFFER_LEN 256
int main(int argc, char *argv[])
{
int retval, m_read, m_write;
int fd;
struct pollfd pollfds[1];
char buffer[MAX_BUFFER_LEN];
if((fd = open("./temp.txt", O_RDWR)) == -1)
{
perror("open : ");
exit(EXIT_FAILURE);
}
pollfds[0].fd = STDIN_FILENO;
pollfds[0].events = POLLIN;
while(1)
{
memset(buffer, 0x00, MAX_BUFFER_LEN);
retval = poll(pollfds, 1, -1);
if(retval == -1)
{
perror("poll : ");
exit(EXIT_FAILURE);
}
printf("retval = %d \n", retval);
if(pollfds[0].revents & POLLIN)
{
m_read = read(STDIN_FILENO, buffer, MAX_BUFFER_LEN);
printf("[read] m_read = %d \n", m_read);
m_write = write(fd, buffer, m_read);
printf("[write] m_write = %d \n", m_write);
}
usleep(1000);
}
close(fd);
exit(EXIT_SUCCESS);
}
System Program/mutex 2014. 3. 10. 21:41
1. 조건 변수
조건 변수는 항상 뮤텍스와 함께 사용된다. 뮤텍스에 대한 보조적인 역활을 한다고 생각한다.
뮤텍스는 공유 변수에 대한 상호 배제를 제공하는 한편, 조건 변수는 어떤 상태 변화를
쓰레간에 보내면서 더 정교한 상호 배제를 제공할 수 있다.
2. 정적 조건 변수 초기화
데이터형은 pthread_cond_t 이다. 뮤텍스 정적 초기화 구문과 같다.
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
3. 조건 변수 대기와 시그널
조건 변수는 하나 이상 쓰레드가 조건 변수 상태 변화를 기다리고 있을 때, 조건 변수의 상태를 알리는
시그널을 보낸다. 여기서 시그널은 일반적인 프로세스와 쓰레드에서 사용되는 시그널이 아니다.
따라서 조건 변수의 상태를 알리는 행위를 시그널을 보낸다라고 하고,
조건 변수의 상태를 기다리는 쪽에서 행위를 대기라 명한다.
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
성공하면 '0'을 리턴하고, 에러가 발생하면 에러번호 (양수)를 리턴한다.
pthread_cond_signal() 함수와 pthread_cond_broadcast() 모두 cond로 지정된 조건 변수에
시그널을 보낼 수 있다. pthread_cond_wait() 함수는 조건 변수 상태 변화를 기다리면서 대기한다.
시그널 전송 두 함수의 차이는 pthread_cond_wait()가 여러 쓰레드에서 블록되어 대기 중 일 때 차이가 난다.
pthread_cond_signal() 함수는 최소 하나의 쓰레드가 깨어 난다는 것만 보장하는
반면 다른 함수는 모든 쓰레드가 깨어나기를 보증한다.
4. 동적 조건 변수 할당 및 제거 함수
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);
성공하면 '0'을 리턴하고, 에러가 발생하면 에러번호 (양수)를 리턴한다.
위 함수의 사용법과 동작 기법은 동적 뮤텍스 사용법과 같다. 따라서 구체적인 사용법은 메뉴얼 또는
동적 뮤텍스 속성 부분을 참조바람.
System Program/mutex 2014. 3. 10. 19:44
1. 정적 뮤텍스
뮤텍스는 쓰레드 공유 자원 사용을 동기화하는 목적으로 사용된다.
뮤텍스는 정적 변수에 초기값을 줘서 고정되는 정적 뮤텍스와 실행 시에 동적으로 생성할 수 있는 동적 뮤텍스로
구분된다. 여기서는 정적 뮤텍스 사용법에 대해서 알아본다.
2. 정적 뮤텍스 초기화
정적 뮤텍스는 pthread_mutex_t 형의 변수에 초기값을 반드시 할당해야 하며,
기본적으로 정적 초기화는 PTHREAD_MUTEX_INITIALIZER 값만 설정하는 것이 가능하다.
하지만 리눅스는 아래 [가능한 초기값] 부분에 기술된 초기값도 정적 초기값으로 사용할 수 있다.
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
[가능한 초기값]
가능한 초기값 |
설명 |
PTHREAD_MUTEX_INITIALIZER |
Fast Mutex, Mutex 생성 시 초기 Default 값 |
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP |
쓰레드로부터 다시 Lock을 획득하도록 허용 |
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP |
mutx 에러 체크용 |
위의 리눅스 초기값은 동적 뮤텍스 부분에서 초기화에 사용되는 값과 일대일 매칭된다.
3. 뮤텍스 잠금과 풀림 함수
초기 뮤텍스는 풀려 있다. 뮤텍스를 잠궈서 다른 쓰레드의 임계영역 진입을 차단하거나
해제하고자 할 경우에는 아래 함수들을 사용한다.
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
성공하면 '0'을 리턴하고, 에러가 발생하면 에러번호(양수)를 리턴한다.
반드시 위의 함수는 쌍으로 사용되야 한다. 만약 잠근 뮤텍스를 다시 잠그려고 하면 데드락되거나
EDEADLK 에러값을 설정한다. 리눅스는 기본적으로 데드락된다.
4. 추가적인 뮤텍스 잠금과 풀림 함수
#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *restrict abs_timeout);
성공하면 '0'을 리턴하고, 에러가 발생하면 에러번호(양수)를 리턴한다.
pthread_mutex_trylock() 함수는 뮤텍스가 현재 잠겨 있으면 EBUSY 에러값을 설정하고 실패한다.
pthread_mutex_timedlock() 함수는 뮤텍 잠근에 시간제한을 둔 함수이며 지정된 시간안에 잠금에 실패하면
ETIMEDOUT 에러값을 설정하고 리턴한다. 기본 동작은 pthread_mutex_lock()와 같다.
5. 뮤텍스 에러 가능값
pthread_mutex_lock()함수는 아래의 에러코드를 반환한다.
-
뮤텍스가 잘못 초기화 되었다.
-
이미 잠금을 얻은 쓰레드가 다시 잠금을 요청할 때 (error checking 뮤텍스일 경우 사용할 수 있다)
pthread_mutex_trylock()함수는 아래의 에러코드를 반환한다.
-
뮤텍스가 잠겨 있어서 잠금을 얻을 수 없다.
-
뮤텍스가 잘못 초기화 되었다.
System Program/semaphore 2014. 3. 10. 16:18
1. 무기명 세마포어
무기명 세마포어는 응용프로그램이 할당한 메모리에 저장된 sem_t형의 변수다.
세마포어는 프로세스나 쓰레드가 공유하는 메모리 영역에 위치함으로써 사용하는 프로세스나 쓰레드에
가용하도록 만들어진다.
무기명 세마포어는 세마포어를 감소, 증가 및 현재값 등을 얻어오는 함수들은 앞의 기명 세마포어와
동일하게 사용하고 무기명 세마포어를 초기화하고 종료하는 함수만 다르다.
2. 무기명 세마포어 초기화
세마포어를 초기화하고 프로세스간에 또는 쓰레드간에 공유되는지 여부를 시스템에 알린다.
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
초기화 함수에서 pshared가 '0'이면 프로세스내의 쓰레드간에 공유를 의미하며
'0'이 아니면 큰 값은 프로세스간의 공유를 의미한다.
하지만 어디까지나 pshared 설정에 따른 동작은 시스템 의존적이며 단지 호환성을 맞추기위해 위에 기술한 것처럼
프로그램을 작성하는 것이 맞다.
3. 세마포어 종료
#include <semaphore.h>
int sem_destroy(sem_t *sem);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
System Program/semaphore 2014. 3. 10. 15:58
1. 세마포어의 종류
세마포어는 기명 세마포어와 무기명 세마포어의 크게 두가지 종류로 분류된다.
기명 세마포어는 말 그대로 이름이 있는 세마포어로 세마포어의 사용 및 구분은 이름을 통해서 할 수 있다.
하지만 무기명은 이름이 없어서 프로세스간에 공유하는 방식이 다르다.
무기명의 경우에는 세마포어 세마포어 디스크립터를 공유영역에 할당함으로써 프로세스 및 쓰레드간에
사용할 수 있다.
2. 세마포어 열기
새로운 기명 세마포어를 생성하거나 기존 파일을 열 때 사용한다. 각 인자의 사용법이나 내용은
메세지 큐의 부분과 같다. 따라서 자세한 내용은 메세지 큐 부분 참조
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag, ...
/* mode_t mode, unsigned int value */);
성공하면 세마포어의 포인터를 리턴하고, 에러가 발생하면 SEM_FAILED를 리턴한다.
3. 세마포어 종료
#include <semaphore.h>
int sem_close(sem_t *sem);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
4. 세마포어 제거
#include <semaphore.h>
int sem_unlink(const char *name);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
5. 세마포어 대기
sem이 참조하는 세마포어의 값을 감소한다.
#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem); /* sem_wait() 함수의 비블로킹 버전이다. */
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
감소 동작을 즉시 실행될 수 없으면, sem_trywait()는 EAGAIN에러로 실패한다.
#include <semaphore.h>
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
세마포어를 감소하지 못한체 시간이 만료되면 ETIMEDOUT 에러로 실패한다.
6. 세마포어 게시
sem이 참조하는 세마포어의 값을 증가한다.
#include <semaphore.h>
int sem_post(sem_t *sem);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
7. 세마포어 현재값 추출
sem이 참조하는 세마포어의 현재값을 int형의 포인터 sval에 리턴한다.
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
System Program/message queue 2014. 3. 10. 15:19
1. 메세지 송신
메세지를 실제 전송하는 함수이다. POSIX 메세지 IPC는 시스템 V와 차이는 메세지 전송 시
우선 순위를 부여할 수 있다. 일반적으로 우선순위는 0 ~ 31의 값을 갖고 높을수록 우선 순위가 높다.
메세지 우선순위에 대한 정확한 값은 sysconf(_SC_MQ_PRIO_MAX)를 확인하는 것이 바람직하다.
#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
2. 메세지 수신
#include <mqueue.h>
int mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
성공하면 수신된 메세지의 바이트 수를 리턴하고, 에러가 발생하면 '-1'을 리턴
3. 타임아웃을 가진 메세지 송수신 함수
#include <mqueue.h>
#include <time.h>
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio,
const struct timespec *abs_timeout);
int mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio,
const struct timespec *abs_timeout);
리턴값은 타임아웃이 없는 함수의 원형의 리턴값과 각각 동일하다.
abs_timeout값을 얻어오기 위해 clock_gettime()를 사용할 수 있다.
설정한 시간이 만료되면 ETIMEOUT에러로 실패한다.
4. 송수신 함수 사용 시 주의 사항
메세지 송수신 함수에서 메세지 msg_len의 값은 mq_msgsize 속성보다 작거나 동일해야 한다.
그렇지 않으면 EMSGSIZE 에러가 발생한다.
메세지 큐가 비어있고, O_NONBLOCK이 설정된 경우 EAGAIN 에러로 즉시 실패한다.
5. 메세지 통지
POSIX가 시스템 V와 또 다른 차이점은 비동기적 메세지 큐가 가용할 수 있는 경우 통지를 받을 수 있다.
통지의 처리는 시그널과 쓰레드에 의해서 각각 구현될 수 있다.
#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
성공하면 수신된 메세지의 바이트 수를 리턴하고, 에러가 발생하면 '-1'을 리턴
메세지 통지에 대한 내용을 전반적으로 살펴보기에는 기술할 내용이 너무 많다. 필요 시 찾아보면 것으로 마무리 한다.
단 비동기적으로 메세지 통지를 받고자 할 경우에는 메세지 통지 기법을 사용할 수 있다.
System Program/message queue 2014. 3. 10. 12:01
1. 개요
메세지 큐는 프로세스간에 IPC 통신이다. IPC관련하여 시스템 V와 POSIX의 두 양대 산맥이 있다.
내가 생각하기에는 POSIX가 대세인 것 같고, 리눅스에서는 더더욱 그런 것 같다.
따라서 시스템 V 기반도 있지만 나는 POSIX 기반만 중점적으로 살펴본다.
2. 메세지 큐 열기
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, ...
/* mode_t mode, struct mq_attr *attr */);
성공하면 메세지 큐 디스크립터 리턴, 에러가 발생하면 (mqd_t) -1을 리턴
두번째 인자인 oflag에 가능한 값은 아래와 같다.
oflag |
설명 |
O_CREAT |
큐가 이미 존재하지 않는 경우 생성 |
O_EXCL |
O_CREAT를 가지고, 큐를 전용으로 생성 |
O_RDONLY |
읽기 전용으로 열기 |
O_WRONLY |
쓰기 전용으로 열기 |
O_RDWR |
읽기와 쓰기용으로 열기 |
O_NONBLOCK |
비블로킹 모드로 열기 |
기존 메세지 큐를 여는데 사용하는 경우, 단지 2개의 인자만을 필요로 한다.
O_CREAT가 oflag에 명시되면 mode와 attr가 추가로 필요하다.
mode 인자는 I/O 블로그 부분에서 소개한 open() 함수에서 소개한 내용과 동일하다.
attr 인자는 새로운 메세지 큐의 속성을 명시하는 mq_attr 구조체이다. NULL로 설정되면 기본값으로
메세지 큐가 설정된다. 구조체는 아래와 같다.
struct mq_attr {
long mq_flags; /* 메세지 큐 디스크립션 플래그 0이나 O_NONBLOCK
[mq_getattr(), mq_setattr()] */
long mq_maxmsg; /* 큐의 최대 메세지 수 [mq_open(), mq_getattr()] */
long mq_msgsize; /* 최대 메세지 크기 (바이트) [mq_open(), mq_getattr()] */
long mq_curmsgs; /* 현재 큐에 있는 메세지 수 [mq_getattr()] */
};
3. 메세지 큐 닫기
메세지를 닫는다는 것은 메세지 큐를 삭제한다는 것은 아니다. 단지 메세지 큐 디스크립터를 닫는다는 것이다.
#include <mqueue.h>
int mq_close(mqd_t mqdes);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
4. 메세지 큐 제거
모든 프로세스가 해당 메세지 큐를 더 이상 사용하지 않을 경우 제거되도록 큐에 표시한다.
프로세스가 해당 메세지 큐를 더 이상 사용하지 않을 경우 즉시 제거된다.
#include <mqueue.h>
int mq_unlink(const char *name);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
5. 메세지 큐 속성 설정 함수
#include <mqueue.h>
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
이전 메세지 큐에 대한 설정을 알고 싶으면 oldattr를 할당하고 그렇지 않으면 NULL로 설정하면 된다.
6. 메세지 큐 속성 추출 함수
#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
7. 주의 사항
mq_maxsize와 mq_msgsize 속성은 메세지 큐가 생성되는 시점에 고정되고, 차후에 변경될 수 없다.
위의 메세지 속성 구조체 주석문에 볼 수 있듯이 mq_setattr() 함수로 설정할 수 있는 구조체 값은
mq_flags 값이다. 일반적으로 O_NONBLOCK 값을 추가적으로 설정할 때 사용한다.
System Program/pthread 2014. 3. 10. 11:01
1. 쓰레드와 시그널
시그널은 정말 복잡하다. 여러가지 경우의 수를 모두 제쳐두고 간단히 말해서 아래와 같이 나는 정리하고 싶다.
시그널은 프로세스 기반으로 동작한다. 즉 시그널의 처리 기본 단위는 프로세스이다.
단 시그널 기반으로 제어가 가능한데 이 경우는 쓰레드 기반 시그널 마스크를 통해서 가능하다.
2. 시그널 마스크 제어
프로세스 기반이 아닌 쓰레드 기반으로 제어를 하고 싶으면 아래 함수를 통해서 쓰레드 전용 시그널 마스크
를 설정하여야 한다.
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
위 함수의 사용법은 프로세스 기반 sigprocmask() 함수와 같다.
3. 쓰레드 기반 시그널 전송
#include <signal.h>
int pthread_kill(pthread_t thread, int sig);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
위의 함수는 프로세스 내의 다른 쓰레드에 시그널 sig를 보낸다. 대상 쓰레드는 thread로 지정한다.
#include <signal.h>
int pthread_sigqueue(pthread_t thread, int sig, const union sigval value);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
pthread_kill() 과 siggueue() 의 기능을 합친 것이다.
같은 프로세스 내에 시그널에게 데이터까지 보낼 경우 위의 함수를 사용한다.
4. 마무리
프로세스 기반의 시그널을 이해하고 적용하기에도 매우 복잡하다. 이를 쓰레드 기반까지 확장하여 처리하는
응용프로그램이 얼마나 될까 쉽다. 여기서는 단지 시그널을 쓰레드에서도 다룰 수 있다는 정도만 이해하고 넘어간다.
System Program/pthread 2014. 3. 7. 14:01
1. 쓰레드 조인
pthread_create()함수를 통해 생성된 쓰레드는 다른 쓰레드에 의해 아니면 자체적으로 종료될 수 있다.
쓰레드 조인은 주 쓰레드가 추가적으로 생성된 쓰레드의 종료 상태를 확인하고
자신이 최종적으로 종료하겠다는 내포적인 의미를 갖는다.
즉 다른 장에서 기술한 것처럼 주 쓰레드가 종료하면 다른 추가적으로 생성된 쓰레드 또한 모두
강제적으로 즉시 종료한다. 쓰레드 조인은 주 쓰레드가 추가적인 쓰레드가 모든 작업을 완료할 수 있게
기다려 주며 마지막까지 살아서 모든 쓰레드의 인무가 완료될 때까지 책임지는 구조이다.
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
pthread_join() 함수의 retval 값은 pthread_exit() 함수를 통해서 설정한 값을 확인할 수 있다.
1.1. 명심해야 할 사항.
pthread_create() 함수에서 atrribute NULL로 주고 기본적으로 생성된 쓰레드는 쓰레드 조인으로
종료를 시켜줘야 한다. 그렇지 않으면 좀비 쓰레드가 계속적으로 생성되어 차후에는
더 이상 쓰레드를 생성할 수 없는 상태로 빠지게 된다.
1.2. 예제
예제는 쓰레드 생성 부분을 참조하길 바람.
2. 쓰레드 분리
기본적으로 쓰레드는 조인할 수 있다. 만약 쓰레드의 종료 상태를 주 쓰레드에서 확인할 필요가 없거나
추가적인 쓰레드의 뒷정리를 시스템에서 자동적으로 하기를 원할 때는 쓰레드를 아래 함수를 통해서 분리한다.
#include <pthread.h>
int pthread_detach(pthread_t thread);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
2.1. 주의 사항
위 쓰레드 조인에서도 기술한 것처럼 주 쓰레드가 종료되면 추가적인 쓰레드도 모두 즉시 강제적으로 종료된다.
따라서 쓰레드 분리 통해 추가적인 쓰레드를 분리하기는 했지만 분리된 쓰레드가 정상적으로 모두
일처러를 완료하기 위해서는 주 쓰레드는 종료해서는 안된다. 보통 주 쓰레드는 무한 루프를 도는 것이 일반적이다.
2.2. 예제
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
// 쓰레드 함수
// 1초를 기다린후 아규먼트^2 을 리턴한다.
void *t_function(void *data)
{
char a[100000];
int num = *((int *)data);
printf("Thread Start\n");
sleep(5);
printf("Thread end\n");
}
int main()
{
pthread_t p_thread;
int thr_id;
int status;
int a = 100;
printf("Before Thread\n");
thr_id = pthread_create(&p_thread, NULL, t_function, (void *)&a);
if (thr_id < 0)
{
perror("thread create error : ");
exit(0);
}
// 식별번호 p_thread 를 가지는 쓰레드를 detach
// 시켜준다.
pthread_detach(p_thread);
pause();
return 0;
}
위의 예제는 pthread_detach()함수의 사용 예를 보기위해서 인용하였으나 일관된 쓰레드 생성 관리를
위해서 attrubute를 사용하는 것이 더 좋은 방법이라 생각된다.
|