c# async/await

심도있게 들어가면 꽤 까다롭네..

몰랐던 사실중에 중요한 내용은 Console 과 WPF 에서 async/await 의 thread 수행 형태가 다르다!

async/await에 대한 “There Is No Thread” 글의 부가 설명

async/await 사용 시 hang 문제가 발생하는 경우

C# 5.0 : async / await 키워드

C# async await explained

C# Task vs ValueTask

백만년만에 쓰는 포스팅…

나이가 들어서 이해했다가도 또 까먹는다 ㅠ_ㅠ

https://www.linkedin.com/pulse/valuetask-vs-task-c-ehsan-ehsani-athar/

https://www.csharpstudy.com/Latest/CS7-async-return.aspx

https://kangworld.tistory.com/195

https://www.sysnet.pe.kr/2/0/12713

https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#prefer-taskfromresult-over-taskrun-for-pre-computed-or-trivially-computed-data

linux top 명령에서 PR 과 NI값 설명

서론

리눅스에서 top 명령을 치면 항목중에 PR(priority) 항목과 NI(nice) 항목이 존재한다.
이 글에서는 PR(priority) 과 NI(nice) 값에 대해서 정리한다. 언제나 그렇듯 틀린 내용일 수 있음.

  • PR은 프로세스 속성값중 priority값을 보여주는 항목이다.
  • NI는 프로세서 속성값중 nice값을 보여주는 항목이다.

그런데 top명령이 priority와 nice값을 있는 그대로 보여주는게 아니라 약간 계산을 해서 보여주기 때문에 혼란이 발생한다.

무슨 이야기냐 하면, 원래 PR(priority)값은 범위가 0 ~ 139까지이고, NI(nice) 값은 범위가 -20~19 까지이다.그런데 top명령에서 PR값은 RT ~ 0 범위로 표현하고, NI 값은 -20~19 까지로 표현한다. nice값은 제대로 보여주는데 priority값은 좀 변형해서 보여주고 있다. 이 부분을 설명한다.

NI(nice)에 대하여

nice 값은 프로세스가 얼마나 친절하냐(?)를 지정한다

nice 값은 -20 에서 19 사이에 값을 지정할 수 있으며, -20이 가장 불친절하기 때문에 더 많은 cpu자원을 쓰게 스케줄링 한다. 19는 가장 친절해서 cpu를 가장 덜 쓰게 스케줄링 한다. 기본적으로 손대지 않으면 0값을 가진다.

쉘에서 특정 프로세스의 nice 값을 바꾸는 명령은 아래와 같다
2997번 프로세스의 nice 값을 -20으로 변경한다

# renice -n -20 -p 2997

코드에서 nice 값을 바꾸는 예제는 아래와 같다

int which = PRIO_PROCESS;
id_t pid;
int priority = -20;
int ret;

pid = getpid();
ret = setpriority(which, pid, priority);

nice 값이 효과를 발휘하는 범위는 동일한 일반 유저 프로세서들 사이에서만 그렇다. nice 값은 일반적인 프로세스(유저 프로세스)에서 우위를 지정하게 해준다. 리눅스 시스템 전반(커널 스레드, 리얼 타임 프로세스, 일반 프로세스)에서 우선순위를 지정하는 값은 priority 이다.

nice 값은 priority값의 일부에 해당하는 영역에 불과하다. 그래서 nice 값와 priority 값은 상관관계가 있으며 변환식에 의해서 값 변화가 가능하다(변환식 아래 있음). 이런 nice 값을 가지고 우선권이 조정되는 프로세스들은 SCHED_OTHER, SCHED_BATCH, SCHED_IDLE 타입으로 지정되어 있다.

스케줄러의 종류와 우선순위 min/max를 보고 싶다면 아래 명령을 입력한다.

# chrt -m

4021번 프로세스가 어떤 스케줄러에 속해 있는지 알고 있다면 아래 명령을 입력한다.

# chrt -p 4021

PR(priority)에 대하여

