반응형
작성일: 2025년 2월 2일

 

 

아이뮤즈(iMUZ) 뮤펜 mu-PEN 태블릿 스타일러스 펜 - 사용자 가이드(매뉴얼)

제품명: 스타일러스 펜

모델명: IP-CT01C

 

 

 

반응형
작성일: 2025년 2월 2일

 

[ 앤커 사운드코어 ] 스페이스원 사용자 매뉴얼 (User Manual)

 

 

[ 앤커 사운드코어 ] 스페이스원 사용자 매뉴얼 (User Manual)

 

 

 

[ 앤커 사운드코어 ] 스페이스원 사용자 매뉴얼 (User Manual)

 

 

 

 

[ 앤커 사운드코어 ] 스페이스원 사용자 매뉴얼 (User Manual)

 

 

 

[ 앤커 사운드코어 ] 스페이스원 사용자 매뉴얼 (User Manual)

 

 

 

 

 

 

 

 

[ 앤커 사운드코어 ] 스페이스원 사용자 매뉴얼 (User Manual)

 

 

 

[ 앤커 사운드코어 ] 스페이스원 사용자 매뉴얼 (User Manual)

 

 

 

 

[ 앤커 사운드코어 ] 스페이스원 사용자 매뉴얼 (User Manual)

 

 

 

 

[ 앤커 사운드코어 ] 스페이스원 사용자 매뉴얼 (User Manual)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
작성일: 2025년 2월 18일

 

 

소스 코드 분석 도구를 분류해본다면, "유료" or "무료"가 아닐까?
(나의 개인적 의견임 ^^)
내가 "유료", "무료"를 분류한 이유는 "유료" 분석 도구가 어마어마하게 비싸서이다. ㅠㅠ
작은 회사가 구입해서 쓸 수 있는 소프트웨어가 아니다.
너무 비싸서, 대기업이나 되야 유료 소스 코드 분석 도구의 유용함을 체감할 수 있다.

 

 

Blackduck Coverity (유료)

Blackduck Coverity는 많이 비싸다.

"Coverity 제품을 한번 써볼까?" 이런 느낌으로 쓸 수 있는 수준의 가격이 아니다. ㅠㅠ

어쨌든, 나는 Blackduck Coverity 분석 도구에 대한 도입을 고려하고 Blackduck Coverity 공급사에 문의 중이니까 의미있는 문의 결과가 있으면 여기에 추가로 내용을 공유할 생각임.

  • 지금까지 알아본 Blackduck Coverity의 가격 정책은 이렇다.
    • 최소 사용자: 10인
    • 1년 단위로 사용료 지불
    • 부가세까지 포함하여 10-User & 1년 사용인 경우, '현대차 아이오닉5 정도의 가격'.
      (Blackduck 영업 비밀이라서 정확한 가격을 말할 수 없음)
      • 10명이 Software를 개발하는데, 매년 이 정도의 돈을 내야 한다는 뜻이다. ㅠㅠ
    • 3년 이상 다년 동안 계약하면, 할인이 있다.

'Blackduck Coverity' 공급사의 담당자와 미팅을 하면, 5일간 Demo를 할 수 있는 Trial License(Demo License)를 준다.

5일간 Source Code 분석해보고, 정말 쓸모가 있다면 도입을 결정하는 것을 추천한다.

 

 

CppCheck (무료)

CppCheck에 대한 자세한 설명은 아래 웹 문서를 참고:

https://cppcheck.sourceforge.io/

 

CppCheck 프로그램 설치하기

## Debian, Ubuntu
$ apt install cppcheck
## macOS
$ brew install cppcheck
## Fedora
$ yum install cppcheck

 

CppCheck 명령 실행하기

내가 제일 많이 사용하는 옵션은 아래와 같다.

현재 디렉토리에 있는 모든 파일을 분석할거라면, 아래의 옵션으로 실행하는 것을 추천한다. 

$ cppcheck --check-level=exhaustive \
--enable=warning,style,performance,portability,unusedFunction ./

 

 

 

SonarQube

## 작성 보류.

 

 

 

 

