|
System Program/socket 2014. 3. 3. 09:18
1. socket() 함수
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
성공하면 파일 디스크립터을 리턴, 에러가 발생하면 '-1'을 리턴
1.1. domain 매개 변수
도메인 |
통신수단 |
응용프로그램 간의 통신 |
주소포멧 |
주소구조체 |
AF_UNIX |
커널 내부 |
동일 호스트 |
경로명 |
sockaddr_un |
AF_INET |
IPv4 이용 |
IPv4로 연결된 호스트 |
32비트 주소 +
16비트 포트 번호 |
sockaddr_in |
AF_INET6 |
IPv6 이용 |
IPv6로 연결된 호스트 |
128비트 주소
16비트 포트 번호 |
sockaddr_in6 |
1.2. type 매개 변수
종류 |
설명 |
SOCK_STREAM |
연결지향적 양방향 신뢰성 있는 통신 방식 |
SOCK_RAW |
socket API에 의한 데이터 가공없이 데이터를 받고자 할 때 사용 |
SOCK_DGRAM |
SOCKET_STREAM과 상반되는 개념 |
1.3. protocol 매개 변수
IPPROTO_TCP : TCP 프로토콜로 AF_INET 도메인과 SOCK_STREAM 유형 사용 시.
IPPROTO_UDP : UDP 프로토콜로 AF_INET 도메인과 SOCK_DGRAM 유형 사용 시.
IPPROTO_RAW : SOCK_RAW 사용하는 경우 시
일반적으로 '0'을 설정, 자세한 설명은 man page를 참조하는 것이 좋다.
2. bind() 시스템 함수
소켓을 주소에 결속한다. 아래 sockaddr 구조에는 결속된 주소 정보를 담고 있다.
전화기에 비유하자면 전화번호를 전화국 (커널)에 등록하는 절차에 해당한다.
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
성공하면 '0'을 리턴, 에러가 발생하면 '-1'을 리턴
domain 매개 변수 표, 5번째 열에 보여지는 것처럼 도메인에 따라 주소체계가 모두 다르다.
모두 다른 주소체계를 위의 sockaddr 구조체를 통해서 통일화할 수 있다.
sockaddr 구조체는 아래와 같다.
struct sockaddr {
sa_family_t sa_family; /* 주소 패밀리 (AF_* 상수) */
char sa_data[14]; /* 소켓 주소 (소켓도메인에 따라 크기가 변한다) */ };
3. listen() 시스템 함수
커널에게 bind에서 결속한 주소로 연결을 받기위해 대기 중임을 알린다.
전화기로 따지면 전화를 받을 준비가 모두된 상태를 의미한다.
이처럼 연결이 되기를 기다리는 형태의 동작을 수동형 연결이라 하며, 일반적으로 서버쪽의 동작이다.
#include <sys/socket.h>
int listen(int sockfd, int backlog);
성공하면 '0'을 리턴, 에러가 발생하면 '-1'을 리턴
위에서 backlog 값은 연결이 지연된 요청에 대한 개수를 리턴한다.
리눅스에서 이 값은 128로 설정되어 있으며, /proc/sys/net/core/somaxconn 파일을 통해서 런타임으로
한도값을 설정할 수 있다.
4. accept() 시스템 함수
연결을 요청한 소켓을 수락하는 동작이다.
전화기로 따지면 수화기를 드는 동작에 해당한다.
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
성공하면 파일 디스크립터를 리턴하고, 에러가 발생하면 '-1'을 리턴
accept에서 핵심은 connect() 호출한 상대방의 소켓과 새로운 연결을 맺을 소켓을 생성하는 것이다.
따라서 socket()함수를 통해서 생성된 소켓은 연결을 계속 기다리는 소켓으로 남아있고,
accept()를 통해서 새로 생성된 소켓을 통해서 connect() 호출한 쪽과 통신을 할 수 있다.
따라서 accept()함수의 리턴값에 새로운 소켓을 리턴하는 것이다.
뒤이은 sockaddr 구조체 포인터를 통해서 connect()을 요청한 쪽의 주소를 얻을 수 있다.
상대방의 주소가 필요없을 경우, 각각 NULL, '0'을 설정하면 된다.
5. connect() 시스템 함수
위에서 listen()으로 대기중인 소켓에 연결을 요청할 때 사용하는 시스템 함수이다.
능동형 연결에 해당하며, 일반적으로 클라이언트쪽의 동작이다.
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
클라이언트쪽에서는 별도의 bind() 동작을 통해서 주소를 결속하는 작업은 없다.
connect()함수의 주소관련 매개변수를 통해서 bind() 동작과 유사한 동작은 포함되어 있다.
6. 소켓을 통한 데이터 전송 및 수신 함수
연결 지향형 소켓 응용에서는 read()/write()함수를 통해서 데이터를 송수신한다.
여기서 중요한 점은 양단간에 연결이 끊어졌을 때, 가장 기본적인 에러처리에 대한 것이다.
연결이 끊어진 곳에 데이터를 읽을려고 시도하면 EOF를 리턴한다.
반대로 데이터를 쓰려고 할 경우, 해당 프로그램은 SIGPIPE 시그널을 받게되고,
시스템 호출은 EPIPE로 실패한다. 시그널 핸들러가 없는 경우 errno를 확인하는 것으로 에러처리하는 것이 바람직하다.
7. close() 시스템 함수
연결된 socket를 닫을 때 사용한다.
8. 연결형 서버-클라이언트 흐름도
System Program/I/O 2014. 3. 1. 15:47
1. select() 시스템 호출
I/O 멀티플렉싱을 구현한 시스템 함수 중에 하나로 I/O Operation이 가능한 파일 디스크립터를 감시할 수 있다.
#include <sys/time.h>
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
준비된 파일 디스크립터 수를 리턴한다.
타임 아웃일 경우 '0'을, 에러가 발생하면 '-1'을 리턴한다.
timeout |
설명 |
0 |
select()함수는 블록되지 않고 지정된 파일 대기 상태인지만 확인하고 즉시 리턴한다. |
NULL |
읽기/쓰기/예외에 지정한 파일 디스크립터 중에 하나 이상 준비된 상태일 때 리턴 |
'0' 이 아닌 값 |
지정된 시간만큼 기다린다 |
리턴값 |
설명 |
-1 |
에러가 발생한 경우이고, EBADF와 EINTR 에러로 에러가 설정될 수 있다.
EBADF는 세개의 파일 디스크립터 집합에 유효하지 않는 파일 디스크립터가 설정된 경우
EINTR은 시그널 핸들러에 의해서 발생한 것으로 EINTR은 자동으로 재시작되지 않는다. |
0 |
파일 디스크립터가 준비상태가 되기 전에 타임아웃된 경우 |
양수 |
한개 이상의 파일 디스크립터가 준비된 경우 |
2. fd_set 데이터형 처리 함수들
위의 select() 함수 정의 부분에서 각각 읽기, 쓰기, 예외 파일 디스크립터 집합에 대한 기본 자료형
fd_set에 대한 자료형을 처리하기 위한 함수를 소개한다.
#include <sys/select.h>
void FD_ZERO(fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
void FD_CLR(int fd, fd_set *fdset);
int FD_ISSET(int fd, fd_set *fdset);
fd 가 fdset에 포함되어 있으면 '1'을 아니면 '0'을 리턴한다.
위의 함수들에 대한 자세한 설명을 스킵한다. 이름에서 그냥 유추할 수 있다.
파일 디스크립터 집합의 최대 크기는 FD_SETSIZE 상수에 정의되어 있다. 리눅스에서는 1024이다.
3. 예제
temp.txt 파일을 읽기 전용으로 하나 열고, 여기에 데이터가 쓰여지면 select() 함수의 블록이
풀리고 표준출력에 파일에 입력된 내용을 출력한다. 간단한 예제를 통해서 함수 사용법을 익히는데 중점을 둔다.
/*
* 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/select.h>
#define MAX_BUFFER_LEN 256
int main(int argc, char *argv[])
{
int retval, n;
int fd;
fd_set readfds, writefd;
char buffer[MAX_BUFFER_LEN];
if((fd = open("./temp.txt", O_RDONLY)) == -1)
{
perror("open : ");
exit(EXIT_FAILURE);
}
/*
newfd = dup(STDOUT_FILENO);
if(newfd == -1)
{
perror("dup : ");
exit(EXIT_FAILURE);
}
close(STDOUT_FILENO);
*/
while(1)
{
FD_ZERO(&readfds);
FD_ZERO(&writefd);
FD_SET(fd, &readfds);
retval = select(fd + 1, &readfds, &writefd, NULL, NULL);
switch(retval)
{
case -1:
perror("slect : ");
exit(EXIT_FAILURE);
default :
if(FD_ISSET(fd, &readfds))
{
while((n = read(fd, buffer, MAX_BUFFER_LEN)) > 0)
{
printf("%s", buffer);
}
}
memset(buffer, 0, MAX_BUFFER_LEN);
break;
}
usleep(1000);
}
close(fd);
exit(EXIT_SUCCESS);
}
System Program/I/O 2014. 2. 26. 16:10
1. 정의
프로세스의 실제 사용자와 그룹 ID에 기반하여 파일 접근권을 검사한다.
#include <unistd.h>
int access(const char *pathname, int mode);
모든 권한이 혀용된 경우 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
pathname이 심볼릭 링크이면 역참조한다.
위 함수의 mode에 할당될 수 있는 매크로는 아래와 같고, OR연산으로 여러개 지정할 수 있다.
상수 |
설명 |
F_OK |
파일이 존재하는가? |
R_OK |
파일을 읽을 수 있는가? |
W_OK |
파일에 쓸 수 있는가? |
X_OK |
파일이 실행될 수 있는가? |
System Program/I/O 2014. 2. 26. 13:25
1. 시스템 함수들
stat(), lstat(), fstat() 시스템 함수들을 정리한다.
이 함수들은 파일에 대한 유용한 정보를 추출해 준다.
2. 정의들
#include <sys/stat.h>
int stat(const char *pathname, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
위의 함수는 아래의 구조체를 통해서 정보를 리턴한다.
기본 구조는 stat()에서 출발하고 lstat() 함수는 파일이 심볼릭 링크인 경우를
fstat() 함수는 파일 경로 정보 대신 열린파일 디스크립터를 입력으로 받는 차이점이 있다.
struct stat{ dev_t st_dev; /* 파일이 위치한 디바이스 ID */ ino_t st_ino; /* 파일의 i-노드 수 */ mode_t st_mode; /* 파일 형식과 권한 */ nlink_t st_nlink; /* 파일의 (하드)링크 수 */ uid_t st_uid; /* 파일 소유자의 사용자 ID */ gid_t st_gid; /* 파일 소유자의 그룹 ID */ dev_t st_rdev; /* 파일 디바이스 특정 파일의 ID */ off_t st_size; /* 파일의 전체 크기 (BYTE) */ blksize_t st_blksize; /* I/O의 최적 블록크기 (바이트) */ blkcnt_t st_blocks; /* 할당된 블록의 수(512B) */ time_t st_atime; /* 마지막 파일 접근 시간 */ time_t st_mtime; /* 마지막 파일 수정 시간 */ time_t st_ctime; /* 마지막 상태 변경 시간 */ };
3. struct stat 구조체로부터 정보 추출
3.1. dev_t st_dev 정보
디바이스의 major/minor가 합쳐져 있는 구조로 major()/minor()함수를 통해서 각각 원하는 값을 추출할 수 있다.
3.2. mode_t st_mode 정보 (가장 복잡하다)
st_mode와 아래 매크로와 AND 연산을 수행하여 추출한다.
[파일 종류 검사 매크로]
상수 |
테스트 매크로 |
설명 |
S_IFREG |
S_ISREG() |
일반파일 검사 |
S_IFDIR |
S_ISDIR() |
디렉토리 검사 |
S_IFCHR |
S_ISCHR() |
문자 디바이스 검사 |
S_IFBLK |
S_ISBLK() |
블록 디바이스 검사 |
S_IFIFO |
S_ISFIFO() |
FIFO나 파이프 검사 |
S_IFSOCK |
S_ISSOCK() |
소켓 검사 |
S_IFLNK |
S_ISLNK() |
심볼릭 링크 파일 검사 |
[파일 권한 비트 검사] {set-user-ID,set_group-ID.Sticky}/소유자/그룹/기타
상수 |
8진수 |
권한비트 |
S_ISUID |
04000 |
set-user-ID |
S_ISGID |
02000 |
set-group-ID |
S_ISVTX |
01000 |
스티키 |
S_IRUSR |
0400 |
사용자-읽기 |
S_IWUSR |
0200 |
사용자-쓰기 |
S_IXUSR |
0100 |
사용자-실행 |
S_IRGRP |
040 |
그룹-읽기 |
S_IWGRP |
020 |
그룹-쓰기 |
S_IXGRP |
010 |
그룹-실행 |
S_IROTH |
04 |
기타-읽기 |
S_IWOTH |
02 |
기타-쓰기 |
S_IXOTH |
01 |
기타-실행 |
추가적으로 사용자/그룹/기타 각각에 대해서 모든 권한이 설정되었는지 여부를 검사하는 아래 매크로도 있다.
S_IRWXU(0700), S_IRWXG(070), S_IRWXO(07)
4. 예제
/*
* 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 <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
int main(int argc, char *argv[])
{
time_t t;
struct stat statbuf;
if(stat("./main.c", &statbuf) == -1)
{
perror("fopen");
exit(EXIT_FAILURE);
}
// major/minor number print
printf("major = %ld\n", major(statbuf.st_dev));
printf("minor = %ld\n", minor(statbuf.st_dev));
// file total size print
printf("file size = %ld \n", statbuf.st_size);
// file modify time print
t = statbuf.st_mtime;
printf("%s\n", ctime(&t));
// file mode check, the macro is defined to <sys/stat.h>
//if((statbuf.st_mode & S_IFMT) == S_IFREG)
if(S_ISREG(statbuf.st_mode))
printf("regular file\n");
exit(EXIT_SUCCESS);
}
System Program/process 2014. 2. 24. 11:44
1. fork() 함수
#include <unistd.h>
pid_t fork(void);
부모프로세스에서 호출 성공 시 : 생성된 자식프로세스의 PID를 리턴, 에러 발생 시 '-1'을 리턴
새로 생성된 자식 프로세스에서 호출 시 : '0'을 리턴
부모 프로세스와 거의 동일한 복제된 프로세스를 생성한다.
자식프로세스는 부모의 스택, 테이터, 힙, 텍스트 세그먼트를 복제한다.
2. fork() 함수 사용 시 유의 사항
1) 열린 파일 디스크립터가 부모 프로세스와 동일한 값으로 복제된다.
fork()함수 복제 후 파일 처리 시에 주의를 기울여야 하며, close(fp)/dup() 함수를 사용하여 문제가 없도록 하여야 한다.
2) fork() 함수 복제 후, 부모와 자식간에 경쟁 상태 진입 시 제어가 필요할 경우는 각가지 동기화 기법을 통해서
문제가 없도록 하여야 한다.
3) 자식 프로세스 실행 시점은 fork() 함수가 성공적으로 리턴 후부터이다.
3. 예제
간단히 fork() 함수의 사용 예 정도로 예제를 마무리 한다.
/*
* 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 <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
int main(int argc, char *argv[])
{
pid_t pid, wpid;
int status;
pid = fork();
switch(pid)
{
case -1:
_exit(EXIT_FAILURE);
case 0 : // child process
printf("child process call fork() fucntion \n");
_exit(EXIT_SUCCESS);
default: // parent process
wait(&status);
printf("child process exit status = %d \n", WEXITSTATUS(status));
break;
}
exit(EXIT_SUCCESS);
}
System Program/time 2014. 2. 24. 08:35
1. 개념
Timer 및 Alarm은 특정 시간에 Process에게 어떤 Job을 수행하도록 Schedule을 한다.
여기서는 전통적인 시스템 콜 함수에 대해서 알아본다.
2. Timer Interfaces
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
1) which 설정 가능 값
- ITIMER_REAL : 실제 시간 (벽시계 개념)
- ITIMER_VIRTUAL
- ITIMER_PROF
2) struct itimerval 구조체
{
struct timeval it_interval;
struct timeval it_value;
}
성공하면 '0'을 리턴, 에러가 발생하면 '-1'을 리턴
2.1. 일회성 Timer 설정법
new_value에서 struct timeval it_interval 값이 '0'으로 설정
2.2. 주기적 Timer 설정
new_value에서 struct timeval it_interval 값에 어떤 주기로 반복할 것인지에 대한 초 값을 적어준다.
new_value에서 struct it_value는 timer에 대한 expire되어야 할 시간값을 설정한다.
2.3. 일회성 Timer 설정에 대한 다른 방법
아래와 같이 간단한 함수를 사용하면 위의 setitmer를 흉내낼 수 있다. 일회성 Timer인 경우는 이것이 더 쉽다.
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
항상 성공, 이전에 남은 시간값을 리턴
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 <errno.h>
#include <sys/time.h>
#include <time.h>
static void signal_handler(int sig)
{
printf("%s : signal received \n", strsignal(sig));
}
int main(int argc, char *argv[])
{
int retval = -1;
struct sigaction sa;
struct itimerval itv = {0};
struct itimerval curitv = {0};
itv.it_interval.tv_sec = 2;
itv.it_value.tv_sec = 2;
sigemptyset(&sa.sa_mask);
sa.sa_handler = signal_handler;
//sa.sa_flags = SA_RESETHAND;
if(sigaction(SIGALRM, &sa, NULL) == -1)
perror("sigaction");
printf("START Timer \n");
setitimer(ITIMER_REAL, &itv, NULL);
//alarm(2);
while(1)
{
if(getitimer(ITIMER_REAL, &curitv) == -1)
{
perror("getitimer");
break;
}
printf("Remained secs : %ld\n", curitv.it_value.tv_sec);
sleep(1);
}
printf("End Timer \n");
return 0;
}
위 예제에서 Timer가 설정값에 도달하여 Event가 발생하면 기본 동작은 프로세서 종료이다.
위의 예제는 시그널 핸들러를 설치하여 시그널을 가로챘으며 SA_RESETHAND(SA_ONESHOT)를 설정하면
주기적 Timer에서 두 번째 시그널에 대해서 기본값으로 동작하여 프로세서가 종료한다.
같이 동작이지만 주석처린된 alarm(2)함수를 사용하면 동작은 같다.
위의 예제에서 itv.it_interval.tv_sec = 2로 설정하는 부분이 없으면 일회성 Timer로 동작한다.
4. 결론
일반적으로 Timer의 구현은 어떤 식으로 할 것인가?
위의 함수를 사용할까?
Timer를 처리하는 task를 통해서 모든 처리를 수행하고 Queu에 넣어서 관리 추적하는 기법도 있을 수 있다.
System Program/time 2014. 2. 21. 15:31
1. 리눅스 시간
리눅스는 초기 기원값에서 얼마만큼 시간이 흘렀는지를 나타내는 지표로 시간을 관리한다.
여기서 초기 기원값은 1970년 1월 1일 0시를 말한다.
이 시간은 time_t라는 데이터형을 통해서 얻거나 관리할 수 있다.
리눅스에서는 time_t를 얻기위해서 아래와 같은 시스템 호출 함수를 지원한다.
1.1. time() 시스템 함수
#include <time.h>
time_t time(time_t *timep);
기원 이래의 초 수를 리턴한다. 에러가 발생하면 (time_t) -1을 리턴한다.
time()함수는 time_t를 두 군데에서 리턴하므로 에러 확인은 유효하지 않는 저장소를 참조할 때 발생할 수 있는 errno에
대한 EFAULT값만 확인한다.
1.2. gettimeofday() 시스템 함수
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
1)
struct timeval {
time_t tv_sec; /* UTC 1970-1-1, 0시 이래의 초 */
suseconds_t tv_usec; /* 추가적인 마이크로 초(long int) */
} ;
2)
struct timezone *tz = NULL 로 할당하면 된다.
성공하면 '0'을 리턴, 에러가 발생하면 '-1'을 리턴
1.3. ctime() 함수
사용자가 읽기 편한 구조로 변환하는 함수이다.
#include <time.h>
char *ctime(const time_t *timep);
성공하면 '\0'으로 끝나는 정적 문자열 리턴, 에러가 발생하면 NULL을 리턴
2. 위의 함수 사용 예시
*
* 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 <errno.h>
#include <sys/time.h>
#include <time.h>
int main(int argc, char *argv[])
{
struct timeval tv;
time_t t;
if(gettimeofday(&tv, NULL) == -1)
perror("gettimeofday\n");
printf("gettimeofday = %ld, %ld\n", tv.tv_sec, tv.tv_usec);
t = time(NULL);
if(errno == EFAULT)
perror("time\n");
printf("time = %ld\n", t);
printf("current time : %s", ctime(&t));
printf("current time : %s", ctime(&tv.tv_sec));
return 0;
}
3. 결론
여기서는 time_t 가 무엇이고, 이를 얻기 위해서 사용되는 함수들을 소개하고 사용법에 대해서 고찰해 보았다.
System Program/time 2014. 2. 21. 15:23
1. 시간에 대해 더 고려해야할 사항
1장에서 time_t로 기원시간으로부터 현재까지 elapse된 초를 얻었다.
그리고 ctime()함수를 통해서 일반적인 달력 시간 표현으로 시간을 단순히 표시하였다.
하지만 실제 개발과정에서는 SDK로 구현된 App.과 Embedded 시스템간의 시간을 맞추는 것이 정확한
개념이 없으면 문제를 많이 일으킨다.
UTC시간과 로컬 시간이라는 개념에 대해서 정확히 이해할 필요가 있다.
여기서는 단순한 달력 시간 표시에서 한발 더 나아가 위의 두 시간의 개념과 관련 시스템 함수에 대해서 알아본다.
2. UTC 시간
영국 그리니치 표준이다. 다른 말로는 "GMT+0"시간으로 어디 어느 나라에서도 동일한 시간이다.
3. 로컬 시간
로컬 시간은 현재 시스템이 존재하는 곳의 시간을 의미한다.
전 세계의 시간을 통일된 체계로 관리하기 위해서 영국 그리니치 천문대를 기준으로 zone을 나눈다.
이것을 timezone이라고 한다. timezone은 아래 URL을 참고하기 바람.
예로 한국은 그리니치 천문대를 기준으로 동쪽 방향으로 9번째 Timezone에 위치한다.
따라서 다른 표기로는 "GMT+9"에 해당된다.
[참조] http://www.timeanddate.com/time/map/
4. GMT 및 Local Time System Call 함수
#include <time.h>
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
성공하면 "\n"과 '\0'으로 끝나는 정적으로 할당된 문자열의 포인터를 리턴, 에러면 NULL을 리턴
여기서 struct tm이라는 새로운 구조체가 등장한다. 시간을 표현하는 다른 구조체이다.
4.1. struct tm 시간을 time_t로 변환하는 함수
#include <time.h>
time_t mktime(struct tm *timeptr);
에러가 발생하면 (time_t) '-1'을 리턴, 성공하면 초
4.2. struct tm을 문자열로 변환하는 함수
#include <time.h>
char *asctime(const struct tm *timeptr);
성공하면 "\n"과 '\0'으로 끝나는 정적으로 할당된 문자열의 포인터를 리턴, 에러면 NULL을 리턴
5. 예제
/*
* 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 <errno.h>
#include <sys/time.h>
#include <time.h>
int main(int argc, char *argv[])
{
struct tm *t;
time_t tt;
tt = time(NULL);
if(errno == EFAULT)
perror("time\n");
// GMT Time
t = gmtime(&tt);
printf("gmt time : %s", asctime(t));
// Local Time
t = localtime(&tt);
printf("local time : %s", asctime(t));
return 0;
}
6. 정리
시간 처리 함수가 너무 많아서 정말 혼동스럽다. 하지만 다음과 같이 정리하면 되지 않을까 한다.
리눅스의 시간은 1970년 1월 1일 0시를 기준으로 현재까지 지난 초수를 기반으로 하기 때문에 time_t가 기준이다.
결국 이 초수는 달력 시간으로 표시하여 일반적으로 통용되는 시간으로 변환하기 위해서 사용하는 함수가 존재한다.
또한 필요에 따라서 표준시간이나 로컬 시간을 출력할 필요가 있고 이 때는 struct tm이라는 구조체를 사용하므로
기존 time_t와 이들 간에 변환을 위한 함수들이 필요하게 된다.
여기서 소개한 함수 말고도 더 정교한 출력을 위해서 strftime() 또는 currTime()등의 함수도 있다.
하지만 embedded 개발자인 나로써는 큰 의미가 없어 보인다.
System Program/I/O 2014. 2. 21. 10:34
1. Usage
fcntl() 시스템 호출은 열린 파일 상태 플래그 변경, 파일 디스크립터 복제 및 접근 모드 제어등 다양한 기능을 수행한다.
2. 함수 정의
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
성공할 경우 리턴값은 cmd에 따라 다르다. 에러는 '-1'을 리턴
3. 열린 파일 상태 검사 예시
int flags;
flags = fcntl(fd, F_GETFL);
if(flags == -1)
perror("fcntl");
if(flags & O_SYNC)
printf("writes are synchronized\n");
동기화된 쓰기용으로 파일이 열린 것인지를 검사한다. flags을 얻어오기 위해서 "F_GETFL"을 사용한다.
4. 접근 모드 상태 검사 예시
accessMode = flags & O_ACCMODE;
if(accessMode == O_WRONLY || accessMode == O_RDWR)
printf("file is writable\n");
접근 모드을 검사하는 것은 별도의 추가적으로 O_ACCMODE라는 flag에 대해서 비트 연산을 수행해야 한다.
5. 일반적인 사용 예시
int flags;
flags = fcntl(fd, F_GETFL);
if(flags == -1)
perror("fcntl");
flags |= O_APPEND;
if(fcntl(fd, F_SETFL, flags) == -1)
perror("fcntl");
O_APPEND 상태 플래그를 추가하고자 할 경우, 기존에 값을 열어서 추가적으로 설정하는 것이 일반적인 방법이다.
설정 시에는 "F_SETFL"을 사용한다.
System Program/signal 2014. 2. 19. 13:43
프로세스에 가해지는 소프트웨어적인 인터럽트라고 이해하면 쉽다.
1. 시그널의 범주
전통적 또는 표준 시그널이라는 두 가지의 큰 범주로 나누어진다. 리눅스에서 표준 시그널은 1 ~ 31까지의 범위이다.
그 이외의 범위는 실시간 시그널에 해당된다.
2. 기본 동작
시그널이 프로세스에 전달되면 다음 중 하나의 기본 동작을 수행한다.
- 시그널이 무시된다. 커널에 의해 버려지고 프로세스에는 아무런 영향이 없다.
- 프로세스를 종료한다. 비 정상적 프로세스 종료라고 한다.
- 코어 덤프 파일을 생성하고 프로세스를 종료한다.
- 프로세스를 멈춘다.
- 이전에 멈췄던 프로세스의 실행을 재개한다.
3. 속성 변경
시그널의 기본 동작을 변경할 수 있는데 이를 속성 변경이라고 하며 아래 중 하나로 변경할 수 있다.
- 기본 동작 수행 (이전에 변경된 속성을 원래되로 돌리는 경우를 말함)
- 시그널을 무시한다. (종료가 기본 동작인 시그널에 유용하다)
- 시그널 핸들러를 실행한다.
* 시그널 핸들러에서 기본동작 수행과 시그널 무시는 핸들러 함수 대신에 각각 SIG_DFL과 SIG_IGN으로 핸들러 함수를 대신할 수 있다.
4. 속성 변경 예시
/*
* 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 <errno.h>
#include <signal.h>
void signal_handler(int sig)
{
if(sig == SIGINT)
printf("SIGINT is tranfered \n");
if(sig == SIGQUIT)
{
printf("SIGQUIT is tranfered \n");
exit(1);
}
}
int main(int argc, char *argv[])
{
if(signal(SIGINT, signal_handler) == SIG_ERR)
{
printf("signal error\n");
exit(1);
}
if(signal(SIGQUIT, signal_handler) == SIG_ERR)
//if(signal(SIGQUIT, SIG_DFL) == SIG_ERR)
{
printf("signal error\n");
exit(1);
}
//while(getchar() != 'q');
for(;;)
{
printf("main function is paused, PID = %d \n", getpid());
pause();
}
return 0;
}
[설명]
SIGINT과 SIGQUIT의 기본 동작을 별도의 시그널 핸들러 함수를 통하여 변경하였다.
SIGQUIT의 기본 동작은 비 정상적인 프로세스 종료를 유도하고 디버깅을 위한 코어덤프를 수행하는 것이다.
이 예제에서는 비 정상적인 종료라는 기본동작을 변경하여 정상적인 종료 동작을 하도록 강제로 변경하였다.
예제로써 적절한 것 같지는 않지만 동작성을 읽히는데 포커스를 둔다.
|