Tips2020. 9. 26. 03:18

npm을 통해 typescript 설치 시 -g 옵션을 통해 globally 하게 typescript를 설치했다 하더라도, 이를 vs code에서 실행할 때 아래와 같은 문제가 발생하는 경우가 있다.

+ tsc
+ ~~~
    + CategoryInfo          : 보안 오류: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

이는 해당 커맨드에 대한 접근 권한이 부여되지 않은 경우로써, 해결방법은 다음과 같다.

  1. 관리자 권한으로 Power Shell 실행.
  2. Get-ExcutionPolicy 명령 실행.
  3. "RemoteSigned" 권한이 아닐 경우 (보통 위와 같은 문제가 발생한다면 "Restricted" 로 나올 것이다), 아래의 명령 실행.
    1. Set-ExecutionPolicy RemoteSigned
  4. 다시 Get-ExecutionPolicy 명령을 실행하여 "RemoteSigned" 로 바뀌었는지 확인.

명령을 실행하면 보안 문제가 발생할 수 있다고 경고가 나올 것이다. 이를 다시 Restricted로 바꿀 경우, 마찬가지로 tsc 명령이 먹히지 않는다. (vs code가 아닌 일반 커맨드 프롬프트에서는 정상적으로 실행된다. vs code 와 같은 타 애플리케이션에 권한을 주지 않는 것으로 보인다.) RemoteSigned 권한일 경우, vs code에서도 정상적으로 tsc와 같은 커맨드가 실행된다.

아래의 포스팅을 참고하였다.

dog-developers.tistory.com/183

Posted by 곰푼
Tips2018. 8. 29. 02:38


Docker는 컨테이너의 일종으로써, 리눅스, MacOS, 윈도우즈와 같은 많은 OS에서 동작한다. 

본 포스트는 리눅스 기반의 docker를 기준으로 작성된 포스트이므로, 다른 OS에는 해당사항이 있을 수도 있고 없을 수도 있다.


Docker도 결국 주어진 머신을 최대한 잘 활용하기 위해 사용되는 것이다.

최근 각광받는 기술이긴 하지만 Docker와 같은 컨테이너들은 역사 자체는 오래되었다. 

가상화 얘기가 한창 나오던 십수년전부터 OS 수준 가상화(OS-level virtualization)라는 이름으로 연구되었던 영역이다. 

이러한 카테고리에 속하는 가상화 기술의 주 목적은 다음과 같다.


  • OS 및 런타임(JAVA, Python 등)과 같은 코드 기반들의 공유.
  • CPU, 메모리와 같은 물리적 자원 및 네트워크 연결, 파일시스템 등의 논리적 자원의 분배 및 고립(isolation). 


OS 및 런타임과 같은 요소들까지 서로 고립시키는 전가상화(full-virtualization) 내지 반가상화(para-virtualization) 기술에 비해 오버헤드가 상대적으로 덜한 기술이라 할 수 있다.

따라서, 유사한 시스템 및 런타임 기반을 가진 애플리케이션을 구동시킬 경우, Docker와 같은 컨테이너 서비스는 아주 좋은 선택이라 할 수 있다.


리눅스 환경에서 동작하는 Docker의 경우 메모리와 같은 자원을 분배하기 위해 cgroup이라는 매커니즘을 활용한다. 

cgroup은 control group의 약자로써, 특정 프로세스 혹은 프로세스 그룹이 사용할 수 있는 자원의 상한을 정해두고, 그 이상으로 사용하지 못하도록 커널 수준에서 제어하는 매커니즘의 명칭이다. 

cgroup은 여러 자원들에 대한 컨트롤러를 제공하는데, Linux 4.5 버전이 되며 덩달아 버전 업 된 cgroup v2에서는 CPU, Memory, I/O의 세 가지 컨트롤러를 지원한다. 

