개요
유닉스 계열(리눅스) 시스템의 시간을 설정/조회할 필요가 있어 정리하여 보았습니다.
*시스템의 시간을 설정할 수 있는 시스템 관리자(root) 권한에서 실행해야 실제로 시스템에 시간을 설정할 수 있습니다.
참고
- time(7) - Linux Programmer’s Manual
- Time Basics (The GNU C Library)
- Linux Programmer’s Manual - CTIME(3)
- IBM Knowledge Center - mktime() – 로컬 시간 변환
- C Date time tutorial
용어 정의
시간은 크게 두 가지 범주로 표현을 할 수 있습니다.
달력시간(Calendar time)
연속된 시간에서 한 지점을 의미합니다. 절대시간(absolute time) 이라고 부르기도 합니다.
상대 시간(Relative time)
특정 시간 지점을 기준으로 얼마나 많은 시간이 경과되었는지를 나타내는 방식으로 시간을 표기하는 방법을 상대시간 이라고 부릅니다.
이러한 시간 표현법으로 표시하는 시간은 아래와 같습니다. 리눅스 시스템에서는 아래의 시간 자원들이 관리됩니다.
벽시계 시간(실시간)
현실세계의 진짜 시간과 날짜입니다. 즉, 벽에 있는 시계에서 여러분이 읽게 되는 그 시간을 의미합니다. 리눅스의 프로세스들은 사용자에게 시간을 입력받거나 표시할 때, 혹은 시스템에서 발생한 이벤트의 시간을 기록할 때 (timestamping) 이 시간을 사용합니다.
프로세스 시간
프로세스가 실행되기 위하여 프로세서(CPU) 를 사용한 시간을 의미합니다. 이것은 프로세스 자체의 실행(user time) 이나 커널이 프로세스를 대신하여 작업한 시간(system time)으로 측정합니다. 프로세스는 이 시간을 프로파일링, 감사, 통계적인 목적에서 사용합니다. 예를 들어 어떠한 알고리즘을 끝마치는데 얼마나 걸리는지 측정하는데 사용합니다. 벽시계 시간을 이러한 목적으로 사용할 경우, 잘못된 결과를 얻게 됩니다. 리눅스의 일반적인 멀티 태스킹 특성 때문에 벽시계 시간은 일반적으로 프로세스 타임보다 크게 표시됩니다. 반대로, 멀티 프로세서와 스레드 프로세스에서는 실제 프로세스 시간이 주어진 작업에 대한 벽시계 시간을 초과 할 수 있습니다!
모노토닉(Monotonic time)
이 시간자원은 엄격히 선형적으로 증가합니다. 리눅스를 포함한 대부분의 운영체제는 시스템의 가동시간 (부팅 이후)을 이 목적으로 사용합니다. 벽시게 식나은 변경할 수 있습니다. - 예를 들어, 사용자가 설정할 수도 있으며 시스템이 클럭 스큐(clock skew) 를 끊임없이 보정합니다 - 그리고 추가로 소위 윤 초라고 부르는 요소로부터 도입된 추가적인 부정확성도 있을 수 있습니다. 반면에 이 모노토닉 타임은 표시된 시간을 변경할 수 없습니다. 이 점이 모노토닉 탐임의 중요한 특성이며 이로 인하여 시간자원이 엄격히 선형적으로 증가하는 것을 보장할 수 있습니다. 이 시간자원은 두개의 샘플링 사이의 차이를 계산하는데 유용합니다.
유닉스 시간(Unix time)
유닉스 시간이란 유닉스 에포크(Unix epoch, 1970년 1월 1일 00:00:00)로부터 경과한 시간을 초로 환산하여 정수로 나타낸 것입니다.
분할시간(Broken-down time)
달력시간은 유닉스 시간과 같이 특정시간을 기준으로 경과된 값으로 표현할 수 있습니다. 이러한 시간 표현법은 계산에 사용하기 편하다는 장점이 있습니다. 하지만 사람들이 일반적으로 생각하는 달력시간(년/월/일/시/분/초 등의 정보가 포함된 시간)으로 쉽게 환산하여 생각하기 힘듭니다. 그래서 이러한 경과시간을 분할하여 사용자들이 읽기 쉽도록 정보를 제공한 표현법을 분할시간이라고 합니다.
유닉스 시간 조회/설정
아래의 코드는 시스템의 시간을 현재보다 1시간 뒤로 설정하는 코드입니다.
앞서 용어 정의에서 언급하였던 것처럼, 유닉스 시간은 유닉스 에포크 이후의 경과된 시간을 초단위로 환산하여 표현한 것으로 아래의 예 에서처럼 연산(1시간 후)을 쉽게 할 수 있습니다.
반면 현재 시간을 출력한 결과(_printf_문)는 1970.1.1 00:00:00 이후로 경과된 초이기 때문에 이를 통해 일상에서 사용하고 있는 날짜 및 시간의 값으로 즉시 환산이 되지 않습니다.
#include <stdio.h>
#include <time.h>
int main(void) {
time_t Now;
Now = time(NULL); // 현재시간을 얻어옵니다.
printf("[GetTime]%ld\n", Now);
Now += 3600; // 1시간 후
stime(&Now);
printf("[SetTime]%ld\n", Now);
Now = time(NULL); // 실제 반영되었는지 확인합니다.
printf("[GetTime]%ld\n", Now);
return 0;
}
위 예제코드는 초 단위로 시간을 설정할 수 있는데, 이보다 상세히 시스템의 시간을 설정할 이유가 있다면 아래와 같이 gettimeofday 와 settimeofday 를 사용합니다. sys/time.h 를 포함합니다.
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
int main(void) {
struct timeval stNow;
gettimeofday(&stNow, NULL); // 현재시간을 얻어옵니다.
printf("[GetTime]%ld:%ld\n", stNow.tv_sec, stNow.tv_usec);
stNow.tv_sec += 3600; // 1시간 후
settimeofday(&stNow, NULL);
printf("[SetTime]%ld:%ld\n", stNow.tv_sec, stNow.tv_usec);
return 0;
}
분할시간(Broken-down time)
분할시간이란 사람이 (쉽게) 인식할 수 있도록 표현한 시간을 말합니다. 리눅스에서 분할시간은 <time.h> 의 tm 구조체에 저장됩니다. 해당 구조체는 아래의 형태와 같습니다.
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
유닉스 시간을 사용자가 읽기 편한 분할시간 형식으로 변환하여 출력하였습니다.
#include <stdio.h>
#include <time.h>
int main(void) {
time_t Now = time(NULL);
struct tm *pstTimeInfo = localtime(&Now);
printf("[GetTime] %02d:%02d:%02d\n", pstTimeInfo->tm_hour,
pstTimeInfo->tm_min, pstTimeInfo->tm_sec);
return 0;
}
두 표현 형식 간의 변환
시간을 표현하는 두 가지 형식(경과시간, 분할시간)은 장단점으로 인하여 사용처가 분명하기 때문에 서로의 형식으로 변할 필요가 있습니다. 둘 간의 변환은 다음의 함수를 사용하여 진행합니다.
// Unix time(Elapsed time) -> Broken-down time
struct tm *localtime(const time_t *timep);
// broken-down time -> Unix Time(Elapsed time)
time_t mktime(struct tm *tm);
사용예제는 다음과 같습니다.
#include <stdio.h>
#include <time.h>
int main(void)
{
time_t Now, Get;
struct tm *pstTimeInfo;
Now = time(NULL); // 현재시간 (elapsed_time, Unix Time)
pstTimeInfo = localtime(&Now); // Unix Time -> BrokenDown Time
printf("[GetTime] %02d:%02d:%02d\n", pstTimeInfo->tm_hour,
pstTimeInfo->tm_min, pstTimeInfo->tm_sec);
Get = mktime(pstTimeInfo); // BrokenDown Time -> Unix Time
printf("[GetTime]%ld %ld\n", Now, Get);
return 0;
}
사용자가 입력한 시간을 바탕으로 시스템 시간을 변경하여 보도록 하겠습니다.
간단한 구현을 위하여 사용자의 입력은 _hh:mm:ss _ 형식의 문자열로 가정하도록 합니다.
예를 들면 2시 10분 2초는 14:10:02 라고 입력합니다.
아래와 같이 프로그램을 실행할 것을 예상합니다.
$ ./a.out 14:10:02
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
int main(int argc, char* argv[]) {
struct tm *tm;
time_t Now;
char* buf[3];
int hour, min, sec;
// argv <= hh:mm:ss
strncpy(buf,
&argv[1][0],
2);
hour = atoi(buf);
strncpy(buf,
&argv[1][3],
2);
min = atoi(buf);
strncpy(buf,
&argv[1][6],
2);
sec = atoi(buf);
Now = time(NULL);
struct tm* pstTm = localtime(&Now);
struct timeval stTimeValue;
if (pstTm) {
pstTm->tm_hour = hour;
pstTm->tm_min = min;
pstTm->tm_sec = sec;
stTimeValue = {mktime(pstTm), 0};
settimeofday(&stTimeValue, 0);
}
}
'운영체제' 카테고리의 다른 글
Node.js 오류 @ WSL (0) | 2020.03.09 |
---|---|
Package Offline 설치 (0) | 2020.02.13 |
(리눅스) 네트워크 카드에 다중 IP 설정 (0) | 2019.08.18 |
리눅스 어플리케이션 개발을 위한 WSL 설치 및 VS Code 연동 (0) | 2019.06.30 |
유닉스(리눅스) - man 페이지 (0) | 2018.09.20 |