how canonical(ICANON) mode work
“줄 단위로만 깨어난다”가 정확히 무슨 뜻?
여기서 깨어난다는 건 read(2)가 블록(block) 상태로 기다리다가 반환(return) 한다는 뜻이야. 터미널이 캐노니컬(ICANON) 모드일 때 커널의 TTY 라인데 Discipline(터미널 드라이버)이 이렇게 동작해:
- 사용자가 키를 치면 바이트들이 커널의 TTY 입력 큐에 쌓임.
- 줄 구분자(line delimiter) 가 들어오기 전까지는 그 줄을 “완성되지 않은 상태”로 보고, read()를 호출한 프로세스는 계속 대기.
- 줄 구분자 = 보통 NL ('\n', 0x0A), 또는 설정된 EOL/EOL2, 그리고 줄 시작 시의 VEOF(기본 ^D).
- 줄 구분자가 오면 그때 비로소 한 줄이 완성되고, 잠자던 read()가 깨어나서 반환.
- 중요: EOF(줄 시작 ^D)인 경우를 제외하고, 줄 구분자 문자 자체(예: \n)는 read()로 반환되는 버퍼에 포함된다. 즉, read() 버퍼 맨 끝이 보통 ...\n로 끝나.
- 그리고 read()는 한 번 호출에 “최대 한 줄”만 준다. read(fd, buf, 0x10000)처럼 크게 요청해도 그때 완성된 한 줄만 반환하고 끝. 그 줄이 요청 크기보다 길면, 요청한 바이트까지만 주고 나머지(같은 줄의 잔여)는 다음 read()에서 받게 된다.
man 페이지의 문구를 그대로 요약하면: “
입력은 줄 단위로 제공
read()는 최대 한 줄만 반환
EOF를 제외하면 줄 구분 문자는 read()로 반환된 버퍼에 포함
그럼 네 질문에 그대로 답하면
“엔터를 치면 뒤에 개행까지. 이게 커널 버퍼로 들어가고 read에서 기다리고 있다가 이 버퍼를 바로 읽어간다 이 소리야? 근데 읽어갈때 개행 까지 읽어간다 이말이야?”
- 맞아. 캐노니컬에서 엔터(= \n)를 치는 순간 그 줄이 완성되고, 잠자던 read()가 깨어나 그 줄을 반환한다.
- 그리고 EOF 예외가 아니라면, 그 \n은 read()가 넘겨주는 버퍼에 포함된다(예: "...ABC\n").
^D(VEOF)의 두 경우
- 줄 시작에서 바로 ^D: read()는 0바이트(EOF)를 반환. (버퍼에 아무 바이트도 안 옴)
- 무언가 입력한 뒤 ^D: 그 시점까지 쌓인 바이트들을 즉시 반환하고, ^D 자체는 데이터로 들어가지 않음. (줄바꿈 없어도 반환)
그리고 가용이라는 개념 정리 ( chatgpt에 있음 )