기타 참고하면 좋은 문서

C++ 정적 코드 분석하기 - clang-tidy, cppcheck, 컴파일 옵션

https://nx006.tistory.com/37

 

 

 

 

반응형
작성일: 2025년 1월 21일

 

C언어로 작성된 Signal Handler 내에서  "localtime()" 함수를 사용할 때, 주의할 점이 있다.

아래 예제 코드처럼 program 내에서 이미 localtime() 함수가 사용되고 있다면, Signal Handler 내부의 localtime() 함수로 인해서 호출되는  futex_wait() 함수 부분에서 Deadlock(데드락) 상태로 빠질 수 있다.

 

아래 예제 소스 코드를 컴파일하고, 테스트해보면 1~5초가 지날 때쯤 deadlock 상태가 된다.

일반적으로 multi thread 환경에서 mutex lock 때문에 deadlock이 되는 경우는 많지만, 아래 예제 코드처럼 multi thread가 아닌데 deadlock 상태가 되는 경우는 경험하기 쉽지 않다.

 

만약, 아래 예제 코드에서 signal handler를 제거한다면 deadlock 상황은 발생하지 않는다.

 

/****************************************************
* How to compile
* $ gcc -g main.c -o myapp
* How to test
* $ ./myapp
***************************************************/
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
static void sigchd_handler(int signo)
{
time_t tv;
tv = time(NULL);
struct tm mytime = *localtime(&tv); // NOTE: 이 부분에서 Deadlock 발생함.
char my_date[64];
sprintf(my_date, "%d-%02d-%02d %02d:%02d:%02d",
mytime.tm_year + 1900, mytime.tm_mon + 1, mytime.tm_mday,
mytime.tm_hour, mytime.tm_min, mytime.tm_sec);
printf("%s(%d) %s() time: %s\n", __FILE__, __LINE__, __func__, my_date);
fflush(NULL);
return ;
}
int register_signal_handler(void)
{
struct sigaction sa;
sa.sa_handler = sigchd_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGCHLD, &sa, NULL);
return __LINE__;
}
#define LOOP_TRUE 1
int main(void)
{
int ret;
int idx;
ret = register_signal_handler();
time_t tv;
while (LOOP_TRUE)
{
printf("My PID: %d\n", (int) getpid());
FILE *fp = popen("ls -al", "r");
tv = time(NULL);
struct tm mytime;
for (idx = 0; idx < 10000; idx++)
{
printf("localtime() Function Call Counter: %d \r", idx);
fflush(NULL);
mytime = *localtime(&tv); // NOTE: 이 부분에서 Deadlock 발생함.
}
printf("\n");
char my_date[64];
sprintf(my_date, "%d-%02d-%02d %02d:%02d:%02d",
mytime.tm_year + 1900, mytime.tm_mon + 1, mytime.tm_mday,
mytime.tm_hour, mytime.tm_min, mytime.tm_sec);
printf("%s(%d) %s() time: %s\n", __FILE__, __LINE__, __func__, my_date);
fflush(NULL);
pclose(fp);
sleep(1);
}
return 0;
}

 

 

위 예제 코드에서 signal handler 때문에 deadlock이 발생하는 이유는 무엇일까?

localtime() 함수의 구현 부분을 보면, futex_wait() 함수를 호출하는 부분이 있다. 이 futex_wait() 함수가 이미 호출된 상태인데 또 한번 futex_wait() 함수가 호출되면 deadlock 상태가 된다. 즉, localtime() 함수가 호출 중인 상태에서 또 한번 localtime() 함수가 호출되면 동일 lock 변수인 tzset_lock 변수를 통해서 lock 시도를 했는데, 또 tzset_lock 변수를 이용해서 lock 시도는 하는 셈이 되는 것이다.

결국 tzset_lock -> locking 그리고 tzset_lock -> locking  이런 식으로 연속으로 두번 locking 시도를 하면서 2번째 locking 시도는 영원히 locking하려고 대기하게 된다. 또한 첫번째 locking 시도는 두 번째의 locking 시도를 한 signal_handler가 종료가 되지 않으니까 영원히 signal_handler의 종료만 기다리며 tzset_lock이 unlocking 되지 않는 것이다.

 

 