nice 값을 이용하지 않고, priority 값만을 따지는 프로세스들이 있다. 리얼 타임 프로세스들이다. 모든 프로세스는 priority값을 가지고 있으며 그 범위는 1 ~ 139 까지 지정할 수 있다. 1이 가장 우선순위가 높고, 139가 가장 우선순위가 낮다. 리얼타임 프로세스들이 지정할 수 있는 priority 범위는 (1~99)까지이다. 리얼 타임 프로세스들은 언제나 일반 프로세스들보다 우선순위가 높아야 한다. 그런 이유로 nice 값만으로 변경할 수 있는 priority 범위는 100 ~ 139 까지이다.

  • nice 값 -20 이 priority 값 100 이다 (nice 중에서는 최고 높은 우선순위)
  • nice 값 19 가 priority 값 139 이다 (nice 중에서는 최고 낮은 우선순위)

그런데 top 명령에서 priority값을 보여주는 PR 값은 위 공식대로 보여주지 않는다. 약간 변경해서 보여준다. 아래는 PR값의 계산 공식이다.

  • 일반적인 프로세스 PR = 20 + NI (NI의 범위는 -20 에서 19 까지)
    • NI값 -20 는 PR값 0 이다 (priority 값은 100, nice 중에는 최고 우선 순위)
    • NI값 0 는 PR값 20 이다 (priority 값은 120, nice 미 지정시 일반적 우선 순위)
    • NI값 19 는 PR값 39 이다 (priority 값은 139, nice 중에는 최하 우선 순위)
  • 리얼타임 프로세스 PR = -1 – rt_priority (rt_priority의 범위는 1 에서 99 까지)
    • rt_priority값 99 은 PR값 -100(rt) 이다 (priority 값은 1, real time 중 최고 우선 순위)
    • rt_priority값 1 은 PR값 -2 이다 (priority 값은 99, real time 중 최저 우선 순위)

위 공식에 따라서 PR이 양수라면 일반 프로세스를 뜻하며, PR값이 음수라면 리얼 타임 프로세스를 뜻한다.

  • PR값이 -100에 가까울 수록 더 높은 우선순위를 가진다
  • rt_priority값이 99에 가까울 수록 더 높은 우선순위를 가진다
  • priority값이 1에 가까울수록 더 높은 우선순위를 가진다

이제 nice값 수정으로는 올릴수 없는 rt_priority 1~99 값을 지정하는 방법을 알아보자.

6097번 프로세스를 fifo 스케줄러에 rt_priority 99로 설정하려면 아래와 같이 입력한다.

# chrt -f -p 99 6097

위 명령을 치고 top 명령을 치면 해당 프로세스의 PR값이 rt 라고 보여지게 된다. 가장 높은 우선순위를 지정받게 된 것이다. rt_priority 값 99는 priority 값 1에 해당한다.

6097번 프로세스를 fifo 스케줄러에 real_time_priority 1로 설정하려면 아래와 같이 입력한다.

# chrt -f -p 1 6097

참고 1
루트 유저가 아닌 프로세스에 대해서 /etc/security/limits.conf 파일을 통해서 nice 값 변경 범위를 제한할 수 있다.

참고 2
커널은 CPU사용량에 따라서 특정 프로세스의 priority값을 동적으로 변경할 수 있다. 이 경우 nice값을 손대는게 아니기 때문에, PR = 20 + NI 공식이 안맞게 된다. 하지만 일반적인 경우에는 이 공식이 맞다. real time priority는 커널이 priority를 임의로 손대지 않는다.

참고 3

chrt: failed to set pid 0's policy: Operation not permitted

chrt 명령으로 rt_priority를 변경하려고 했는데 위 에러가 발생한다면, 아래 명령을 입력한다

# sysctl -w kernel.sched_rt_runtime_us=-1

참고자료
* https://blog.naver.com/PostView.nhn?blogId=alice_k106&logNo=221149061940&parentCategoryNo=&categoryNo=23&viewDate=&isShowPopularPosts=false&from=postList
* https://code.i-harness.com/ko-kr/q/879ceb
* https://www.ibm.com/developerworks/library/l-lpic1-103-6/index.html
* https://askubuntu.com/questions/656771/process-niceness-vs-priority
* https://superuser.com/questions/203657/difference-between-nice-value-and-priority-in-the-top-output
* https://m.blog.naver.com/PostView.nhn?blogId=alice_k106&logNo=221170316769&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F

