2016년 3월 13일 일요일

쥬니어 개발자의 해외 취업 준비 #15 프로토타입 전문 컨설팅 회사

약 2주전에 처음보는  회사로부터 연락이 왔다. (알만한 회사는 다 떨어졌으니 그럴만도 하다)

사실 실리콘벨리에 와서 놀란 것 중 하나는 서울경기 지역정도 되는 이 땅에 엄청나게 많은 회사들이 몰려있다는 사실이었다. 대충 감으로... 5배쯤 많이? 근데 왜 내 자리는 없을까? ㅠ

아무튼 전화를 전화를 받았는데 제시? 라고 자기를 소개한 여자 목소리가 들렸다.




헬로 씅화? 너, 내가 메일을 하나 보냈는데 혹시 봤니? 답장이 없어서..

아, 그랬어? 못봤네.. 잠깐만.. 이름이 제시.. 뭐라고?

 xxx 의 제스야. 내가 임베디드 소프트웨어 엔지니어 포지션이 있는데 메일에 간단한 설명과 코드가 있으니까 그것 풀어서 답변 줄래?

(재밌는 경우다. 메일로 코드를 보냈다고?)

응 바빠서 메일을 못봤는데 답장할께.





난 귀찮았지만 그래도 이것도 연습이다.. 라고 생각하고 메일로 코드를 짜서 보내줬다.


그냥 간단한  reverse_string() 함수를 짜는 문제였다. 하나는 call by value, 다른 하나는 call by address로.. (왠지 두 함수 차이를 묻지 않을까?)
그리고 다른 문제
 0x40000000 번지의 값을 읽고 쓰는 매크로를 작성하시오.


아무튼 그렇게 메일을 보내놓고 잊고 있었는데, 1주일이 넘은 뒤에 제스로부터 답장이 왔다.


미안, 오래걸렸지? thank you for your patience 너 다음 인터뷰 잡혔으니 적당한 시간 알려줄래? 실무 엔지니어가 전화를 할거고 30-60분 정도 소요될거야. 아래 사항을 숙지해 줬으면 좋겠어.

-어려운 질문들에 대답할 준비를 하고,
-전화 빵빵 터지는곳에 있고,
-인터넷 되는 컴퓨터 앞에 있어라.




그렇게 이 낯선 회사와의 인터뷰 약속을 잡았다.


오후 2시 점심을 먹고도 배가 고파서 샌드위치를 만들어 먹었는데 좀 더부룩 하다.
딱히 인터뷰 준비는 안하고 그냥 바깥 바람을 쐬다가 전화가 올 시간이 되어 책상에 앉아 이어폰을 꼽고 기다리고 있었다.

샌프란시스코로부터 걸려온 전화는 통화 품질이 별로 좋지 않았다. 내가 VoIP를 사용하고 있었기 때문이었다. 하지만 이것도 조금 익숙해졌다.

자신을 밥이라고 소개한 남자는 먼저 자기 회사를 잘 아느냐고 했다.
모른다고 했더니 회사 설명을 해준다.
본사는 시애틀이고 최근에 샌프란시스코에 오피스를 둔 작은 팀을 꾸리고 있다고 한다. 주로 다른 회사들을 기술적인 컨설팅을 해 주는데 주로 프로토 타입을 개발하고 선행 연구들을 한다고 한다. 자기들이 아마존의 에코를 만들었다고...
나는 에코 디바이스에 굉장히 흥미가 있다고 응수하며 밥과 이야기를 해 나갔다.


나 에코 좋아해 작년에 사서 잘 쓰고 있어! 어메이징한 디바이스더군!

작년에 사서 잘 쓰고 있는 에코!






내가 했던 일들과 간단한 백그라운드 체크가 끝나고 밥의 본격적인 인터뷰 질문을 하였다.




너가 만약 아주 작고 저전력의, 무선통신 네트워크로 연결되고 다중으로 분산된 이미지 처리 디바이스를 만들고 그것들이 서버에 연결되어서 통신을 한다면 디바이스를 어떻게 만들지, 어떤 프로세서를 사용하고 어떻게 설계를 할지 설명해주길 바라.

질문이 잘 이해가 안가는데, 이미지 디바이스는 무엇을 뜻하지? 카메라 센서가 달린 디바이스인가? 그것들이 연결되어 네트워크로 연결되어 있고 distributed system을 이룬다고?

응 맞아. 이럴 경우 너는 이 디바이스를 어떻게 설계할래?


흠.. 재밌는 질문이네.. 일단 작은 임베디드 시스템을 설계한다고 하고 하나의 카메라 센서로부터 데이터를 받을테고 그것들을 네트워크로 전송하게 된다면 압축을 해야겠네. 또 네트워크 스택이 필요할 테고 저전력으로 설계가 되어야 한단 말이지?
일단 주로 임베디드 시스템에서 사용되는 ARM 아키텍쳐 기반의 SoC를 선택했다고 가정하자.  기본적으로 heterogeneous computing 환경이 될거야. 외부 peripherals 로는 이미지 센서가 있고 데이터를 받아들이겠지. 하지만 바로 들어오는 데이터는 raw데이터일 거야. 이게 이미지든 비디오든 압축해야 하는데 그걸 전부 AP에서 소프트웨어로 처리했다간 엄청난 자원을 소모할걸. 그리고 전력 소모도 심하겠지. 그래서 내부적으로 하드웨어 코덱이 내장되어야 할것 같아. 하드웨어 코덱을 제어하기 위한 sub processor들이 몇개 있을 테고 그것들은 또 AP와 통신 하겠지. AP는 조금더 성능이 좋은 프로세서인데 여기엔 아주 안정적인 네트워크 스택이 포함된 OS가 있어야 할것 같아. 리눅스 처럼. 그리고 여기에 어플리케이션을 설계할거고 서버와 통신을 하도록 만들겠지.
음, 서버단도 설명해야 하니?

