반응형

Device: Raspberry Pi 5
OS: 12 (bookworm, 2023-12-05)

 

라즈베리파이 OS가 점점 쓰기 편해지면서 VNC 설정도 굳이 x11vnc를 별도로 추가설치 할 필요 없이, 설정에서 잡아 주면 바로 접근이 가능하다.

[GUI] Raspberry Pi Configuration 창에서 설정

1. 라즈베리파이 버튼(시작 버튼?) > Preferences > Raspberry Pi Configuration 실행

2. Interfaces 탭에서 VNC 설정을 enable로 변경

3. VNC Viewer에 표시하는 화면 크기는 Display 탭 > Headless Resolution 에서 변경 가능

Raspberry Pi Configuration 내 VNC 설정

[Terminal] raspi-config 실행

1. raspi-config 실행

sudo raspi-config

 

2. 3번 Interface Options 선택

 

3. I2 VNC 선택 후, VNC Server를 enable할지 묻는 질문에 Yes 선택

 

4. VNC Viewer에서 보이는 화면 크기는, 설정 첫 화면(메인 메뉴)에서 2번 Display Options에서 설정할 수 있다.

 

 

반응형
블로그 이미지

Bryan_

,
반응형

Date: 2023.12.23
Target: Raspberry Pi 3B+
OS: Raspberry Pi OS (legacy) with Desktop (bullseye)

거의 5년 만에 라즈베리파이3를 다시 부활시키려고, SD 카드에 이미지 넣는 방법을 찾아보니, Raspberry Pi Imager 프로그램을 공식 제공한다는 것을 알게 되었다. 원래 Raspbian OS 시절에 Etcher라는 별도의 프로그램을 사용했었는데 점점 OS 설치하기 편해지는 것 같다.

