개발자 노트

키보드로 입력한 값이 콘솔로 출력될 때까지 본문

이것저것

키보드로 입력한 값이 콘솔로 출력될 때까지

jurogrammer 2021. 7. 3. 11:13

상위 주제

exception handling 여정

상황 - 간단한 입출력 프로그램

  1. app에서 키보드의 입력을 받는다.
  2. app에서 입력받은 값을 console로 출력한다.
  3. 이 앱을 console에서 실행한다.

추가로 과정을 정밀히 분석하기 위해서 pid도 출력합니다.

코드

public class App {
    public static void main(String[] args) {
        printPid();

        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            System.out.println(scanner.next());
        }
    }

    private static void printPid() {
        long pid = ProcessHandle.current().pid();
        System.out.println("current pid: " + pid);
    }
}

실행

console에서 위 코드를 컴파일 한 뒤, iterm에서 실행합니다.

이때 fork()를 통해 java 프로그램의 child process가 생성되죠. 정말 iterm에서 child process를 생성했는지 확인해보겠습니다.

P_ID

프로세스 아이디는 52590이라고 찍히네요.

child process

이 화면은 활성상태 보기인데요. 상단의 [보기] → [모든 프로세스 (계층 표시)] 를 클릭하면 부모 자식 노드의 계층 관계로 볼 수 있습니다.

호호; iterm2에서 기본적으로 생성하는게 있네요.. zsh가 parent process, java가 child process 관계를 가지네요. child process로 잘 생성되었습니다.

프로그램을 실행시켰으니 다음으로 입출력에 대해 알아보겠습니다.

입출력

결국엔 프로세스가 키보드로 입력된 값을 출력하려면 우선 메모리에 키보드로 입력한 값이 있어야 하죠.

입력

  1. 키보드를 다닥다닥 쳐봅니다.
  2. 키보드에서 발생한 전기 신호가 키보드의 버퍼에 저장된다.
  3. 디바이스 드라이버에 의해 버퍼의 데이터가 app의 영역 내 memory에 적재된다.
  4. app에선 메모리에 적재된 데이터를 읽는다.

출력

  1. 적재된 데이터를 읽고 콘솔로 값을 출력한다.

과정을 이와 작성했는데요... 의문점이 한둘이 아닙니다... ㄷ ㄷ ㄷ ㄷ

의문점

  1. 입력 장치가 한 둘인가? 마우스도 있고 키보드도 있는데...? 반대로 출력장치도 한 둘 아닌데... 왜 하필 콘솔에 출력하지??
  2. 콘솔에 출력한다.라고 한다면 파일로 입력한다.라고 말할 수 있지 않나? 장치는 물리적인 장비를 의미하잖아. 입력 장치는 결국 단순히 데이터를 생성하는 수단에 불과하니까 말이다. 그러면 결국 중요한건 입력, 출력이 아닌가? 내가 입출력 장치, 입출력...? 과 혼용하여 사용하고 있구나. 키보드와 콘솔 말이다.
  3. 운영체제가 app 메모리의 어느 주소에 데이터를 저장 시켜줘야할 지 어떻게 정하지???
  4. 난 콘솔에 값을 입력했는데 어떻게 자식프로세스인 app이 내 입력 값을 받았지? 그리고 app에서 출력했는데 콘솔에 값이 출력되네?

고민에 대한 답

1번

답변하기 위한 키워드는 "표준 입출력"입니다.

1) 전에 소켓을 공부하면서 file descriptor의 값 0,1,2는 표준 입출력 및 에러로 정해져있다. 라는 말을 들었구요...

2) 전에 지인이 공부하다가 파이썬의 print가 표준 출력을 한다는데 표준 출력이 뭐야? 라고 물어봤습니다. 당시엔 전혀 고민해보지 않았던 단어라 당황했죠.

헐... 결국... 표준이란 말이 필요한 이유는 입출력 종류가 많으므로 "표준" 입력,출력 이라는 표현을 했던거네요... 자세한 내용은 아래 위키에 있죠.

https://en.wikipedia.org/wiki/Standard_streams

2번

위 위키 내용을 참조하보면 답변할 수 있습니다. 유닉스 이전엔 입출력 '장치'를 일일이 연결해주었는데요. 유닉스에서는 device란 개념을 없애고 data stream이란 개념으로 추상화시켰다고 합니다. (여기서 stream은 정렬된 순서를 가진 byte data를 의미 하고 end of file을 만나기 전까지 읽을 수 있는 형태이지요.)

그리고는 자동으로 input과 output은 terminal의 keyboard와 terminal display로 기본 설정했다고 하죠.

3번

소켓프로그래밍 생각해보니... read할 때 buffer를 전달하거든요...? 운영체제가 메모리의 어느 위치에 값을 불러올 지 결정하기 위해서 app에서 전달해줬던거네요.

역시... C언어를 공부해야 low level까지 알긴 하네요.

4번

이것은 위 위키에서 잘 나와있습니다. 간단히 말하자면 redirect나 pipeline방법으로 child process는 parent process의 표준 스트림을 상속받습니다.

따라서 parent process인 콘솔에 값을 입력하더라도 child process에게 입력 값이 전달됬던 것이고, child process의 출력 값이 parent process인 터미널 화면에 출력됬던 것이죠.

결론

따라서 결론을 내리자면 다음과 같습니다.

콘솔의 입출력

  1. 키보드를 치면 키보드의 버퍼에 값이 쌓인다.
  2. 디바이스 드라이버가 버퍼 값을 읽고 콘솔의 메모리 영역에 그 값을 적재한다.
  3. 콘솔 입장(app)에선 이것은 dataStream으로 추상화되어 있고, 이 데이터 스트림의 값을 읽는다.
  4. 콘솔이 이 값을 출력한다.
  5. 그 출력된 값을 특정 메모리 영역에 전달한다.(만약 출력장치가 memory mapped I/O를 쓴다면)
  6. 디바이스 드라이버에 의해 해당 메모리의 데이터가 모니터 버퍼로 전달된다.
  7. 모니터는 버퍼로부터 데이터를 읽어 화면을 출력한다.

콘솔에서 실행되는 app의 입출력까지의 과정

여기서 app은 입력 값을 그대로 출력하는 echo program이라 보겠습니다.

  1. 키보드를 치면 키보드의 버퍼에 값이 쌓인다.
  2. 디바이스 드라이버가 버퍼 값을 읽고 콘솔의 메모리 영역에 그 값을 적재한다.
  3. 콘솔 입장(app)에선 이것은 dataStream으로 추상화되어 있고, 이 데이터 스트림의 값을 읽는다.
  4. 콘솔이 이 값을 출력한다. 이때, redirection, pipeline 등 ipc방법을 통해 child process인 app의 input으로 전달된다.
  5. child process가 data stream에서 그 값을 읽는다.
  6. child process가 읽은 값을 data stream에 출력한다.
  7. 위 ipc를 통해 child process의 출력이 console의 입력으로 전달된다.
  8. console은 그 입력 값을 출력한다.
  9. ....이하 동일
반응형

'이것저것' 카테고리의 다른 글

Exception handling - java의 Exception처리(이론)  (0) 2021.08.08
exception handling 여정 - Redirection  (0) 2021.07.03
Exception handling 여정  (0) 2021.07.03
추상화 구조에 대해  (0) 2021.03.07
올해 공부하고 싶은 것  (0) 2021.01.12
Comments