상대적으로 역사가 오래된 cgroup v1은 이보다 많은 컨트롤러를 지원하는데, 이로 인해 아직까지 docker에서는 cgroup v1을 활용한다고 한다. (명시적으로 표기된 공식 문서는 발견하지 못했지만, cgroup v2로 커널을 설정할 경우 Docker가 cgroup을 인식하지 못한다.)


자 그럼 왜 이번 포스팅의 주제인 MongoDB와 Docker 얘기를 꺼내기전에 왜 이런 장황한 이야기를 늘어놨는지 밝히도록 하겠다. 

cgroup은 결국 논리적으로 자원을 분배한다. 그렇기 때문에 docker 컨테이너를 생성한후, 컨테이너에 접속하여 cat /proc/meminfo와 같은 명령을 입력하게 되면 호스트 머신이 가진 자원이 그대로 보이는 것을 확인할 수 있을 것이다. 

예를 들자면 이런 것이다.

16G의 메모리를 탑재한 머신에 네 개의 컨테이너를 생성한다고 가정하자. 

메모리를 최대한으로 허용한다면, 각각의 컨테이너에게 4G씩의 메모리를 할당할 수 있을 것이다. (물론 호스트의 OS 및 서비스를 위해 가용 메모리를 남겨둬야 한다. 이것은 단순히 예시일 뿐이다.)

즉, 우리가 기대하는 것은 각 컨테이너가 최대 4G의 메모리를 사용하는 것이다.

그리고 그 컨테이너에서 MongoDB의 인스턴스를 구동시켜보자. 

MongoDB의 WiredTiger 스토리지 엔진은 자체적으로 데이터베이스의 레코드들을 캐시해두기 위한 in-memory cache를 생성하게 된다. 

이때 in-memory cache의 크기는 1GB 혹은 (memory size / 2) - 1G 중 큰 쪽으로 선택된다. 

여기서 저 memory size가 각 컨테이너에게 할당된 메모리 크기가 된다면, (4G / 2) - 1G 이므로 1G 크기의 in-memory cache를 가질 것이라 예측할 수 있다. 

그리하여 MongoDB를 구동시키고 시간이 지난다면... 짜잔! 무언가 잘못되었다는 것을 느낄 수 있을 것이다. (물론 운이 좋으면 혹은 나쁘다면 잘못되지 않고 계속 실행된다.)


그 이유는 단순한데, 컨테이너에서 구동되고 있는 MongoDB가 컨테이너에게 할당된 메모리가 아닌 호스트 머신이 가지고 있는 메모리를 인식하여 in-memory cache를 생성하기 때문이다. 

MongoDB에 리퀘스트가 도달할 때마다 MongoDB는 I/O를 줄이기 위해 레코드들을 캐싱해둘 것이고, 캐싱된 데이터의 크기가 현재 할당된 in-memory cache 사이즈를 넘을 경우 MongoDB는 메모리를 추가로 요청하게 된다. 

만약, 미리 설정된 메모리 크기를 넘어설 경우 (예를 들어, (memory size / 2) -1G의 크기를 넘어설 경우), MongoDB는 캐시에서 오래된 레코드들을 찾아 디스크에 저장하고 메모리 공간을 회수한 뒤 회수된 메모리 공간에 새로운 레코드를 캐싱하게 된다.

안넘어섰을 경우에는 미리 설정된 사이즈까지 메모리를 추가로 요청하게 된다.

그리고 바로 이 시점이 오류가 발생하는 시점이다. 


컨테이너에 할당된 메모리 사이즈와 애플리케이션이 인식한 메모리 사이즈 사이의 정보 격차로 인해, 컨테이너에 할당된 메모리 사이즈 이상의 메모리 요청이 발생할 경우 해당 요청은 거부되고 애플리케이션이 오동작을 일으키는 것이다. 

경우에 따라서 OOM killer가 발생할 수도 있고, 이것을 옵션으로 막아두었다면 컨테이너가 deadlock 상태에 빠지거나 그냥 꺼질 수 있다 (여기까지는 직접 발견한 증상이다). 