아니 그정도면 됐어. 내가 원하는 답변으로 충분한거 같아.
좋아 그 다음엔 코드를 같이 보면서 이야기 하자. 브라우저를 열고, 주소창에 collaboedit.com/xxxxx 주소로 접속해봐.


역시 콜라보 에디트 서비스로 코드를 보려고 한다. 밥은 먼저 접속해있었다. 밥은 내가 몇주전 이메일로 보낸 내 코드를 그대로 같아 붙였다. 나는 내 코드를 보면서 밥이 어떤 질문을 할지 궁금했다. 사실 코드는 굉장히 간단한 문제들 뿐이었다.
제스가 이메일로 코드를 보내주고 나는 그걸 이메일로 답장했기 때문에 그 당시엔 그냥 동작 확인만 하고 보냈던거 같다.



오케이, 승화. 아래 코드 중에서 질문이 하나 있는데 말야.. 문자열 크기를 알아내는 곳.. 이 곳 라인에 왜 표준 라이브러리의 strlen을 안쓰고 직접 짰니?

어.. 그건 그냥 너가 알고리즘 자체를 묻는거 같아서 그냥 그렇게 짰어.

어~ 그렇구나. 알겠어 그 다음엔 여기 포인터를 사용했는데 말야..  *(fw) 로 포인터 연산을 했는데 fw[0]을 사용해도 되는데. 왜 그렇게 했어?

응, 뭐 그렇게 해도 동작은 하겠지만 일단 기본적으로 내가 포인터 연산으로 문제를 해결했는데 그대로 코드의 readability를 위해서 그렇게 했어. 사실 배열 스타일로 접근해도 되는데 별로 안좋아해. 여기에 하드코딩으로 [0] 이렇게 쓰면 뭔가 맘에 안들어.

응 그렇구나 하하 나도 포인터 연산을 더 선호해. 그 다음 질문인데.. 여기 아래 코드를 보자.
여기 라인을 실행하면 어떤일이 발생하지?

응.. 그 라인 말이지? 사실 거기는 나 실행을 안했어. 시스템이 다운될 걸 알고 있었거든. 문제는 0x40000000 주소번지에 값을 바로 바꿔버리는 코드인데 거긴 시스템 영역이거든. 아마 프로그램이 죽어버릴거야.

오~ 맞아. 내가 원하던 답이야.
자 그다음 질문은 말야.. 바로 이 4000000 번째 주소의 값이 만약에 하드웨어의 레지스터에 접근해서 데이터를 읽고 쓴다고 치자. 그러면 넌 어떻게 이 코드를 짤래?

(임베디드 소프트웨어 엔지니어들이 받는 아주 간단하고 근본적인, 유명한 질문이다. 나는 아래처럼 코드를 짜고 설명했다.)

응. 하드웨어 레지스터란 말이지? 이렇게 짜야지.
volatile 키워드를 사용해서 값에 읽고 써야해. 이 키워드를 사용해야 컴파일러가 해당 라인을 최적화 해서 지워버리지 않기 때문이지.

Great! 바로 내가 원하던 답이야. 좋아. 이제 거의 3시가 되었어. 미안하지만 나 다음 일정이 있어서 대화는 이제 슬슬 종료해야 겠네.



그렇게 1시간 정도 밥과의 인터뷰는 끝이 났다.



내가 제출한 코드.
그리고 중간중간 밥의 질문들.



 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
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

/* reverses a string in-place */
static void reverse_string(char *string)
{
     //find length
    int size = 0;
    while(*(string+size) != 0) size++;
    // strlen(string); 이렇게 안쓰고 왜 직접 짰나?

    char *fw = string, *bw = string + size - 1;
    char buf;
    for(int i = 0; i < size / 2; ++i){
        buf = *(fw);
        // buf = fw[0];   왜 이렇게 안하고 포인터를 썼는지 물어봤다.
        *(fw) = *(bw);
        *(bw) = buf;
        fw++; bw--;
    }
}

/* returns a pointer to a newly-allocated reversed string */
static char *reversed_string(const char *string)
{
   /* your code here */
     //find length
    int size = 0;
    while(*(string+size) != 0) size++;

    char *newString = (char*)malloc(size);
    int bw = size - 1;
    for(int i = 0; i < size; ++i, --bw) {
        *(newString + i) = *(string + bw);
    }
    newString[size] = 0;
    return newString;
}

int main(int argc, char *argv[])
{
    char test_string[] = "SYNAPSE";
    char *reversed = reversed_string(test_string);
    reverse_string(test_string);
    printf("reverse:  %s\n", test_string);
    printf("reversed: %s\n", reversed);
    free(reversed);

/* a 32-bit memory-mapped read/write register at address 0x40000000 */
#define REGISTER  *( (int*)(0x40000000) )   //assume that WORD size of this architecture is int.


    // 4천만번째 주소가 만약 하드웨어 레지스터라면 어떻게 짜야 하는지 물어봤다.
    volatile int *myaddr = 0x40000000;
    *myaddr = 0xaa;
   
 
    REGISTER = 0xdeadbeef;  // 여길 실행하면 무슨일이 일어났는지 물어봤다.
    printf("REGISTER: %" PRIu32 "\n", REGISTER);
    return 0;
}