아무튼 Raspberry Pi Imager를 써서 라즈베리파이5는 무사히 설치하고 부팅도 잘 시켰는데, 5년 묵은 라즈베리파이3는 부팅도 잘 안되고, SD 카드에 이미지를 복사하고 보니 마지막에 해시 값이 맞지 않는다는 오류(Verification hash doesn't match download)도 뜨는 것이 영 시원찮다.

Raspberry Pi Imager 프로그램에서 보면 이미 한번 OS 설치를 하면 PC에 캐시 데이터가 남는데, 이 캐시를 프로그램 상에서 지우는 방법은 아직 없는 것 같다. 참고로 캐시 파일 위치는 C:\Users\<사용자 이름>\AppData\Local\Raspberry Pi\Imager\cache 폴더에 있는 lastdownload.cache 파일이다.

캐시를 지우기 전에, 이미 공식 홈페이지(https://www.raspberrypi.com/software/operating-systems/#raspberry-pi-os-legacy )에서 직접 다운로드 받아 둔 이미지 파일도 있어서 이것을 바로 써 보기로 했다.

1. Raspberry Pi Imager 프로그램에서 일단 디바이스부터 선택하고,

2. 운영체제를 고르는 창에서 맨 아래로 스크롤해서 "Use custom"을 선택하면, PC에 다운받아 둔 로컬 이미지 파일을 고를 수 있다.

 

이번에는 정상적으로 SD카드에 기록되었다.

 

 

반응형
블로그 이미지

Bryan_

,
반응형

OS: Ubuntu 18.04
Simulator: ns-3.30

 

ns-3에서 무선 채널을 통해 두 노드가 서로 unicast로 패킷을 보낼 때, 시뮬레이션 타임을 기준으로 완전히 똑같은 시간에 패킷을 보내면 (즉, ns3::Socket::Send 함수를 똑같은 시간에 사용하면) 충돌이 발생한다.
이것을 방지하려면 패킷을 보낼 때 의도적으로 약간의 지터(jitter)를 부여하면 되는데, 예를 들면 두 노드가 ns3::Socket::Send를 call할 때, 수십~수백 마이크로초(microseconds) 정도의 차이만 있어도 무선 채널에서 충돌로 인한 패킷 유실을 피할 수 있다.

이를 위해 각 노드에서 Socket::Send 함수를 call 하는 부분 앞에 매번 지터를 아래와 같이 넣어 주었다.

Ptr<UniformRandomVariable> rng = CreateObject<UniformRandomVariable> ();
uint32_t jitter = rng->GetInteger(0, 1000);
Simulator::Schedule(jitter, &패킷_보내는_함수, 함수 파라미터, ...);

문제는 위와 같이 지터를 넣어 주는데도 영문도 모르게 패킷이 전달이 안되는 것이다.

분명히 동일한 시간에 패킷을 보내지만 않으면 서로 모두 전달이 되는데도 불구하고, bootstrap처럼 자동으로 주기적으로 heartbeat 메세지를 보내도록 설정하면, 패킷이 이유 없이 사라지는 것이었다. 보내는 노드에서 Socket::Send 함수를 call 한 기록만 있고 패킷을 받은 노드가 하나도 없는 상황...

 

UniformRandomVariable 클래스를 다시 확인해 보니, GetInteger 함수는 "다음(next)" 랜덤 값을 반환한다고 되어 있다. 그 말은, 랜덤 숫자의 배열을 미리 생성해 놓고, 맨 첫번째 인덱스부터 시작해서 GetInteger 함수를 call할 때마다 순서대로 하나씩 반환한다는 뜻이다.

내가 동시에 모든 노드가 시뮬레이션 시작 시간에 동시에 heartbeat 메세지를 생성해서 broadcast하도록 스케줄링을 걸었고, 그 스케줄링 함수 안에 Socket::Send 함수 앞에 GetInteger를 썼다. 내가 랜덤에 시드 값을 지정하지 않았기 때문에, 모든 노드가 같은 타이밍에 GetInteger를 맨 처음 불러왔다면, 모두가 똑같은 jitter 숫자를 가져온다는 뜻이므로, 랜덤을 잘못 적용한 셈이다. ㅠㅠ

 

각 노드별로 노드 ID 숫자를 seed로 입력해 주었더니, 모두가 처음으로 받아 오는 GetInteger 값이 다 다른 숫자가 되었다. 이제서야 진짜 랜덤으로 작동한 것이다.

#include "ns3/random-variable-stream.h"

// ...(중략)

SeedManager::SetSeed(node_id + 1); // ns-3에서 node id는 0부터 시작하고, 

                                                                            // Seed 값에 0이 들어가면 런타임 에러가 나서 1을 더해 줬다.

Ptr<UniformRandomVariable> rng = CreateObject<UniformRandomVariable> ();

 

 

반응형
블로그 이미지

Bryan_

,
반응형

OS: Ubuntu 16.04

ns-3 version: 3.26


ns-3에서 기존의 리눅스에서 돌아가던 코드를 포팅하는 과정에서, UDP (또는 TCP) 소켓을 열고 패킷을 보내는 코드를 그대로 옮겨 오면서 ns-3 클래스 중 하나인 Ptr<Socket>을 직접 만들어서 패킷을 보내도록 만들어 두었다.


그런데, 이상하게 1초마다 보내는 패킷의 개수를 대폭 늘리기만 하면 (일부러 링크가 saturated 되도록 만들고 경로 변경을 유도하게 만들어야 했다), 시뮬레이션 타임이 끝나기 전에 이유 없이 시뮬레이션이 아주 느려지면서 몇 시간씩 걸리는 것이었다. 에러 메세지는 하나도 없었고, 메모리 문제라도 있었으면 Segmentation fault라도 떠서 디버그를 했을 텐데 그런 문제도 없었고, 실제로 몇 시간씩 걸리더라도 메모리 사용량이 그에 비례해서 늘어나지도 않고 오로지 CPU 사용량만 100%를 찍고 있었다.



결론부터 얘기하자면, 원인은 Socket 오브젝트의 잘못된 사용이었는데, 소켓을 열고 나서 하나도 닫지 않아서 발생한 문제였다. 나는 ns3::Socket 오브젝트를 생성하는 함수를 따로 만들고, 그걸 원하는 조건에서 보내면서 나에게 필요한 통계 처리를 하기 위해서 아래와 같은 간단한 함수를 정의해서 코드 이곳저곳에서 사용하였다.


void sendPacket (Ptr<Socket> socket, Ptr<Packet> packet) {

        socket->Send(packet);

        // 특정 통계 내는 작업

}



이미 언급했듯이 위의 함수에서 결정적인 실수를 하나 했는데, sendPacket 함수를 call 하기 전에 매번 socket을 새로 생성하도록 해 놓고는 잊고 socket을 한 번도 닫지 않은 것이었다. 소켓을 하나만 열고 재활용을 한다면 위의 함수가 문제가 없지만, 매번 소켓을 새로 생성한다면 위의 함수에서 socket->Close(); 를 반드시 추가해야 하는데 그러지 못했었다.


즉, 시뮬레이션 타임이 끝날 때까지 내가 직접 열어 놓은 모든 소켓이 하나도 안 닫히고 끝까지 열려 있었고 시뮬레이션 규모가 작을 때에는 그게 문제가 되지 않다가, 보내는 패킷 개수를 대폭 늘렸더니 에러는 없지만 시뮬레이션을 마치는 데 걸리는 실제 시간이 너무 오래 걸리는 것이었다. 내 컴퓨터에서 40초짜리 point-to-point 네트워크 환경에 대한 시뮬레이션을 돌리는 데 6-7시간이 걸렸다.


처음에 원인을 모를 때에는 내가 file I/O를 너무 많이 열어 놔서 그런가 하는 생각에 특정 로그를 기록하기 위해 열어 놓은 모든 파일 포인터를 다 주석처리해 보기도 했고, 콘솔 화면에 찍히는 메세지를 다 없애 보기도 했고, 랜덤 숫자 생성을 너무 자주 하는 건가 해서 그 부분까지 랜덤을 쓰지 않도록 했는데도 여전히 특정 시간대에서 거의 진행을 하지 못하고 한참을 멈춰 있는 것이었다.


그리고 그 때의 CPU 사용량은 코어 1개를 100% 사용하고 있었는데, 내가 parallel processor (mpi)를 쓰지 않았기 때문에 ns-3가 내부적으로 엄청나게 많은 정체불명의 연산을 하고 있다는 추측밖에 들지 않았다. 그 때에 메모리 사용량이 같이 늘어났다면 내가 뭔가 데이터 구조를 잘못 관리했다거나, 버그가 있어서 데이터가 무한정 늘어난다거나 하는 추측이라도 했을 텐데 메모리 사용량도 늘어나지 않았다.


시뮬레이션 전체 시간 40초 중에서 유독 33초 전까지는 1분도 안돼서 진행을 하다가, 그 뒤부터 갑자기 원인 불명으로 느려지면서 6시간을 소비하는 것이 이상해서, 보내는 패킷의 개수를 줄였더니 전체 시뮬레이션이 1분 만에 잘 끝나는 것이었다.


생각해 보니, 약 15~20초 사이에 4개의 트래픽을 생성하고, 매 초마다 250~300개 정도의 패킷을 보내도록 했더니 33~34초 정도에서 항상 멈추는 것이 확인되었고, 그 중에서 똑같은 링크를 통해서 3개의 트래픽이 동시에 지나가는 영역이 있는데, 시뮬레이션 시간으로 약 33초 정도면 그 링크를 지나가는 패킷이 약 15000~20000개쯤 되는 것 같았다.


그러면 ns-3가 실제 TCP/IP 프로토콜과 transport 작동을 그대로 구현했기 때문에 소켓을 하나 열면 보내는 쪽에서는 50001~65535 중에서 아직 다른 소켓에 bind되지 않은 하나의 랜덤 숫자를 찾아서 소켓을 열고, 패킷을 보내는 방식으로 작동할 것이므로, 15535개 이상의 소켓을 열기 시작하면 bind할 포트 번호를 찾지 못할 것이다.


아...

이런 상황에서 실제 컴퓨터는 소켓이 bind를 못한다던지 하는 오류를 냈을 텐데 ns-3는 아무 오류도 없이 bind될 소켓을 찾느라 계속 기다리기만 했던 것이었다. 그런데 bind할 소켓이 하나도 없었을 텐데 실제 시간으로 6시간쯤 걸려서 결국 패킷을 다 보내기는 했다는 것도 신기하네. Timeout 같은 게 설정되어 있어서 그랬을까?


앞으로는 socket->Send를 쓸 때에는 꼭 socket->Close를 잊지 말아야겠다.



반응형
블로그 이미지

Bryan_

,
반응형

OS: Ubuntu 16.04 (amd64)

Version: ns-3.26

gcc: 4.8.4



*결론부터 말하면, 소스 파일 확장자를 반드시 .cc 로 맞추자.



ns-3 에서 바로 시뮬레이션 코드를 테스트할 때 scratch 디렉토리에 소스코드를 집어넣으면 되고, 이 때 객체지향 컨셉을 적당히 쓰기 위해 하나의 클래스를 독립된 헤더와 소스 파일로 만드는 경우가 있다.


헤더와 소스 파일을 만들 때, [클래스 이름].h, [클래스 이름].cc 로 맞춰서 만들어야 하는데, 실수로 소스 파일 확장자를 cc가 아닌 cpp 또는 그외 다른 확장자로 쓰게 되면 ns-3의 waf 스크립트에서 소스 파일만 인식을 못해서 빌드 과정에서 "undefined reference to X" 에러가 발생한다.


문제는 이클립스 CPP 환경에서 ns-3를 프로젝트로 로드해 와서 scratch 폴더 내부에 클래스를 생성할 때에는 소스 파일의 확장자가 .cc이든 .cpp이든 아무 경고 메세지가 발생하지 않는다. 나도 이클립스 CPP에서 New > Class 메뉴를 통해서 자동으로 클래스를 만들면서 소스 파일 확장자가 .cpp로 만들어지는 것을 미처 인지하지 못해서 이 링크 에러를 보게 되었다.


ns-3.26/build/scratch 디렉토리를 살펴 보니 아예 내가 만든 클래스에 대한 오브젝트 파일(.o) 파일이 존재하지 않았다. 그 원인은 ns-3.26/wscript 파일에서 소스 파일을 자동으로 불러올 때 .cc 파일만 인식하게 되어 있기 때문이다.


(ns-3.26/wscript 파일 일부)

...(앞부분 생략)...

elif filename.endswith(".cc"):

name = filename[:-len(".cc")]

...(이하 생략)



wscript 파일을 고쳐서 cpp 파일도 자동 인식하도록 만들어도 되지만, 일단 ns-3에서 scratch 디렉토리에 대해서 미리 정해져 있는 컨벤션이고, 나중에 시뮬레이션 코드만 다른 컴퓨터의 다른 ns-3 버전에서 돌려야 할 때 같은 문제가 또 발생하게 되므로, 그냥 소스 파일을 .cc로 맞추는 것이 안전한 방법이 되겠다.



반응형
블로그 이미지

Bryan_

,