이를 방지하기 위해서는 wiredtiger의 캐시 사이즈를 제한할 필요가 있다. 

이는 --wiredTigerCacheSizeGB  라는 옵션을 통해 설정할 수 있다. 

MongoD 인스턴스를 실행시킬 때, 다음과 같은 옵션을 덧붙이면 된다.

$mongod --wiredTigerCacheSizeGB  2G --config <config path>

그럼 in-memory cache의 크기는 2G로 제한이 되고, 위에서 발생가능한 오류들을 사전에 방지할 수 있다. 


사실 이러한 semantic gap으로 인한 버그들은 굉장히 많을 것이다. 

이때문에 verification이라던지, gap을 없애려는 노력들을 하는 것일 거고...

앞으로 이와 관련된 연구를 좀 해볼 수 있을려나...?

Posted by 곰푼
Tips2018. 4. 27. 09:21

보통 make 혹은 cmake 등으로 빌드를 수행할 때, undefined reference 에러가 발생하는 경우가 있을 것이다. 

필요한 라이브러리를 -l 옵션을 사용하여 링커에게 전달하지 않았거나, 라이브러리 간의 order가 맞지 않을 때 발생하는 문제이다. 예를 들어 pthread를 사용한다면, 다음과 같이 옵션을 주면 된다.

$ gcc a.c -o a -lpthread

코드가 pthread를 사용하여 구현되었을 경우, 해당 실행 파일(여기서는 a) 내에는 pthread관련 함수의 바이너리가 없기 때문에 실제 코드가 구현된 pthread 라이브러리를 링크시켜주어야 한다. 이 역할을 수행하는 것이 바로 링커(리눅스에서는 보통 ld)이며, 링커에게 링크 해야하는 라이브러리를 알려주는 지시자가 바로 -lpthread 가 되겠다.

물론, 이걸로 해결안되는 경우도 많다. 사실 이 경우 때문에 이 포스팅을 남기기도 하는 것이고.

내가 작성한 라이브러리를 사용하여 다른 애플리케이션을 수정할 일이 생겼다. 그래서 애플리케이션 코드를 수정하고, cmake 스크립트를 수정하여 라이브러리를 링크하도록 만들어 놓고 빌드를 수행하였는데, undefined reference 에러가 발생하는게 아닌가?

여러 삽질을 해봤는데, 결과는 영 꽝이었고, 혹시나 싶어 nm을 통해 코드에서 심볼들이 어떻게 정의되어 있는지 보았다. 애플리케이션도 라이브러리로 컴파일 된 뒤, 다른 실행 파일에서 이를 링크하여 사용하는 방식으로 되어 있기 때문에, 여기서는 애플리케이션이 작성한 라이브러리를 확인하였다.

$ nm app.a | c++filt | grep pthread

                 U pthread_cond_broadcast

                 U pthread_cond_init

                 U pthread_cond_signal

                 U pthread_cond_wait

                 ...

$ nm app.a | c++filt | grep my_lib_func

                 U my_lib_func1(my_struct*)

                 U my_lib_func2(unsigned long, my_struct*, int)

                 U my_lib_func3(unsigned long, my_struct*)


차이가 눈에 띄는가?

pthread 함수의 경우, argument에 대한 정의가 심볼에 포함되어 있지 않지만, 내가 작성한 라이브러리의 경우 심볼에 argument의 정의가 포함되어 있다.

이런 이유가 발생한 이유는 따지고 보면 간단한데(물론 찾는 것은 간단하지 않았다.), 나는 C언어를 사용하여 라이브러리를 작성하였고, 애플리케이션은 C++로 구현되어 있기 때문이었다. 

C++는 오버로딩과 같은 특성을 지원하기 때문에, 심볼을 다룰 때 간결한 C언어와 차이점을 보이게 되는 듯 하다. 

