♣ 도움받은 사이트

 

♣ Open SSH Server 설치

2017년 후반 쯤에 Windows 10 에서 Open SSH Server Beta 버전을 지원하기 시작한 것 같다.

지금(18년 7월) 은 Beta 버전이 아닌 정식 버전으로 지원하고 있다.

베타버전과 정식 버전은 설정상 약간의 차이가 있으며, 현재 구글링해서 나오는 부분은 대부분 Beta 버전을 기준으로 설명하고 있다.

본 문서는 정식버전을 기준으로 설명한다. 

 

1. Windows 10 이 아니면 Windows 10 으로 업그레이드 한다.
   현재 (2018년 7월) Windows 10 에서 OpenSSH 설치를 제공하고 있다.
   이것저것 조사하고 설치해 본 후 내린 결론은, Windows 10 에서 제공하는 Open SSH Server를 서버를 설치하는게 정신건강에 이롭다.

 

2. 설정 -> 앱 -> 선택적 기능관리 -> 기능추가 -> OpenSSH 서버 선택하여 설치한다.

---------------------------------------------------------------------------

 

 

 

------------------------------------------------------------------------------

 

 

 

♣ OpenSSH Server 경로

C:\Windows\System32\OpenSSH 에 설치된다.

 

Beta 버전인 경우, SSH Key 를 직접 생성해야 했는데

현 버전은 OpenSSH 를 구동시키니 자동으로 생성되었다. 아래 경로에 키가 없으면 생성하는 듯 하다.

아래 경로는 시스템 경로이므로 탐색기에서 보기 -> 숨김항목을 체크해야 탐색기에 나타난다.

 

c:\ProgramData\ssh

 

authorized_keys 에는 인증받은 public key 가 저장되며 각 윈도우 계정 디렉토리의 .ssh 에 저장된다.

만약 윈도우 계정이 hahaite 라면,

 

C:\Users\hahaite\.ssh\authorized_keys

 

에 위치한다.

 

참고로,

.ssh 디렉토리는 앞에 점(dot) 이 있어 탐색기 -> 새폴더로 생성이 안된다.

이에 Dos 나 PowerShell 에서 "mkdir .ssh" 라고 명령어로 디렉토리를 생성하였다.

 

♣ OpenSSH Server 실행

PowerShell 을 관리자 모드로 실행한다.

 

 

 

PowerShell 에서,

Get-Service sshd 로 현재 OpenSSH 의 실행 여부를 확인할 수 있다.

 

아래 명령어로 실행, 중지, 재실행할 수 있다.

 

Start-Service sshd

Stop-Service sshd

Restart-Service sshd

 

 

♣ OpenSSH Server 접속

Linux 에선 아래와 같이 접속하여 OpenSSH 접속을 확인한다.

$ ssh hahaite@172.16.253.10

 

Windows 에선 Putty 를 사용하여 접속됨을 테스트하였다.

만약 putty 접속 때 아래와 같은 메시지가 나타나면,

(Disconnected: Server protocol violation: unexpected SSH2_MSG_UNIMPLEMENTED packet. )

 

 

아래와 같이 Putty -> 설정에서 Diffie-Hellman group 14 항목을 맨 위로 올린다.

 

 

 

 

 

 

 

 

 

 

