일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 리커버릿
- 지훈현서
- GEF
- 실시간로깅
- vhdx 파일 복구
- DYNAMIC Section
- pwntools
- OpenAI 개발
- docker
- python3.11 pip
- c++
- wsl2 복구
- VSCode C++ 표준 버전 수정
- pip 에러 해결
- vmware 반응 속도
- pwn
- std::cerr
- 공유 라이브러리는 왜 항상 같은 순서로 맵핑 될까?
- GDB
- Recoverit
- Python3
- z3 signed 이슈
- 개발
- Windows 부팅 오류
- 임베디드 시스템 해킹
- python
- Seccomp bypass
- tool
- Python.h: No such file or directory
- python3.11 설치
- Today
- Total
OZ1NG의 뽀나블(Pwnable)
[Tips][C++][개발] 실시간 로깅 구현 시, std::cerr 대신 fprintf를 사용하면 더 좋은 이유 본문
[개요]
C++에서는 출력 스트림으로 stdout에 대응하는 std::cout가 존재하듯, stderr에 대응하는 std::cerr가 존재한다.
하지만, std::cerr를 실시간 로깅에 사용하는 경우, 특히 멀티 프로세싱 또는 멀티 스레딩 프로그램 개발에서의 실시간 로깅에 사용하는 경우 다음과 같은 문제가 발생할 수 있다.
[문제 코드]
<Child Process 1>
std::cerr << "hello" << "world!" << std::endl;
<Child Process 2>
std::cerr << "HELLO" << "WORLD!" << std::endl;
예를들어 멀티 프로세싱으로 구동되는 프로그램이고, 서로 다른 자식프로세스에서 위와 같은 로깅 문구를 거의 동시에 출력한다고 가정해보자.
그 전에 먼저 std::cerr의 출력 방식을 간단하게 설명하고 넘어가도록 하겠다.
std::cerr의 `<<` 연산자로 어떠한 데이터를 출력한다는 것은, stderr 스트림을 사용해서 `<<` 연산자 다음 데이터를 출력하겠다는 뜻이다. 그런데 이때의 의미는 std::endl을 만날 때까지 stderr 버퍼에 문구를 채우고 한번에 출력을 하겠다는 것보다는, 그냥 `<<` 연산자 다음에 위치한 데이터를 '일단' 출력하겠다는 것에 더 가깝다.
그래서 [문제 코드]와 같이 std::cerr가 동시에 사용된다면, 다음과 같이 섞여서 출력 될 수 있다.
[출력 예시]
helloHelloworld!World!`\n` // std::endl
`\n` // std::endl
위 문제 코드야 뭐 문자열이기 때문에 좀 어지러워도 이해를 못할 정도는 아니지만, 다음과 같이 std::cerr를 통해 어떤 실시간 숫자 데이터를 출력한다고 생각해보면 얼마나 어지럽게 출력이 될지 가늠조차하기 어려워진다.
[어지럼증 유발 출력 예시]
[예시 코드]
<Child Process 1>
int data1 = 12;
std::cerr << data1 << std::endl;
<Child Process 2>
int datd2 = 34;
std::cerr << data2 << std::endl;
[동시 출력 결과 예시1]
3412
[동시 출력 결과 예시2]
1234
data1과 data2와 같이 중요한 데이터를 로깅해야하는데, 어떨때는 예시1처럼 기록되고 어떨때는 예시2처럼 기록된다면, 사실상 로깅을 하는 의미가 없을 정도로 로깅된 데이터를 이해하기 쉽지 않을 것이다.
[해결책]
그렇다면 이런 문제를 어떻게 해결할 수 있을까?
바로 근.본 C로 돌아가 fprintf(stderr, ...)로 로깅을 시도하는 것이다.
[수정된 코드]
<Child Process 1>
std::cerr << "hello" << "world!" << std::endl;
-->
fprintf(stderr, "hello world!\n");
<Child Process 2>
std::cerr << "HELLO" << "WORLD!" << std::endl;
-->
fprintf(stderr, "HELLO WORLD!\n");
[출력 예시]
hello world!
HELLO WORLD!
fprintf는 서식문자를 통해 출력할 데이터를 문자열화 한 뒤, 버퍼에 모으고, 한번에 출력을 하는 특징을 가지고 있다.
때문에 std::cerr를 사용할 때의 문제점을 해결 할 수 있다!
[결론]
따라서 이러한 이유 때문에 멀티 프로세싱 또는 멀티 스레딩 환경에서 실시간 로깅을 할때는 편리한 std::cerr 보다는 조금 귀찮더라도 fprintf를 사용하는 것을 추천한다.
'Tips' 카테고리의 다른 글
[Tips] VSCode C++ 표준 버전 수정 (0) | 2023.09.11 |
---|---|
[Tips][OpenAI]Embedding으로 부족한 토큰 수를 매꿔보자 (부제: 어떻게 pdfGPT, ChatGPT는 많은 데이터를 기억할 수 있을까?) (0) | 2023.04.27 |
[Tips] Windows 10/11 MBR 날라갔을 때(에러코드: 0xc000000f) 데이터 복구 방법 (6) | 2023.04.21 |
[Tips] strace attach (0) | 2023.02.20 |
[Tips][CS] Linux(ELF) - 메모리에 맵핑된 공유 라이브러리는 왜 항상 같은 순서로 맵핑 될까? (2) | 2023.01.10 |