linux localtime 함수 오류?? (c/c++)

jinghwa의 이미지

로그파일을 일자별로 생성하고 있습니다.
아래는 로그 파일을 생성하는 함수인데요..

프로세스에서 MakeLog 함수 호출시 일자별로 ( 프로세스_날짜.log ) 생성되는 로그파일과 전체로그(프로세스.log) 를 둘다 write 하고 있습니다.
시스템에 총 6개의 프로세스가 떠있고 각각 thread 를 실행하는 구조이고요 이모든 프로세스에서 MakeLog를 통해 로그 파일을 생성합니다.

이상한 점은 전날 로그에다가 로그를 기록해요..
즉 오늘이 4/3일인데 4/2일 로그에 로그를 기록합니다. 매일 그런건 아니고요..
호출이 많은 경우인데 .. MakeLog 함수를 초당 24~100번 정도 호출한 경우 간헐적으로 발생합니다.

리눅스(redhat 7.2) 에서 localtime에 의한 날짜 계산이 잘못될 수도 있나요??
또는 File open을 많이 할 경우 FILE *fp 가 이전 날짜의 pointer 로 재할당 되거나 .. 그럴수도 있을까요?
fclose가 제대로 안되거나요.. ㅠㅠ

정말 단순한 소스인데 .. 왜 이런지 모르겠어어요

void MakeLog(int flag, const char *fmt, ...)
{
FILE *fp;
FILE *fp1;

struct tm *Time;
time_t time_ty;
time(&time_ty);
Time = localtime(&time_ty);

sprintf(tmm, "%02d:%02d:%02d", Time->tm_hour, Time->tm_min, Time->tm_sec);
sprintf(date, "%04d%02d%02d", Time->tm_year + 1900, Time->tm_mon + 1, Time->tm_mday) ;

sprintf(logfile, "%s/%s_%s.log", LogDir, LogFile, date);
sprintf(todaylogfile,"%s/%s.log",LogDir, LogFile);

//프로세스명.log 파일에 추가
fp1 = fopen(todaylogfile, "a");

if(fp1 == NULL)
{
printf("\n *************** logfile = [%s]", todaylogfile);
return;
}
fprintf(fp1, "[%s]:%s", tmm, tbuf);
fclose(fp1);

//프로세스명_날짜.log 파일에 추가
fp = fopen(logfile, "a");

if(fp == NULL)
{
printf("\n *************** logfile = [%s]", logfile);
return;
}
fprintf(fp, "[%s]:%s", tmm, tbuf);
fclose(fp);

memset((char *)&fmt, 0x00, sizeof(fmt));
memset((char *)&ap, 0x00, sizeof(ap));
return;
}

ktd2004의 이미지

localtime_r 함수를 사용해 보시면 어떨까요?

jinghwa의 이미지

localtime_r 은 localtime과 어떤 차이가 있을까요? 또 inux에서도 사용가능할까요?

ktd2004의 이미지

linux에서도 사용이 가능하네요.

* http://man7.org/linux/man-pages/man3/localtime.3p.html

localtime_r은 재진입 가능한 버전입니다.

pynoos의 이미지

localtime_r 을 써야만하는 코드입니다.
그리고, 초당 24~100번의 file open을 한다면, 조금더 효율적으로 한 번 open한 로그파일은 재사용하는게 좋을 것 같습니다. 파일 명이라도 캐싱을 하고, 달라질 경우 open 하는 방식을 추천합니다.

또, 맨 마지막 memset은 뭔가 이상한데요? 굳이 fmt를 초기화할 이유가 없어 보입니다. 초기화 되는 크기도 이상하고요.

jinghwa의 이미지

localtime과 localtime_r의 차이점이 무엇인지요..
현재 localtime 으로 사용중인데 ..
어제 날짜를 가져오는 현상이 localtime 함수의 오류라고 할 수 있을까요?

anony999의 이미지

localtime() 은 MT-Unsafe 함수입니다.
multi-thread 에서 동시 호출할 경우, 정확한 동작을 보장할 수 없다는 뜻입니다.

본문에 6개의 프로스세가 각각 thread 를 호출한다고 했는데..
만약 하나의 프로세스 내에서, 두 개 이상의 thread 가 이 함수를 호출한다면..
예상치 못한 일이 일어날 수도 있겠죠.