uVision&J-Link를 이용한 디버깅

  1. Options for Target 윈도우에 들어가서 Debug Tab으로 간다

스크린샷 2016-07-25 오후 5.56.07.png

Use : J-Link / J-TRACE Cortex를 선택

  1. 오른쪽에 있는 Setting을 누른다

스크린샷 2016-07-25 오후 5.57.31.png

Port : SWD 로 변경

Max Clock : 50Mhz로 변경 ( 이건 좀 확인해봐야 할듯..)

  1. Flash Download Tab으로 이동한다

스크린샷 2016-07-25 오후 5.59.27.png

위 그림처럼 설정한다

확인 누르고 나온다

  1. 다시 옵션 화면으로 나왔다면, Utilities 탭으로 이동한다

스크린샷 2016-07-25 오후 6.01.19.png

Use External Tool for Flash Programming 선택

Run Independent 체크

concurrently, parallelism, asynchronous

concurrently(동시성), parallelism(병렬화), asynchronous(비동기)

이름부터 어렵다

비슷한것 같으면서도 다르기 때문에 혼란을 야기하는데, 여기에 정리해놓고 리마인드 해야지

다만, 이 내용이 정확한 내용인지는 나도 장담 못함…

concurrently(동시성)

여러 작업이 번갈아 가면서 동시에 실행되는 것을 말한다

동시라고는 하지만 실제로는 Context Switching에 가깝다

따라서 Single core에서도 동시성이 있는 프로그래밍을 하고 수행할 수 있다.

thread 기반 프로그래밍이 동시성 프로그래밍의  좋은 예이다.

single core에서도 다수의 thread를 만들어서 수행할 수 있으며, 사용자가 보기에 동시에 실행되는 것처럼 빠르게 번갈아 가면서 수행된다

concurrently 프로그래밍은 태생적으로 resource에 대한 race condition이 발생 한다.

예를 들어서 전역 변수를 접근하는 concurrently 프로그램을 짠다면 하이젠버그를 만나게 될 것이다.

parallelism (병렬화)

여러 작업이 다수의 core에서 동시에 수행되는 것을 말한다.

병렬화 프로그래밍은 single core에서는 불가능하다. 반드시 다수의 코어가 실제적으로도 동시에 일을 해야 하는 것을 말한다.

이런 의미에서 보면 동시성 프로그래밍을 한 후에, multicore에서 실행하면

“야호~ 나 프로그램을 병렬화 했어” 라고 말할 수 있을까?

OS가 알아서 작업을 각각의 core에 알아서 스케쥴일 해줄테니 말이다.

잘 모르겠지만, 아닌것 같다.

병렬화는 프로그래머가 명시적으로 해당 부분을 강제로 다수의 코어가 나누어서 수행한다는 의미가 강하다.

예를 들어서 1에서 100까지 더해서 합하는 프로그래밍을 한다고 해보자.

이때 병렬화 프로그래밍을 한다는 것은, 1~50까지는 1번 코어가 계산하도록 하고, 51~100까지는 2번 코어가 계산을 하도록 한후에 나중에 합치는 프로그래밍을 할 것이다.

보통 동시성 프로그래밍을 한다고 하면, 위 작업같은 조치를 취하지 않는다.

병렬화 프로그래밍은 대부분이 프로세서가 시간이 오래 걸리는 연산 작업을 다수의 코어가 나눠서 같이 계산을 한다는 의미로 많이 쓰인다.

이런 용도로 병렬화 라이브러리가 OpenMP이다. OpenMP를 쓰는 프로그래밍은 병렬화 프로그래밍이라고 할 수 있다.

병렬화도 잘못 짜게 되면 resource에 대한 race condition을 유발 할 수도 있지만, OpenMP같은 라이브러리만을 이용해서 활용한다면, 그 문제가 거의 발생하지 않는다.

con_and_par.jpg

위 그림은 concurrent 와 parallelism 의 차이를 보여준다

asynchronous (비동기)