해결책은 간단하다. 

헤더파일의 선언문들을 extern "C" { } 로 감싸주면된다. 물론, C++의 경우에만 이를 적용하면 되므로 #ifdef __cplusplus와 같은 매크로를 이용해 구분을 해주면 더욱 좋겠다.,

이렇게 헤더파일을 수정할 경우, 실행파일의 심볼 테이블에서 argument에 대한 정의가 제거된다. 

$ nm app.a | c++filt | grep my_lib_func

                 U my_lib_func1

                 U my_lib_func2

                 U my_lib_func3

빌드해본 결과, 에러없이 잘 빌드되는 것을 확인할 수 있었다.

Posted by 곰푼
Tips2017. 10. 20. 01:32

재밌는 현상을 발견해서 포스팅을 남긴다.


Python으로 class를 지정해서 코딩할 때, 한 클래스 내에서 리스트를 사용할 일이 있어서 다음과 같은 형태로 클래스를 정의했다.


class foo:
    name    = None
    foo_list = []


해당 클래스의 객체를 여러개 만든 뒤, 들어오는 input에 대해 name을 확인하여 각기 다른 foo 객체의 foo_list에 삽입하도록 했다. 

이후 여러 foo 객체를 오가며 foo_list에 삽입된 항목들을 출력하도록 했더니... 오잉? 모든 항목이 동일한 리스트에 들어가 있는 것이 아닌가?


열심히 디버깅 하다가 미심쩍은 부분이 있어서 다음과 같이 변경하였다.


class foo:
    name    = None
    foo_list = None
    def __init__ (self):
        foo_list = []


그러고 나니 각 객체 별로 별도의 리스트를 잘 출력하는 것이었다.

그러니까, class의 멤버 변수를 정의할 때, 초기값을 바로 때려넣어버리면 그게 일종의 static type이 되어서 모든 객체에서 공유하는 멤버 변수가 되는 것이었다. 수정한 코드와 같이 init을 통해 객체가 생성될 때 리스트를 선언하여 사용하면 각 객체 별로 별도의 리스트를 가질 수 있게 되는 것이다.

뭐, 저 용어들이 엄밀하게 따져서 맞는 용어인지는 모르겠다. 어쨌든 내 전문 분야는 C 언어이고, C 언어의 지식으로 해석해봤을 때 저게 static type (혹은 전역변수)으로 보일 뿐이다.



Posted by 곰푼
Tips2014. 2. 10. 22:35


안드로이드 kitkat의 공장 이미지가 나왔다.

리눅스를 사용한다면 다음의 명령으로 다운받을 수 있다.


$wget https://dl.google.com/dl/android/aosp/hammerhead-krt16m-factory-bd9c39de.tgz //4.4

$wget https://dl.google.com/dl/android/aosp/hammerhead-kot49h-factory-02006b99.tgz  //4.4.2


압축을 풀면 안에 설치를 위한 스크립트가 있다.


다음으로는 적절한 펌웨어를 받아야하는데


//NFC, Bluetooth, Wi-Fi

$wget https://dl.google.com/dl/android/aosp/broadcom-hammerhead-kot49h-a670ed75.tgz

//Camera, Sensors, Audio

$wget https://dl.google.com/dl/android/aosp/lge-hammerhead-kot49h-e6165a67.tgz

//Graphics, GSM, Camera, GPS, Sensors, Media, DSP, USB

$wget https://dl.google.com/dl/android/aosp/qcom-hammerhead-kot49h-518133bf.tgz


위는 4.4.2 버전 기준이다.


https://developers.google.com/android/nexus/drivers


위의 페이지를 참고하면 될 것이다.

Posted by 곰푼
Tips2014. 2. 8. 20:24


repo도 업데이트가 된 듯 합니다.


$ curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
$ chmod a
+x ~/bin/repo


새로받으세요!

Posted by 곰푼
Tips2013. 10. 24. 23:30