Deadlock 상태에 빠지는 이유를 이해했다면, 실습해보자!

아래와 같이 Source code를 compile하고, 바이너리 코드를 실행해보자.

실행하고 대략 1~5초 후에 프로세스가 아무것도 출력하지 않고 멈춘 것처럼 보일 것이다.

이때가 동일한 tzset_lock 변수에 대해서 첫번째 locking -> 두번째 locking을 연달아 시도하면서 deadlock 상태에 빠진 것이다.

$ gcc -g main.c -o myapp
$ ./myapp
My PID: 255676
main.c(25) sigchd_handler() time: 2025-01-21 07:54:58
localtime() Function Call Counter: 9999
main.c(72) main() time: 2025-01-21 07:54:58
My PID: 255676
localtime() Function Call Counter: 382 <-- 여기서 Deadlock 상태에 빠짐

 

 

머리로 이해한 것을 Process 내부의 function call stack을 통해서 확인해보자!

Deadlock 상태에 빠졌을 때, 아래와 같이 gdb로 function call stack을 확인해보면

  1. main()함수는 localtime()를 호출하면서 tzset_lock 변수를 통해 locking 시도를 하고 있었고,
  2. 이런 와중에 popen("ls -al", "r") 함수를 통해 실행되었던 child process가 종료되면서,
  3. Parent Process인 "myapp"이 SIGCHLD를 수신하게 된다.
  4. 이때 signal handler인 "sigchd_handler"가 실행되면서 localtime() 함수를 한번 더 실행하게 된다.
  5. 짠~!  main() 함수가 아직 localtime()함수를 통해 tzset_lock을 unlock하지 않았는데, signal handler의 localtime() 함수가 tzset_lock을 locking 시도를 했기 때문에 이때부터 dealock 상태가 되는 것이다.

 

$ gdb ./myapp 255676
(gdb) where
#0 futex_wait (private=0, expected=2, futex_word=0x7fa99d11c760 <tzset_lock>) at ../sysdeps/nptl/futex-internal.h:146
#1 __GI___lll_lock_wait_private (futex=futex@entry=0x7fa99d11c760 <tzset_lock>) at ./nptl/lowlevellock.c:34
#2 0x00007fa99cfd6744 in __tz_convert (timer=1737446099, use_localtime=1, tp=0x7fa99d11c6a0 <_tmbuf>) at ./time/tzset.c:572
#3 0x0000564ee1a0a30e in sigchd_handler (signo=17) at main.c:20
#4 <signal handler called>
#5 __GI___fstatat64 (fd=-100, file=0x7fa99d0d4b02 "/etc/localtime", buf=0x7ffdd9ca4780, flag=0)
at ../sysdeps/unix/sysv/linux/fstatat64.c:166
#6 0x00007fa99cfd683c in __tzfile_read (file=file@entry=0x7fa99d0d4b02 "/etc/localtime", extra=extra@entry=0,
extrap=extrap@entry=0x0) at ./time/tzfile.c:155
#7 0x00007fa99cfd5c24 in tzset_internal (always=<optimized out>) at ./time/tzset.c:405
#8 0x00007fa99cfd65a7 in __tz_convert (timer=1737446099, use_localtime=1, tp=0x7fa99d11c6a0 <_tmbuf>) at ./time/tzset.c:577
#9 0x0000564ee1a0a528 in main () at main.c:65
(gdb)

 

 

그러면, signal handler 내부의 localtime() 함수 때문에 deadlock이 생기는 상황을 피하려면 어떻게 해야 할까?

signal handler에서는 Lock 관련 함수/변수 사용 금지, I/O 관련 함수 호출을 하지 말라는 권고가 옛날부터 전해져 내려오고 있다.

signal handler는 최소의 작업만, 그리고 빠른 시간에 처리하고 종료되도록 구현하라는 권고도 있어왔다.