비동기 프로그래밍은 하나의 작업의 흐름에서 프로그램의 수행이  블록되지 않고, 계속 실행하게 만드는 기법이다.

프로그램이 블록 되는 이유는 대부분이 I/O 처리때문이다. (HDD, Network, Display, Mouse, …)

예를 들어서 아래와 같은 코드를 보자.

read_storage(buffer, 2000);

위 코드는 저장장치에서 2000바이트를 읽어서 buffer에 저장하라는 코드이다. (pseudo code임)

위 코드는 2000바이트를 읽을 때 까지 해당 코드가 멈추게 된다. 그냥 하릴없이 멈춘다. 저장장치가 2000바이트를 다 줄때까지…

그런데 해당 함수를 호출한 thread만 멈춘다. 다른 thread는 안 멈춘다.

다른 thread에는 영향을 안주고 자신만 멈추니까 비동기 프로그래밍의 매력이 별로 없어보인다.

GUI 프로그래밍을 생각해보자. 위 read_storage 처리 함수와 GUI 의 다양한 처리를 하는 함수들이 같은 thread에서 동작한다고 생각해보자. 그러면 저 함수가 리턴될 때 까지 화면은 멈추게 되고, 응답없음 이란 글자를 보게될 것이다.

그래서 과거에는 GUI 프로그래밍을 할  때 GUI 를 처리하는 thread에서는 저런 block이 발생하는 작업을 안하고 다른 thread에게 시키던지, non-block 함수를 써서 계속 polling으로 I/O 처리가 끝났는지 확인하곤 했다.

figure1.gif

위 그림은 동기,비동기 / 블럭,논블럭 차이에 대한 설명이다.

사실 비동기 프로그래밍은 병렬화, 동시성 프로그래밍 하고는 같은 층위의 개념은 아니다. 병렬화, 동시성 개념은 다수의 작업을 코어에 어떻게 할당하는가에 대한 논의라면, 비동기 프로그래밍은 하나의 작업이 멈출지, 말지에 대한 논의이다. 좀 더 하위 개념에 가까운것 같다.

그럼에도 불구하고  비동기 프로그래밍은 multi-thread 프로그래밍에 좋은 대안이다. 실제로 multi-thread 프로그래밍보다 비동기 프로그래밍이 성능이 좋다. (정말?)