그래서 Thread Safe 함수인 localtime_r() 로 바꾸시는 겁니다.

pynoos의 이미지

localtime의 결과로 넘어오는 struct tm 구조체는 메모리 할당되어 넘어오거나 thread local storage에 있는 것이 아니라 라이브러리 안에 있는 모든 thread들이 공유하는 것입니다. 그러면 어느 순간 tm 구조체 안의 값이 변경되는 중간에 다른 thread에서 값을 읽어 갈 수 있습니다. 특히나 multi processor 환경에서 thread가 돌아가는 경우는 발생할 가능성이 높아집니다.

(multi process를 multi processor로 수정합니다. 엄연히 다른 용어니까요. ^^)

pynoos의 이미지

bushi의 이미지

localtime_r() 은 문제의 본질이 아닌 것 같습니다.
난잡한 전역변수들이 먼저 해결이 되지 않는다면 localtime_r() 은 별 소용이 없고,
같은 파일에 대해 open/write 가 겹치는게 먼저 해결이 되지 않는다면 전역 변수들을 모두 없애도 별 소용이 없고요.

게다가... 제가 보기엔 localtime() 이 문제의 근원이라고 볼 근거도 전혀 없습니다.
입력으로 주는 time_t 가 일관성 없이 서로 무관한 다른 값으로 휙휙 바뀌지 않는한, localtime() 결과값이 저장되는 곳이 다른 쓰레드에 의해 아무리 overwrite 되어봤자 거기서 거기입니다.
설령 어떻게 묘하게 비트 단위 set/clear 가 섞였다고 가정해도, 겨우 날짜 정도만 굉장히 그럴싸하게 어제 날짜로 바뀔 확률은 그렇지 않을 확률에 비하면 0에 가깝습니다.


(추가했던 의사 코드는 문제 재현이 될 리가 없는 코드라 삭제했습니다.)

ymir의 이미지

위 bushi 님 말씀이 일리가 있는 것 같습니다.
MT-Unsafe 라고 하더라도, 내가 원하는 값이 아닌 다른 쓰레드에 의해 갱신된 값이 써질 가능성 뿐이고..
이는 결과에는 큰 영향을 미치지는 않을 것으로 보이는데요.
운이 나쁘면, 날짜가 바뀌는 시점에 어제 로그가 간발의 차이로 다음날 로그에 써질 미약한 가능성은 있겠죠.

다만, 코드가 중간 중간 삭제되어 전체를 알 수 없고, 주어진 코드 상에서는 별다른 문제를 찾기는 어려우니..
일단, 가장 쉽게 배제할 수 있는 문제부터 클리어 하게 해 놓고 나면..
이제 다른 부분에서 문제를 찾아 볼 수 있을 테니.. 아주 의미없는 것은 아닌 것 같습니다.

추가로, localtime() 이 잘못된(?) 값을 리턴했다면, 아예 time() 의 리턴값을 의심해 볼 필요가 있고..
time() 의 리턴값에 문제가 있다고 보면, 시스템의 clock 이나 ntp 같은 것을 의심해 볼 필요도 있을 것 같습니다.

그리고 &fmt 에 memset() 하는 것은 완전히 의미 없는 일이니 삭제하시는 게 낫겠고..
오늘 로그가 어떻게 해서 어제 로그 파일에 쓰였다는 것을 확인했는지 설명할 필요가 있을 것 같습니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

백연구원의 이미지

조금 이상하네요. 전 본문을 보고 localtime() 의 thread unsafe 문제는 아니라고 생각했거든요, 서로 다른 여섯개의 프로세스 안에 각각의 thread 라고 하면 한 개의 프로세스 내에서 MakeLog() 를 접근하는 thread 는 멀티가 아닌데요. 그리고 "오늘이 4/3일인데 4/2일 로그에 로그를 기록합니다. " 이 경우도 thread unsafe 문제라면 자정을 갓 넘기는 시점에만 문제가 발생할 것 같고요.

제가 질문을 잘못 이해한걸까요? 아무튼 위에 댓글들처럼 다른 여러가지 이유로 수정이 필요한 포인트는 있는 것 같습니다.


소곤소곤

pynoos의 이미지

코드를 다시 잘 보니 localtime 이 큰 문제를 일으킬 것 같진 않군요.

그 외에 다른 전역 변수들에서 문제가 생길 가능성이 더 커 보이네요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.