그러니까 localtime() 함수로 예쁘게 date 값을 뽑아내려고 하지 말고, 그냥 time(NULL)을 이용해서 epoch time 값을 뽑아서 활용하면 문제를 해결할 수 있다. 

signal handler 내부에서 Lock 관련 함수를 쓰지 않으면 만사 OK !!

반응형
작성일: 2025년 1월 13일

 

Golang으로 코드를 작성하다보면, 다른 개발자가 작성한 go package를 사용할 일이 생긴다.

분명 누군가 go package를 만들었을 것 같은데, 찾기가 쉽지 않다.

(안정성, 보안성 측면에서 믿을 만한 package를 찾기가 힘들다는 뜻)

 

아래의 웹 사이트를 이용해서 Go Package를 찾아보는 것을 추천!

Golang 표준 라이브러리 (표준 패키지)

https://pkg.go.dev/std

 

Standard library - Go Packages

Discover Packages Standard library Version: go1.23.4 Opens a new window with list of versions in this module. Published: Dec 3, 2024 License: BSD-3-Clause Opens a new window with license information. Jump to ... Directories Directories ¶ Show internal Exp

pkg.go.dev

 

"Awesome Go" 에서 많이 사용되는 패키지 모음

https://github.com/avelino/awesome-go

 

GitHub - avelino/awesome-go: A curated list of awesome Go frameworks, libraries and software

A curated list of awesome Go frameworks, libraries and software - avelino/awesome-go

github.com

 

반응형
작성일: 2025년 1월 12일

 

문서를 읽다보면, 한 화면씩 넘기면서 캡처한 내용을 PDF 파일로 만들어야 할 경우가 있다. (즉, eBook처럼 만들어야 하는 경우)

그리고 이 PDF 파일에서 문자열 검색이 가능하도록 OCR을 적용해야 할 경우도 발생한다.

그럴 때 아래와 같이 Python Script를 작성해서 실행하면 된다.

 

Step 1: 화면 캡처해서 PNG 파일로 저장하기

아래 Python source code 처럼 작성하고, -R 옵션 부분만 수정해서 사용하면 된다.

-R 옵션은 캡처할 화면의 위치(X, Y 좌표)이다.

그리고 maxPages는 내가 만들 PDF 파일의 전체 페이지이다.