현재 상황이 최신판 (>= 4.2)에서 하위 버전(ics)으로 안드로이드를 다운그레이드 하려는 상황인데...



이미지를 올려도 부팅이 안된다 두둥!



https://developers.google.com/android/nexus/images 이 페이지를 참고해서 원하는 버전에 맞는 공장 이미지를 덮어 씌울 수 있다.



내가 쓰던게 약주롬이었으므로 약주 버전을 받아서 안에 있는 쉘 스크립트를 실행시키면 일단 해결

Posted by 곰푼
Tips2013. 10. 24. 01:11

4.2 버전 이상이 되면서, 환경 설정에서 개발자 메뉴가 사라졌습니다.


이를 사용하기 위해서는 다소 특수한(?) 작업이 필요합니다.


먼저 환경설정(setting)으로 가서 About phone 메뉴로 갑니다. 


About phone에서 맨 아래로 내리면 build number 가 있을 겁니다. 


이걸 7번 터치해주세요. 친절하게 카운트도 세어줍니다.


그러고 나면 Setting 루트 메뉴에서 개발자 옵션이 생긴 것을 확인 하실 수 있습니다.


왜 이런 뻘짓을 했을까요? -_-;;;


Posted by 곰푼
Tips/Kernel development2012. 9. 30. 01:21

Virtual File System


리눅스를 비롯한 대부분의 유닉스 계열 운영체제는 VFS라는 레이어를 제공한다. 이것은 표준의 SCI(System Call Interface)와 ext2, ext3 를 비롯한 실제 파일시스템 구현 사이에 존재하는 일종의 인터페이스이며, 커널은 이 인터페이스를 통해서 파일시스템 수준에서 동작해야 하는 작업을 처리하기 때문에 가상 파일 시스템(VFS)라는 이름이 붙게 되었다.


그렇다면 왜 VFS가 존재하게 되었는가?


컴퓨터 기술의 발전과 동시에 이를 관리하기 위한 시스템 기술들도 발전하게 되었다. 커널은 비선점형 커널에서 선점형 커널로 발전하였고, 멀티 코어를 지원하기 위한 개념들도 등장하게 되었다. 이는 저장장치와 이 저장장치를 다루는 드라이버 레벨의 소프트웨어, 그리고 보다 추상적인 관점에서 운영체제와 저장장치간의 교두보 역할을 하게 되는 파일 시스템도 마찬가지이다. 기존의 UFS에서 도스 기반의 FAT, 리눅스의 등장과 함께 그 발전을 함께한 ext 계열의 파일 시스템들이 바로 그 맥락에서 발전해온 파일 시스템들이다. 


파일 시스템은 사용자 어플리케이션을 대신하여 파일 레벨의 작업을 수행한다. 사용자 어플리케이션이 표준 라이브러리의 함수를 호출하여 특정 파일에 '쓰기'작업을 한다고 치면, 라이브러리는 커널의 write() 시스템 콜을 호출할 것이다. 커널은 해당 시스템 콜에서 타겟으로 하는 파일이 존재하는 파일시스템에게 write() 시스템 콜에서 정의되어 있는 루틴대로 작업을 시킬 것이고, 파일 시스템은 작업을 처리한 후 커널에게 다시 적절한 값을 리턴할 것이다. 최종적으로 사용자는 커널과 표준 라이브러리로부터 리턴값을 돌려받아 작업이 제대로 처리되었는지를 확인하게 될 것이다.


이는 파일 시스템이 하나만 존재하는 경우에만 성립한다 할 수 있다. 파일 시스템이 둘 이상 존재하게 될 경우, 모든 파일 시스템이 동일한 함수를 제공하지 않는 이상 시스템 콜도 그에 맞추어 서로 다른 루틴으로 작성되어야 할 것이다. 이렇게 되면 새로운 파일 시스템이 생길 때마다 새로운 시스템 콜이 작성되어야 할 것이고, 운영체제도 다시 컴파일 되어야 할 것이다.