그 예시가 바로 node.js 이다.  참조( http://www.nextree.co.kr/p7292/ )

비동기 프로그래밍의 가장 큰 예시는 callback, EventEmitter 이다.

2018년 6월 스크랩✭

c++ 에서 static library 와 template

c++에서 템플릿의 구현체는 기본적으로 해더파일에 구현해야 한다.
템플릿의 내용을 cpp안에 넣어도 해당 코드를 라이브러리 형태로 다른곳에서 참조하지만 않으면 문제가 없다.
문제는 해당 cpp파일을 라이브러리(.a)로 만든다음에, 다른 프로그램이 링킹하려고 한다면 심볼을 찾을 수 없다는 오류가 발생한다.

예를 들어 아래와 같은 경우다.

//template.h
class NSFinder {
public:
    template <typename T>
    static T getParam(T val);
};

//template.cpp
template <typename T>
T NSFinder::getParam(T val) {
    return val
}

//other program
int main() {
    //에러, 함수를 찾을 수 없다
    int temp = NSFinder::getParam<int>(1);
}

가장 올바른 해결책은 h에 템플릿 코드를 넣는것이다. 하지만 이 방법말고도 가능한 방법이 있다.

1.라이브러리 코드 안에서 컴파일시에 필요한 템플릿 객체 강재로 생성하기.

아래 코드 처럼 템플릿이 존재하는 cpp안에서 필요한 타입의 템플릿 객체를 이용하면, 라이브러리안에 실체가 만들어지기 때문에 링크 에러가 발생하지 않는다. 단점은, 명시적으로 어떤 객체를 사용할지 선언해준 범위에서만 사용가능 하다.

//template.h
class NSFinder {
public:
    NSFinder();
    template <typename T>
    static T getParam(T val);
};

//template.cpp
template <typename T>
T NSFinder::getParam(T val) {
    return val
}

NSFinder::NSFinder() {
    //라이브러리 코드 안에서 한번 사용한다.
    int temp = NSFinder::getParam<int>(1);
}

//other program
int main() {
    //빌드 성공
    int temp = NSFinder::getParam<int>(1);
    //이건 실패한다
    double temp = NSFinder::getParam<double>(1.0);
}

2.템플릿 특수화를 이용해서 만들어준다. 이 방법은 코드가 중복되는 단점과, 명시적으로 어떤 객체를 사용할지 선언해준 범위에서만 사용 가능하다.

//template.h
class NSFinder {
public:
    NSFinder();
    template <typename T>
    static T getParam(T val);
};

//template.cpp
//템플릿 특수화
template <> 
int NSFinder::getParam(int val) {
    return val
}

template <typename T>
T NSFinder::getParam(T val) {
    return val
}

//other program
int main() {
    //빌드 성공
    int temp = NSFinder::getParam<int>(1);
    //이건 실패한다
    double temp = NSFinder::getParam<double>(1.0);
}

여러모로 불편하니, 그냥 해더파일에 템플릿을 구현하는게 정답이다

11월 스크랩

ARM Ubuntu 14.04에 최신 docker 설치기

회사 타켓보드에 개발 용이성을 위해서 ARM Ubuntu14.04에 docker를 올리는 시도를 했다

그냥 간단하게는 아래 명령으로 되는데,

$ sudo apt-get install docker.io

Ubuntu 14.04는 너무 오래되서, docker 버전이 너무 낮아서 시도를 하지 않았다. (실제로 저 명령으로 docker 설치가 잘 되는지 모른다는 이야기다)

처음부터 바로 docker ppa에서 바로 최신 버전을 받아서 설치를 진행했다.

https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/#upgrade-docker-after-using-the-convenience-script

그리고 실행하니까 커널에 필요한 기능이 추가되어 있지 않아서 데몬이 동작하지 않는다.

첫번째 이슈는 cgroup이었다. 커널에 cgroup 자체는 활성화되어 있었지만, blkio같은 서브모듈이 비활성이어서 전부 활성화 처리 해주었다.

두번째는 network nat기능이었다. 이 부분은 너무 옵션이 많이 필요해서 관련 자료를 참고했다.

스크린샷 2017-11-03 오전 10.49.09.png스크린샷 2017-11-03 오전 10.49.17.png스크린샷 2017-11-03 오전 10.49.23.png

위에는 falinux에서 진행한 docker세미나 자료이다.

https://wiki.gentoo.org/wiki/Docker

위는 gentoo에서 제공하는 docker 커널 옵션이다. 나는 잘 안되서 2개를 다 시도 했다. 따라서 위 2개중에 어떤게 맞는지는 알수없다. 나는 그냥 2가지를 다 활성화시켰다.

그래도 네트워크쪽에서 아래와 같은 오류가 발생했다.

failed to create endpoint vigilant_varahamihira on network bridge: failed to add the host (vethadd7f95)  sandbox (veth63708ff) pair interfaces: operation not supported"

해결책은 아래 커널 옵션을 활성화 해준다

device driver -> network device support -> network core driver support -> virtual ethernet pair device

그래도 아래와 같은 오류가 발생했다.

starting container process caused "process_linux.go:368: container init caused \"rootfs_linux.go:57: mounting \\\"mqueue\\\" to rootfs \\\"/var/lib/docker/devicemapper/mnt/e8d53dff710da9a18279cec3db4fb324ecc1874b5f3fa2e4496b94c31938d715/rootfs\\\" at \\\"/dev/mqueue\\\" caused \\\"no such device\\\"\"".

해결책으로 CONFIG_POSIX_MQUEUE 커널 옵션을 활성화 했다

그래도 아래와 같은 오류가 발생했다.

oci runtime error: container_linux.go:265: starting container process caused \"process_linux.go:368: container init caused \\"open /dev/ptmx: no such file or directory\\"\"

해결책으로 아래와 같이 -v 옵션을 추가해서 실행했다.

$ sudo docker run --rm -it -v "/dev:/dev" arm32v7/ros:kinetic-ros-base /bin/bash

2017년 10월 스크랩✭