도움받은 사이트 :

 - 행복한 코딩세상 (http://downman.tistory.com/231)

 - Stack OverFlow (https://stackoverflow.com/questions/8512958/is-there-a-windows-variant-of-strsep)

 

 

아래와 같이 콤마로 구분되어있는 문자열은 strtok() 함수를 사용하여 쉽게 문자열을 구분할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <cstdio>
#include <cstring>
 
int main()
{
        char szText[256] ;
        char *pSep ;
 
        strcpy(szText, "name,age,address") ;
 
        pSep = strtok(szText, ",") ;
        printf("1 : %s\n", pSep) ;
 
        pSep = strtok(NULL, ",") ;
        printf("2 : %s\n", pSep) ;
 
        pSep = strtok(NULL, ",") ;
        printf("3 : %s\n", pSep) ;
 
        return 1 ;
}

 

만약 아래와 같이 내용없는 토큰이 연달아 나타난다면,

"name,age,,,,address"

안타깝게도 strtok 는 내용없는 부분을 그냥 무시해버린다.

 

무시하지 않도록 하기 위해 strsep() 함수를 쓸 수 있다.

 

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
#include <cstdio>
#include <cstring>
 
int main()
{
        char szText[256] ;
        char *pSep ;
        char *pText;
 
        strcpy(szText, "name,age,,,,address") ;
 
        pText = szText ;
        pSep = strsep(&pText, ",") ;
        printf("pSep : %s\n", pSep) ;
 
        pSep = strsep(&pText, ",") ;
        printf("pSep : %s\n", pSep) ;
 
        pSep = strsep(&pText, ",") ;
        pSep = strsep(&pText, ",") ;
        pSep = strsep(&pText, ",") ;
 
        pSep = strsep(&pText, ",") ;
        printf("pSep : %s\n", pSep) ;
 
        return 1 ;
}

내용없는 token 을 skip 하기 위해 strsep 함수를 그냥 세번 호출한 부분을 볼 수 있다.

 

문제는 이 함수는 Linux 에서 제공을 하기때문에 Visual C++ 개발환경에선 strsep() 함수를 제공하지 않는다.

 

이에 아래와 같이 strsep 함수를 직접 만들어 쓰면 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
char* mystrsep(char** stringp, const char* delim)
{
        char* start = *stringp;
        char* p;
 
        p = (start != NULL) ? strpbrk(start, delim) : NULL;
 
        if (p == NULL)
        {
                *stringp = NULL;
        }
        else
        {
                *p = '\0';
                *stringp = p + 1;
        }
 
        return start;
}

 

(본 글은 Zebra 사의 Zebra Programming Guide 문서를 가지고 작성하였습니다.)

 

테스트 프린터 : ZM400, ZT410

 

본 글은 ZPL 에서 영어 이외의 글자. 예를 들어 스페인어 독일어, 체코어 등을 표시하는 법을 다룹니다.

ë, ß, Ä, é

뭐, 이런 글자들입니다.

 

예를 들어, 프랑스어 Référence 를 출력하는 코드는 아래와 같습니다.

^XA
^CI0
^FO80,70 ^A0N,30,30
^FH^FD R_82f_82rence SCF^FS
^XZ

 

위 문서에 보면 프랑스어는 Zebra Code Page 850 에 정의되어 있습니다.

그리고 é 는 Hex값으로 82 정의되어 있습니다.

 

 

 

 

위와 같이 특수문자는 정의된 Hex 값 앞에 _ 를 붙여 표시할 수 있습니다.

 

CI0 는 Zebra Code page 850 을 사용하겠다는 의미입니다. (문서의 ^CI 참조)

 

 

만약 체코어를 출력하게되면 어떻게 해야할까요?

Čeština 와 같이 말입니다.

 

위 Zebra Code page 850 에선 Č, š 와 같은 문자를 찾을 수 없습니다.

이 문자는 Zebra Code page 1250 에 정의되어 있습니다.

 

 

 

 

프랑스어와 같이 출력하면 아래와 같은 코드가 됩니다.

이 때 주의할 점은 ^CI31 로 값이 주어집니다.

이는 위 문서에 의해, Zebra Code page 1250 을 사용하기 위해선 CI31 로 세팅하라고 명시되어 잇습니다.

 

 ^XA
^CI31
^FO80,70 ^A0N,30,30
^FH^FD _c8e_9atina SCF^FS
^XZ

 

이렇게 간단히 특수문자를 출력하는 방법을 알아보았습니다.

도움받은 사이트 : https://stackoverflow.com/questions/33615048/what-zpl-codes-could-affect-the-start-position-of-a-qrcode

 

프린터 : ZM400, ZT410

 

아래는 간단한 QR Code 를 출력하는 ZPL 코드입니다.

 

^XA

^FO 0,0

^BQ N,2,15
^FDMA,hahaite^FS

^XZ

그런데 어쩔 때 보면 위와 같이 좌표를 0,0으로 주었는데도 QR Code 상단에 여백이 생기는 경우가 발생합니다.

예상 위치보다 더 아래에 출력된다는거죠.

 

웃긴게 이게 막 왔다갔다 하더라는거죠. 어떤날은 여백이 요만큼인데 어떤날은 여백이 이만큼인...

 

이래저래 찾아보니 이전에 출력한 바코드가 영향을 주는 듯 하더군요.

 

위 링크 사이트를 참고하여, QRCode 출력 전 dummy 바코드 하나 추가하니 해결되었습니다.

 

^XA

^BY,,1

^FO 0,0

^BQ N,2,15
^FDMA,hahaite^FS

^XZ 

 

 

 

공유기에 내 PC 와 잉여 노트북에 MariaDB 설치함.

C++ 기반 MariaDB API 를 포팅하고 for 문을 약 25만번 돌면서

select 를 날려 값이 중복되는지 체크하는, 복잡하지 않은 테스트였다.

 

노트북에서 테스트가 잘 되어 회사 서버(Ubunru) 에 설치하여 똑같이 테스트를 진행하는데

Lost connection to MySQL server during query.

가 뜨는 것이었다.

 

구글링해보니 이래저래 많은 해결책이 있어 금방 해결하겠다 싶었는데, 뭘 해도 해결이 안됨.

정말, 검색해서 나오는 건 다 해본듯 하다. 심지어 OS 문제인가 싶어 VirtualBox에 Ubuntu 14, 16도 설치해보고,

MariaDB Upgrade, Downgrade 도 해보고, 쿼리 사이에 Sleep 도 줘보고,

심지어 DB 연결 -> 쿼리 하나 전송 -> 연결해제 -> DB 연결 -> 쿼리 하나 전송 -> 연결 해제~~

이런 식으로도 테스트를 해 보았지만 위 증상은 여전했다.

 

♣ Error Log 체크

그러다 MariaDB 의 에러로그를 봤다.

[Warning] Aborted connection 16 to db: 'base_station' user: 'mesteam' host: '192.168.10.25' (Got an error reading communication packets).

 

혹시나 하고 위 error 내역으로 구글링을 진행하고 아래 사이트까지 가게 됨.

https://www.percona.com/blog/2016/05/16/mysql-got-an-error-reading-communication-packet-errors/

그야말로 이 이슈에 대해 할 수 있는 건 다 정리해 놓은 듯 했다.

그 중 tcpdump 를 떠 보라는 내용이 보여 MariaDB Server 에서 TCP Dump 를 실행하고 쿼리를 날려보았다.

 $ sudo tcpdump port 3306

 

♣ TCP Dump

그렇게, 양단이 쿼리와 ACK 를 다다다~ 주고 받다가, 위 증상이 나타났을 때 어~! 뭔가 이상함을 발견함.

14:16:57.598731 IP 192.168.10.25.51060 > 192.168.109.5.mysql: Flags [P.], seq 1860445:1860525, ack 3837097, win 251, length 80
14:16:57.598987 IP 192.168.109.5.mysql > 192.168.10.25.51060: Flags [P.], seq 3837097:3837262, ack 1860525, win 237, length 165
14:16:57.599944 IP 192.168.10.25.51060 > 192.168.109.5.mysql: Flags [P.], seq 1860525:1860605, ack 3837262, win 256, length 80
14:16:57.600200 IP 192.168.109.5.mysql > 192.168.10.25.51060: Flags [P.], seq 3837262:3837427, ack 1860605, win 237, length 165
14:17:49.661117 IP 192.168.109.5.mysql > 192.168.10.25.51060: Flags [P.], seq 9712582:9712747, ack 4709245, win 237, length 165
14:17:49.661515 IP 192.168.109.5.mysql > 192.168.253.200.51060: Flags [P.], seq 2639052253:2639052418, ack 1996360229, win 237, length 165
14:17:52.424828 IP 192.168.10.25.51060 > 192.168.109.5.mysql: Flags [P.], seq 4709165:4709245, ack 9712582, win 254, length 80
14:17:52.424854 IP 192.168.109.5.mysql > 192.168.10.25.51060: Flags [.], ack 4709245, win 237, options [nop,nop,sack 1 {4709165:4709245}], length 0
14:17:52.425088 IP 192.168.109.5.mysql > 192.168.253.200.51060: Flags [.], ack 1, win 237, options [nop,nop,sack 1 {4294967217:1}], length 0
14:17:56.205151 IP 192.168.109.5.mysql > 192.168.10.25.51060: Flags [P.], seq 9712582:9712747, ack 4709245, win 237, length 165
14:17:56.205610 IP 192.168.109.5.mysql > 192.168.253.200.51060: Flags [P.], seq 0:165, ack 1, win 237, length 165
14:18:02.025159 IP 192.168.10.25.51060 > 192.168.109.5.mysql: Flags [R.], seq 4709245, ack 9712582, win 0, length 0
 

192.168.109.5 : DB Server

192.168.10.25 : 회사에서 부여받은 내 IP -> 공유기에 세팅해서 사용함.

192.168.253.200 : 공유기에서 DHCP 로 할당한, 내 PC 의 IP.

 

즉 어느 시점에서 DB Server 가 공유기가 아닌, 내 PC 로 패킷을 날리기 시작한 것이다.

이것은, 공유기 문제?

 

그래서 공유기를 빼고 라인을 직접 내 PC 에 연결하여 테스트를 진행해 보았더니, 잘된다.

하아~~

 

공유기 설정문제인지, 공유기 자체 버그인지는 아직 모른다.

공유기를 다른 회사 제품으로 바꾸어 테스트해보니 이상없이 잘 돌아간다.

하~ 회사 이름을 밝힐 수도 없고... 일단 공유기를 교체하고 진행하자.

 

 

도움받은 사이트 :

 - http://bitsoul.tistory.com/m/150

 - https://bash.cyberciti.biz/guide/Logical_Not_!

 

리눅스에서 개발하다보면 남의 코드를 받았을 때 한글이 다 깨지는 증상이 있다.

 

이는 저쪽 개발자가 source insight 로 개발한 경우가 대부분이다.

source insight 는 UTF-8 을 지원하지 않아 한글을 입력하면 euc-kr 로 저장된다.

 

이를 리눅스환경에서 열면 한글이 모두 깨져보이게 된다. 

(정확히 표현하면 charset이 euc-kr 이 아닌 경우)

 

검색해보니 어떤 중국인이 source insight UTF-8 플러그인을 개발한 것 같다.

사족인데, source insight 쓸거면 호환성을 위해 UTF-8 플러그인을 설치했으면 한다.

 

아래는 euc-kr 방식의 파일을 리눅스에서 UTF-8 방식으로 전환하는 방법이다.

 

 $ locale

locale 명령어로 linux 로 저장되는 파일 방식을 확인할 수 있다.

내가 문서나 코드를 작성하고 저장했을 때, UTF-8 로 저장되는지 확인해 본다.

 

 $ file -i test.c

test.c 란 파일의 charset 을 확인한다. ISO8859-1 로 나오면 euc-kr로 여겨도... 되나?

한글이 깨진 파일을 위 명령어로 확인해 보면 charset=ISO8859-1 로 나온다.

 

 $ iconv -c -f euc-kr -t utf-8 test.c > test_utf8.c

test.c 의 charset을 UTF-8 로 변경하여 test_utf8.c 란 파일로 저장한다.

이 후, test_utf8.c 를 test.c 로 복사하여 원 코드를 변경할 수 있다.

 

참고로, 아래와 test.c > test.c 같이 파일이름을 같게 하면 안된다. 주의하자.

 $ iconv -c -f euc-kr -t utf-8 test.c > test.c

 

위 번거로움을 아래와 같이 shell script 하나 작성해서 편하게 utf-8로 변경할 수 있겠다.

 $ touch convUTF8.sh

 $ chmod +x convUTF8.sh

 $ vi convUTF8.sh

 

if [ -z $1 ]

then
        echo "Please input file to convert."
        exit
fi

 

if [ ! -f $1 ]
then
        echo "Cannot find the file - $1"
        exit
fi

 

cp $1 $1.org
iconv -c -f euc-kr -t utf-8 $1 > $1.utf8
cp $1.utf8 $1
rm $1
echo "Converted file $1"

 

 

이 후,

 $ convUTF8.sh test.c

하면 test.c 의 charset 을 UTF-8 로 복사하며,

원래 파일은 test.c.org 로 백업한다.

 

 

예전 싸이월드 블로그에 작성한 글을 티스토리로 옮김.

싸이블로그 링크 : http://cy.cyworld.com/home/21147242/post/4EE70E1794D5739A07C68401

원문 작성일 : 2011년 12월 23일

 

================================================================

도움 받은 사이트 : http://apollo89.tistory.com/173
프로젝트 진행중에 컴파일을 하면
driver 하위 디렉토리들이 자기 밑에 objs 라는 디렉토리를 수십개 생성해버림.


drvier/audio/objs
drvier/audio/src/objs
drvier/video/objs
drvier/video/src/objs


갯수가 적으면 그려려니 하겠는데, 수십개가 뜨다보니
svn st 하면


? drvier/audio/objs
? drvier/audio/src/objs
? drvier/video/objs
? drvier/video/src/objs
 .............


해서 화면을 뒤덮어버림. ;;;;;;
전에 선임이 svn ignore 를 쓰던 기억이 떠올라 찾아봄.
별 생각없이 덤볐는데 의외로 사용법이 까칠하여 이것저것 정리해 봄.
svn propset (또는 svn ps ) 를 이용하는 방법과,
svn propedit (또는 svn pe ) 를 이용하는 방법이 있다.


♣ svn propset 성공 사례 - 1

$ svn propset svn:ignore objs ./driver/audio/
$ svn propset svn:ignore objs ./driver/audio/src/
$ svn propset svn:ignore objs ./driver/video/
$ svn propset svn:ignore objs ./driver/video/src/
$ svn st


이러고 제대로 되었는지 확인.


♣ svn propset 실패 사례 - 1
$ svn propset svn:ignore


./driver/audio/objs ./

 


♣ svn propset 실패 사례 - 2
$ svn propset svn:ignore


objs objs2


./driver/audio/src/


// 한번에 하나씩


♣ svn propset 실패 사례 - 3
$ svn propset svn:ignore objs ./ -R

// Recursive 안됨. ;;;


♣ svn propedit 성공 사례 - 1

$ svn propedit svn:ignore driver/audio/


(편집창 뜨면)
objs
(저장하고 나옴.)
이렇게 나머지 세 디렉토리를 일일이 해 주어야 함.


♣ svn propedit 실패 사례

$ svn propedit svn:ignore ./
(편집창 뜨면)


driver/audio/
driver/audio/src/
driver/video/
driver/video/src/


(저장하고 나옴.)


(이렇게 하니 안됨)


일단 이렇게 해서 수십개의 objs 디렉토리를 일일이 svn propset svn:ignore 명령어로
무시시켜버림.
svn st 하니 깔끔해서 좋다.
찜찜한게, 이렇게 노가다를 해야하는건가? 뭔가 좋은 방법이 숨어있을 것 같은 느낌이...

 

 

Vector의 2차원 배열 표시 도움받은 곳 : http://sosal.kr/524

 

오일러 프로젝트 풀다가 아래와 같은 구조를 생성할 일이 있어

짜긴 했는데, 이런 구조를 뭐라 하는지 모르겠네요.

 

일단 TriangleTree 라고 명하고 클래스를 만들어보았습니다.

- triangleTree.h

- triangleTree.cpp

 

그리고 main.cpp

에서 함수 호출의 예를 볼 수 있습니다.

 

 

 

 

triangleTree.h

 

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
32
33
34
#ifndef _TRIANGLETREE_H_
#define _TRIANGLETREE_H_
 
#include <vector>
 
class CTriangleTree
{
public :
        int init(int width) ;
        int getNext(int* pList) ;
        int destroy() ;
 
private :
        int loop() ;
 
public :
 
private :
        int m_width ;
        int m_max ;
 
        std::vector<std::vector<int> >  m_data ;
        std::vector<int>        m_direct ;
        std::vector<int>        m_value ;
 
 
        int m_status ;
        int m_row ;
        int m_col ;
 
};
 
#endif
 

 

triangleTree.cpp

 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include "triangleTree.h"
 
#include <cstdio>
 
#define LEFT    0x01
#define RIGHT   0x02
 
#define MOVE_FIRST_CHILD        0X04
#define MOVE_SECOND_CHILD       0X05
#define MOVE_PARENT             0X06
 
using namespace std ;
 
int CTriangleTree::init(int width)
{
        m_max = width ;
 
        m_status = MOVE_FIRST_CHILD ;
        m_row = 0 ;
        m_col = 0 ;
 
        m_data.clear() ;
        m_direct.clear() ;
        m_value.clear() ;
 
        m_direct.assign(width, LEFT) ;
 
        m_data.assign(width, vector<int>(width, 0)) ;
 
        for(int ii = 0; ii < m_max; ii++)
        {
                for(int jj = 0; jj <= ii; jj++)
                        m_data[ii][jj] = jj ;
        }
 
        m_value.assign(width, 0) ;
 
        return 1 ;
}
 
int CTriangleTree::getNext(int* pList)
{
        if(m_row  == m_max-1 && m_col == m_max-1)
                return 0 ;
 
        int ii ;
        int flagBreak = 0 ;
 
        while(1)
        {
                if(flagBreak)
                        break ;
 
                switch(m_status)
                {
                case MOVE_FIRST_CHILD :
                        m_row++ ;
                        m_value[m_row] = m_data[m_row][m_col] ;
                        m_direct[m_row - 1] = LEFT ;
 
                        m_status = (m_row < m_max-1) ? MOVE_FIRST_CHILD : MOVE_SECOND_CHILD ;
 
                        if(m_row == m_max-1)
                        {
                                for(ii = 0; ii <= m_row ; ii++)
                                        pList[ii] = m_value[ii] ;
                                return 1 ;
                        }
                        break ;
 
                case MOVE_SECOND_CHILD :
                        m_col++ ;
                        m_value[m_row] = m_data[m_row][m_col] ;
 
                        m_direct[m_row - 1] = RIGHT ;
                        m_status = (m_row < m_max-1) ?  MOVE_FIRST_CHILD : MOVE_PARENT ;
 
                        if(m_row == m_max-1)
                        {
                                for(ii = 0; ii <= m_row ; ii++)
                                        pList[ii] = m_value[ii] ;
                                return 1 ;
                        }
                        break ;
                case MOVE_PARENT :
                        if(m_direct[m_row-1] == RIGHT)
                                m_col-- ;
                        m_row-- ;
 
                        if(m_row == 0)
                        {
                                m_status = MOVE_SECOND_CHILD ;
                                m_row++ ;
                                break ;
                        }
 
                        m_status = (m_direct[m_row-1] == LEFT) ?  MOVE_SECOND_CHILD : MOVE_PARENT ;
                        break ;
                }
        }
 
        return 0 ;
}
 
int CTriangleTree::destroy()
{
        m_data.clear() ;
        m_direct.clear() ;
        m_value.clear() ;
 
        return 1 ;
}
 

 

main.cpp

 

 

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
#include "triangleTree.h"
 
#include <cstdio>
 
using namespace std ;
 
int main()
{
        CTriangleTree triTree ;
 
        const int MAX = 5 ;
 
        triTree.init(MAX) ;
        int value[MAX] ;
 
        while(1)
        {
                if(!triTree.getNext(value))
                        break ;
 
                for(int ii = 0; ii < MAX; ii++)
                        printf("%d ", value[ii]) ;
                printf("\n") ;
 
        }
 
        return 1 ;
}
 

 

조합을 구하려다 한참을 삽질하고,

결국 로제타 코드에서 큰 도움을 받았다.

( https://rosettacode.org/wiki/Combinations#C.2B.2B )

 

어찌나 쉽고 심플하게 짜놨는지... 허망~

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <algorithm>
#include <iostream>
#include <string>
 
void comb(int N, int K)
{
    std::string bitmask(K, 1); // K leading 1's
    bitmask.resize(N, 0); // N-K trailing 0's
 
    // print integers and permute bitmask
    do {
        for (int i = 0; i < N; ++i) // [0..N-1] integers
        {
            if (bitmask[i]) std::cout << " " << i;
        }
        std::cout << std::endl;
    } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
}
 
int main()
{
    comb(5, 3);
}
 

 

위 코드를 참고하여 써먹기 쉽게 아래처럼 클래스화 시켜 보았다.

 

 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <cstdio>
 
#include <vector>
#include <algorithm>
#include <string>
 
 
using namespace std ;
 
class CCombination
{
public :
        CCombination(vector<int>* vecInt, int r) ;
 
        int getNext(vector<int>* vecCombi) ;
 
private :
        string m_bitmask ;
        vector<int>* m_vecInt ;
 
};
 
CCombination::CCombination(vector<int>* vecInt, int r)
{
        m_vecInt = vecInt ;
 
        m_bitmask.clear() ;
        m_bitmask.resize(r, 1) ;
        m_bitmask.resize(vecInt->size(), 0) ;
}
 
 
int CCombination::getNext(vector<int>* vecCombi)
{
        vecCombi->clear() ;
 
        int size = m_bitmask.size() ;
 
        for(int ii = 0; ii < size; ii++)
        {
                if(m_bitmask[ii])
                        vecCombi->push_back((*m_vecInt)[ii]) ;
        }
 
        if(!prev_permutation(m_bitmask.begin(), m_bitmask.end()))
                return 0 ;
 
        return 1 ;
}
 
int main()
{
        vector<int> vecInt ;
        vector<int> vecCombi ;
 
        int n = 6 ;
        int r = 4 ;
 
        for(int ii = 1; ii <= n; ii++)
                vecInt.push_back(ii) ;
 
        CCombination combination(&vecInt, r) ;
 
        int flagBreak ;
 
        while(1)
        {
                flagBreak = combination.getNext(&vecCombi) ;
 
                for(int ii = 0; ii < r; ii++)
                        printf("%d ", vecCombi[ii]) ;
                printf("\n") ;
 
                if(!flagBreak)
                        break ;
        }
 
        return 1 ;
}
 
 
 

뭔가 썩 마음에 들진 않는데 딱히 개선안이 떠오르지 않는다. 허망함 때문인가? ㅋㅋ

시간이 지나면 떠오를 때가 있겠지.

도움받은 사이트 : 나무위키 (https://namu.wiki/w/%EC%88%9C%EC%97%B4(%EC%88%98%ED%95%99))

 

개요 :

서로 다른 n개의 원소에서 r개를 중복없이 골라 순서에 상관있게 나열하는 것을 n개에서 r개를 택하는 순열이라고 한다.

 

ex) 4개의 원소 : 1, 2, 3, 4

이중 2개를 골라 나열하면, n = 4, r = 2.

 

1 2

1 3

1 4

2 1

2 3

2 4

3 1

3 2

3 4

4 1

4 2

4 3

 

C++ 에선 STL 에 next_permutation 이란 함수가 있지만 위와 같이 nPr 타입을 지원하진 않는다.

이에 위와 같이 nPr 타입의 순열을 출력하는 함수를 간단히 구현해 보았다.

 

(Warning : 처음 초기값이 오름차순으로 입력되어야 한다. 1,2,3,5,8 ~~~ 등 )

 

reverse() 함수도 STL 에서 제공하는 함수이다.

 

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
32
33
34
35
36
37
38
#include <cstdio>
 
#include <vector>
#include <algorithm>
 
using namespace std ;
 
int permutation(int n, int r)
{
        vector<int>             vecInt ;
        vector<int>::iterator   vecIntIter ;
 

        for(int ii = 1; ii <= n; ii++)

                vecInt.push_back(ii) ;
 
        while(1)
        {
                for(int ii = 0; ii < r; ii++)
                        printf("%d ", vecInt[ii]) ;
                printf("\n") ;
 
                reverse(vecInt.begin() + r, vecInt.end()) ;
 
                if(!next_permutation(vecInt.begin(), vecInt.end()))
                        break ;
        }
 
        return 1 ;
}
 
 
int main()
{
        permutation(4, 2) ;
 
        return 1 ;
}
 

 

위 실행으로 순열 결과는 얻었지만 응용하기엔 애매하다.

 

이에 아래와 같이 살짝 변형하였다.

 

permutation() 함수의 vector<int>* 부분은 템플릿을 사용하여 일반화시킬 수 있지만

가독성이 떨어져 특정 타입으로 선언하였다.

 

 

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
32
33
34
35
36
37
38
#include <cstdio>
 
#include <vector>
#include <algorithm>
 
using namespace std ;
 
int permutation(vector<int>* vecInt, int r)
{
        reverse(vecInt->begin() + r, vecInt->end()) ;
        return next_permutation(vecInt->begin(), vecInt->end()) ;
}
 
 
int main()
{
        vector<int>             vecInt ;
        vector<int>::iterator   vecIntIter ;
 
        int n = 4 ;
        int r = 2 ;
 
        for(int ii = 1; ii <= n; ii++)
                vecInt.push_back(ii) ;
 
        while(1)
        {
                for(int ii = 0; ii < r; ii++)
                        printf("%d ", vecInt[ii]) ;
                printf("\n") ;
 
                if(!permutation(&vecInt, r))
                        break ;
        }
 
        return 1 ;
}
 

 

나도 함 github 써보고자 들어가서 계정 만들고 repository 도 만들고~

 

그러다 push 할 때마다 id, password 넣는게 귀찮아서 ssh 키 연동을 찾아 봄.

 

github 소개랑 ssh 키 생성은 검색하면 많이 나오고 아래는 그 외 사항을 잊지않으려 추가한 정도이다.

(나는 이미 예전에 생성한 ssh 키를 사용하고 있다.)

 

1. clone 시, https 가 아니라 ssh 로 시작하는 주소로 clone 해야 한다.

ssh 주소는 아래 useSSH 키 누르면 확인할 수 있다.

 

 

 

2. ~/.ssh/config 를 설정하고 있다면,

host 를 github.com 으로 바꾸어야 한다.

 

 

3. 아래 명령어로 ssh key 의 연동을 확인할 수 있다.

$ ssh -T git@github.com

Hi hahaite! You've successfully authenticated, but GitHub does not provide shell access

 

와 같이 출력되면 된다.

 

위와 같이 하니 수정 후, push 할 때 바로 서버로 전송되었다.

 

roughTimer_C_170622.zip

roughTimer_CPP_170622.zip

 

 

제목 : Rough Timer

언어 : C, C++

OS : Ubuntu 14.04

컴파일러 : gcc, g++

라이센스 : 누구나, 어디서나 쓸 수 있습니다. (누가 쓰겠냐마는...)

 

usleep() 함수를 이용해 타이머를 만들어 보았다. (첨부파일 참조)

 

예를 들어 2초짜리 타이머를 등록하면,

쓰레드에서 100ms 마다 1씩 카운트하여 2초가 됐을 때 콜백함수를 호출하는 방식이다.

 

고로, 정확도는 떨어지며 100ms 이하 타이머는 사용할 수 없다.

정확도 보다도 "약 몇초 후에" 식으로 운영되는 타이머라면 심플하게 쓸 수 있을 것이다.

 

 

이에 Rough Timer 로 명명하였다.

 

C++ 코드는 STL List 를 사용하였고,

C  코드는 간단히 linked-list 함수를 만들어 사용하였다.

 

코드 안에 main() 함수에서 간단한 테스트 코드를 볼 수 있다.

 

(주의사항 : 타이머 발동 시, 호출되는 콜백함수는 최대한 빨리 빠져나와야 한다.

 그렇지 않으면 전체 타이머 시간과 다른 타이머에 영향을 준다.)

 

 

 

msgQueue_CPP_170616.zip

 

 

컴파일 환경 : Ubuntu 14.04

 

(추후, C 기반 포팅작업 필요함.)

 

모듈의 독립성을 높이기 위해 메시지큐를 사용하여 task 를 처리하는 틀을 작성한다.

 

예를 들어 Core 부분과  UI Display 부분이 있을 때,

Core는 UI의 존재를 모르도록 구성한다.

 

Core 는 자신의 task 를 메시지큐에 전달하면 main 에서 이를 판별하여

이 후 작업(UI 갱신 등)을 진행하도록 한다.

 

이와 같은 작업을 위해 코드를 작성하였으며 추후 투입될 작업에 맞춰

모듈과 task 정의를 추가한다.

select() 함수를 예제보고 가져다 쓰기만 하다가 뭔가 수정만 하면 잘 안되어

이대로는 안되겠다 싶어 개념을 좀 더 파악하기로 하였다.

 

♣ fd_set 구조체

fd_set 구조체는 File Descriptor (이하 FD) 를 저장하는 구조체이다.

안에 내용을 보면 그냥 배열로 여기면 편하다.

 

구조체 내용은 OS마다 조금씩 다른 듯 하다.

내가 쓰는 Ubuntu 14 에선 /usr/include/sys/select.h 에서 위 구조체를 확인할 수 있었다.

 

♣ FD_SET

fd_set 구조체에 2와 5의 FD 를 저장한다고 하면,

두번째 비트와 다섯번째 비트가 1로 변경된다.

값 저장은 FD_SET 매크로를 쓴다.

 

fd_set testFds ;

FD_SET(2, &testFds) ;

FD_SET(5, &testFds) ;

 

위 testFds 구조체의 배열값을 출력해 보면,

0 1 0 0 1 0 0 0 0 ~~~

위와 같이 2번째와 5번째 비트가 1 로 세팅된다.

 

♣ FD_ZERO

언급했듯이 fd_set 구조체의 주요 변수는 배열이다.

fd_set testFds ; 와 같이 선언만 하고 안에 배열값을 찍어보면 쓰레기값이 있을 수 있다.

 

이에 구조체를 초기화 해야 하며 아래와 같이 한다.

FD_ZERO(&testFds) ;

 

위에 FD_SET 예제에서도 변수 선언 후, 초기화를 해 주어야 한다.

 

fd_set testFds ;

FD_ZERO(&testFds)

FD_SET(2, &testFds) ;

FD_SET(5, &testFds) ;

 

♣ FD_CLR

FD_SET(2, &testFds) ;

FD_SET(3, &testFds) ;

FD_SET(5, &testFds) ;

 

위와 같이 입력 후, testFds 는 아래와 같이 세팅된다.

0 1 1 0 1 0 0 0 0 ~~~

 

이 때 만약 3번 FD 를 지우고 싶으면 FD_CLR 매크로를 쓴다.

FD_CLR(3, &testFds) ;

 

결과는 아래와 같다. 세번째 값이 0으로 초기화 되었다.

0 1 0 0 1 0 0 0 0 ~~~~

 

♣ FD_ISSET

이 매크로는 아래 select 함수와 함께 설명한다.

 

 

♣ select()

select 함수는 fd_set 구조체에 할당된 FD 의 이벤트가 발생하면

이를 감지하고 어떤 FD 이벤트가 발생했는지 알려준다.

 

ex)

fd_set testFds ;

FD_SET(2, &testFds) ;

FD_SET(5, &testFds) ;

 

select(6, &testFds, NULL, NULL, NULL) ;

 

이는 FD 이벤트는 6 미만값만 체크하며,

6 미만 중 2와 5 FD 의 이벤트가 발생했을 때만 깨어난다.

 

만약 2번 FD 의 이벤트가 발생하여 select 함수가 깨어나고,

select 함수 이후에 testFds 를 찍어보면,

 

0 1 0 0 0 0 0 0 0 ~~~~~~~

식으로 값이 변경된다. -> 이걸 몰라 한참 헤맸다.

 

즉 testFds 에 2, 5 FD 를 저장한 후,

저장된 FD (2,5) 중 이벤트가 발생하면,

해당 이벤트만 1 로 남기고 모두 0 으로 세팅한다.

 

이제 어떤 FD의 이벤트가 발생했는지 체크하여 FD_ISSET 매크로를 사용한다.

 

if(FD_ISSET(2, &testFds))

{

  // 2번 FD 가 발생했다.

}

 

if(FD_ISSET(5, &testFds))

{

  // 5번 FD 가 발생했다.

}

위와 같이 해당 FD에 대한 후처리를 진행하면 된다.

 

 

♣ 틀 정립

select 함수는 위 예제처럼 testFds 를 변경시킨다.

그럼 select 함수 전이나 후로 매번 testFds 를 다시 세팅해 주어야 한다.

 

이에 아래와 같은 틀이 될 것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fd_set readFds ;
FD_ZERO(&readFds) ;
FD_SET(2, &readFds) ;
FD_SET(5, &readFds) ;
 
while(1)
{
      // select 함수에서 fd_set 이 변경되니 원본 대신 복사본을 select 함수에 전달한다.
      checkFds = readFds ;
 
      printf("sleep - select()\n") ;
      select(6, &checkFds, NULL, NULL, NULL) ;
      printf("wake up - select()\n") ;
 
      if(FD_ISSET(2, &checkFds))
      {
          // 2번 FD 이벤트에 대한 사후 처리.
      }
 
      if(FD_ISSET(5, &checkFds))
      {
          // 5번 FD 이벤트에 대한 사후 처리.
      }
}

 

♣ 정리

이런 저런 코드를 보면서 원본 (readFds) 를 checkFds 로 복사하는 이유를 몰랐는데

select() 함수 내에서 fd_set 구조체 값을 변경하기 때문이다.

 

이를 위와 같이 정리하며 아래 내용이 빠져있다.

  • select() 함수의 리턴값 처리
  • select() 함수의 첫번째 인자 max FD 값의 처리
  • select() 함수의 세번째, 네번째 인자 writeFds, exceptFds 에 대한 처리.
  • select() 함수의 다섯번째 인자 타이머에 대한 처리.

 

도움받은 사이트 : http://blog.naver.com/dolzzo/130131251201

 

뭔가 간단한 프로그램을 짜고 있는데 프로그램 종료할 때마다 segmentation fault 가 뜨길래

디버깅해보니 육안상 이상이 없었다.

 

gdb 로 코어 뜨고 한참을 삽질한 끝에야 원인을 찾을 수 있었다.

 

나처럼 조인시위키 (https://www.joinc.co.kr/w/man/3/pthread_join) 를 통해 쓰레드를 익혔으면

아래 형태를 많이 봤을 것이다.

 

int status ;

pthread_join(thread, (void**)&status) ;

 

이 코드가 32bits OS 에선 이상이 없는데 64 bits OS 로 넘어오면서 문제를 일으킨다.

 

sizeof(void**) 값은 8인데 여기에 4Bytes 짜리 int 타입의 변수를 넣었기 때문에 메모리 충돌을 일으킨 것이다.

(64bits Linux 로 넘어온지 3년쯤 되는데 그동안 왜 아무일 없었던거지?)

 

해결책으론 아래와 같이 int 대신 long 을 쓰거나,

 

long status ;

 

아니면 아예 void* 로 선언하는 경우가 있겠다.

(앞으론 아래처럼 써야겠다.)

 

void* status ;

pthread_join(thread, &status) ;

 

참고로, 정보문화사의 Beginning Linux Programming (3판) 의 예제를 보면

아래와 같이 status 값을 확인하고 있다.

 

printf("status : %s\n", (char*)status) ;

 

 

+ Recent posts