이런 문제점을 해결하기 위해서 커널은 모든 파일 시스템이 지켜야할 일종의 표준 모델을 제시하였다. 앞으로 살펴볼 VFS상의 컴포넌트들이 바로 그것인데, 리눅스 커널은 이 모델을 정의하며 객체 지향 모델의 특징을 가져왔다. 하나의 자료구조에 관리를 위한 변수 뿐 아니라 함수의 포인터들의 선언으로 이루어진 테이블을 정의한 것이다. 다음은 표준 모델 중 하나인 struct file의 간략한 예시이다. 


struct file {

struct list_head f_list;

struct dentry * f_dentry;

struct vfsmount * f_vfsmnt;

struct file_operations * f_op;

...

};


struct file_operations {

int (*llseek) (struct file*, int, int);

int (*func2) (int, void *);

...

};


file_operations는 해당 함수의 원형을 정해놓고, 포인터의 형태로 선언한 것이다. 파일 시스템은 이러한 구조체로 제시된 함수의 형식에 맞추어 그 세부사항을 구현하면 된다. 그렇게 되면 커널은 VFS 수준에서 해당 파일시스템의 함수 포인터를 이용한 호출로 작업을 완료할 수 있게 된다. 마치 객체 지향 모델의 인터페이스(혹은 추상클래스)와 같은 모습이라 할 수 있겠다. 


이러한 표준 모형을 제시하기 위해서 VFS는 4개의 객체를 정의한다. 각각은 다음과 같다.

  1. super_block
  2. inode
  3. dentry
  4. file
이 다음부터는 각각의 자료구조와 역할에 대해서 알아보기로 한다.


'Tips > Kernel development' 카테고리의 다른 글

PID Namespace  (0) 2012.03.15
Linux Kernel : 커널이란  (0) 2012.01.31
Posted by 곰푼
Tips2012. 8. 10. 12:43

타겟 소스는 binutils02.21.1이다.

먼저 binutils 패키지를 다운받는다.

http://ftp.gnu.org/gnu/binutils/



압축을 풀고 빌드를 위한 디렉토리를 생성한다.

$tar -xvf binutils-2.21.1.tar.bz2
$mkdir build-binutils
$cd build-binutils

위에서 압축을 푼 디렉토리와 빌드를  위한 디렉토리는 같을 필요가 없다. (다르게 생성하는 것이 좋다.)


 환경설정이 필요한데 여기서는 쉘의 환경변수를 이용한다. 여기서 타겟 머신은 mips 아키텍쳐가 된다. mipsel로 표기된 것은 little endian을 사용하는 mips환경이라는 뜻이다. 만약 big endian을 사용할 것이라면 mips-linux-gnu로 하면 된다.

$export TARGET=mipsel-linux-gnu
$export PREFIX=/home/user/working_dir


이후 빌드를 위한 환경설정을 생성하기 위해 configure를 실행하고, make를 통해 빌드를 수행한다.

$../binutils-2.21.1/configure --target=$TARGET --prefix=$PREFIX

$make

$make install

configure의 옵션에서 --target은 빌드의 결과물이 생성하는 코드가 동작할 환경을 의미한다. 컴파일러를 빌드하는 것으로 예로 들자면, 컴파일러가 생성해내는 코드가 동작할 환경을 의미하는 것이다. 컴파일러 자체가 동작할 환경을 지정해주기 위해서는 --host 옵션을 사용하면 된다.

'Tips' 카테고리의 다른 글

안드로이드 부트로더 문제  (0) 2013.10.24
안드로이드 최신버전(>=4.2)에서 USB Debugging 사용하기  (0) 2013.10.24
Python-Twitter  (0) 2011.07.27
gprolog-GNU's prolog compiler/interpreter  (0) 2011.04.18
A* Search  (0) 2011.04.02
Posted by 곰푼