#!/opt/homebrew/bin/python3
import os
import time
import pyautogui
## 숫자 745를 2로 나눈 이유는 2 Page를 한 화면으로 캡처하기 위함.
maxPage = (745 // 2) + 1
time.sleep(5)
for idx in range(maxPage):
## 숫자 110,170,1785,1205는 캡처할 화면의 위치에 대한 X-Y 좌표 값이다.
## 캡처할 때마다 이 숫자를 조정해서 사용해야 한다.
my_cmd = "screencapture -R110,170,1785,1205 p_%04d.png" % idx
print("cmd:", my_cmd)
os.system(my_cmd)
## 오른쪽 화살표 키를 누르면서 한 화면씩 캡처하기.
pyautogui.press('right')
time.sleep(1)

 

 

Step 2: 여러 개의 PNG 파일을 PDF 파일로 변환하기

Python source code를 작성하기 전에 "img2pdf" package를 설치한다.

$ pip3 install img2pdf

 

자세한 내용은 아래 Web document를 참고;

https://pypi.org/project/img2pdf/

 

 

img2pdf 명령어도 제공하기 때문에 python source code를 작성하지 않아도 image 파일을 PDF 파일로 변환할 수 있다.

그러나 여러 image 파일간의 순서(sorting) 같은 것도 필요해서 아래와 같이 source code를 작성해야 할 필요가 있을 것이다.

#!/opt/homebrew/bin/python3
import os
import img2pdf
# Replace the directory path with the folder containing JPEG images to be converted
directory_path = "."
image_files = [idx for idx in sorted(os.listdir(directory_path)) if idx.endswith(".png")]
# Convert the list of JPEG images to a single PDF file
pdf_data = img2pdf.convert(image_files)
# Write the PDF content to a file (make sure you have write permissions for the specified file)
with open("my-book.pdf", "wb") as file:
file.write(pdf_data)

 

위에서 작성한 python code를 실행한다.

$ ./my-img2pdf

 

참고: 위와 같이 PDF 파일로 변환하면, 글자가 그림으로 표현되기 때문에 "문자열" 검색이 되도록 하려면 OCR을 적용해야 한다.

 

 

Step 3: PDF 파일에 OCR 적용하기

OCR 관련 소프트웨어 패키지를 설치하기

OCRmyPDF 패키지와 다국어 관련 패키지 tesseract-lang을 설치한다.

$ brew install ocrmypdf
## 영어 이외에 언어(예: 한국어, 일본어, 중국어)를 사용하려면 아래와 같이 tesseract-lang 패키지를 설치
$ brew install tesseract-lang

기존 PDF 파일을 OCR 적용된 PDF 파일로 변환하기

## "eng+kor" 옵션을 설정하여 Image에 있는 영어, 한국어를 인식하도록 함.
$ ocrmypdf -l eng+kor my-book.pdf my-book-ocr.pdf

 

Apple m2 CPU를 사용했을 경우, 만약 500 Page 분량이면 ocymypdf 명령이 완료되기 까지 10분 정도 소요된다. 

그리고 8개의 CPU Core 사용률이 모두 95% 이상이 된다.

 

자세한 내용은 아래 웹 문서를 참고;

https://github.com/ocrmypdf/OCRmyPDF

 


 

이렇게 하면, 여러 개의 그림 파일(.jpg, .png, .gif, .tiff 등)을 1개의 PDF 파일로 만들 수 있다. 나만의 eBook이 만들어지는 셈이다.

 

반응형
작성일: 2025년 1월 11일

 

집에서 혼자 끄적끄적 거리면서 개발할 때는 내가 어떤 라이브러리를 사용하는지 큰 관심이 없지만,

회사에서 상용 소프트웨어를 만들면서 오픈소스 라이브러리를 쓸때마다 라이센스(라이선스)를 확인하게 된다.

 

라이센스 유형이 다양한데, "한국저작권위원회"의 "오픈 소스 라이선스 가이드"가 설명이 가장 쉬운 것 같다.

아래 "오픈 소스 라이선스 가이드"가 다루는 내용은;

  • BSD 계열
    • BSD
    • Apache
  • GPL 계열
    • GPL 2.0
    • GPL 3.0
    • LGPL
    • Affero GPL
    • GPL Exceptions
  • MPL 계열
    • MPL
    • CDDL
    • CPL, EPL
  • Font License
    • GPL Font Exception
    • SIL Open Font License (OFL)
    • Ubuntu Font License

 

https://www.olis.or.kr/license/licenseGuide.do

 

라이선스 가이드

오픈 소스 라이선스 가이드 4.0은 오픈소스 개발 실무자들이 쉽게 오픈소스 라이선스의 내용을 참고할 수 있도록 제정된 것으로, 2019년 6월에 발표되었다. 목차 이 가이드는 다음과 같은 목차로

www.olis.or.kr

 

 

오픈 소스를 사용하다가 생기는 분쟁에 대한 대응 방안 가이드도 제공되고 있다.

 

https://www.olis.or.kr/license/conflictGuide.do

 

분쟁 대응방안 가이드

오픈소스SW 라이선스 분쟁 대응방안 가이드는 오픈소스를 활용하는 과정에서 발생할 수 있는 법적 리스크와 분쟁 사례들을 정리하고, 법적 리스크 관리에 관한 기업들의 사례를 제시함으로써,

www.olis.or.kr

 

 

반응형
작성일: 2025년 1월 10일

 

Linux kernel source code를 다운로드하는 방법

GitHub에서 Git Clone하기

$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

 

참고: github에서 clone하면, 다운로드 시간이 엄청 길다. 내 경우는 10분 소요되었다.

 

 

Tar 파일로 다운로드하기

아래 Linux Kernel 공식 Archive 홈페이지에서 원하는 Linux 버전의 소스 코드에 대한 Tar 파일을 다운로드.

https://www.kernel.org

 

 

+ Recent posts