POSIX 단말 인터페이스

POSIX 단말 인터페이스()는 POSIX 표준 및 단일 유닉스 규격에 정의된 대로, 프로그램을 위한 애플리케이션 프로그래밍 인터페이스(API)와 단말기 사용자를 위한 행동 기대치 세트 모두를 포함하는 일반화된 추상화이다. 이는 BSD 버전 4와 유닉스 제7판의 단말 인터페이스로부터 역사적으로 발전한 것이다.

 일반적인 기본 개념 
 하드웨어 
유닉스 시스템에서는 다양한 입출력 장치를 "단말기"로 간주한다. 여기에는 다음과 같이 포함된다.
 프린터, 전신타자기, 다이얼 업 접속을 통해 원격 단말기를 지원하는 모뎀, 그리고 직접 연결된 로컬 단말기와 같이 직렬 포트로 연결된 직렬 장치
 시스템 유닛에 직접 통합된 디스플레이 어댑터와 키보드 하드웨어. 이들은 함께 로컬 "콘솔"을 형성하며, 사용자나 프로그램에는 단일 CRT 단말기 또는 여러 개의 가상 단말기로 표시될 수 있다.
 Xterm, 콘솔, 그놈 터미널, 터미널 프로그램과 같은 소프트웨어 단말 에뮬레이터, 그리고 rlogin 데몬이나 SSH 데몬과 같은 네트워크 서버. 이들은 의사 단말기를 사용한다.

 단말 지능 및 기능 
 지능: 단말기는 지능형이 아닌 멍청한 장치임 
메인프레임이나 미니컴퓨터와 달리, 원래의 유닉스 시스템은 오로지 멍청한 단말기(dumb terminals)만을 위해 개발되었으며 이는 오늘날에도 마찬가지이다. 단말기는 문자의 흐름을 주고받는 문자 중심 장치이다. 문자 스트림이 제어 문자, 이스케이프 코드, 특수 문자를 포함하여 구조화되어 있기는 하지만, 입출력 프로토콜은 스마트 단말기 또는 지능형 단말기의 입출력 프로토콜처럼 구조화되어 있지 않다. 필드 형식 사양도 없으며, 전체 화면(입력 양식)의 입력 데이터를 블록 단위로 전송하는 기능도 없다.

반면, 폐쇄형 아키텍처의 메인프레임과 미니컴퓨터는 흔히 블록 지향 단말기를 사용한다.

 기능: terminfo, termcap, curses 등 

단말기의 "기능(capabilities)"은 순수한 전신타자기에서 제공하는 것 이상의 다양한 단말기 기능들로 구성되며, 프로그램에서 이를 활용할 수 있다. 이들은 (주로) 단말기로 보내거나 단말기로부터 받는 이스케이프 코드로 구성된다. 단말기로 보내는 이스케이프 코드는 CRT 단말기(또는 소프트웨어 단말 에뮬레이터)가 할 수 있지만 전신타자기는 할 수 없는 다양한 기능들을 수행한다. 예를 들어 단말기 커서를 화면의 특정 위치로 이동시키기, 화면의 전체 또는 일부를 지우거나 스크롤하기, 연결된 프린터 장치 켜고 끄기, 프로그래밍 가능한 기능 키, 디스플레이 색상 및 속성(예: 반전 비디오) 변경, 디스플레이 제목 문자열 설정 등이 있다. 단말기로부터 받는 이스케이프 코드는 기능 키, 화살표 키 및 기타 특수 키 입력(Home 키, End 키, Help 키, PgUp 키, PgDn 키, Insert 키, Delete 키 등)을 나타낸다.

이러한 기능들은 시스템 관리자가 설정하는 데이터베이스에 인코딩되며, 프로그램은 (오래된 Termcap 라이브러리를 대체하는) Terminfo 라이브러리를 통해 이 데이터베이스에 액세스한다. 이를 바탕으로 Curses 및 Ncurses 라이브러리가 구축된다. 응용 프로그램은 이러한 단말기 기능을 사용하여 창, 대화 상자, 버튼, 레이블, 입력 필드, 메뉴 등을 갖춘 텍스트 사용자 인터페이스를 제공한다.

 환경 변수 제어: TERM 등 
(단말기를 인식하는) 프로그램의 입출력이 사용하는 단말기의 특정 기능 세트는 프로그램이나 라이브러리에 하드코딩되는 대신 데이터베이스에서 가져오며, TERM 환경 변수(그리고 선택적으로 termcap 및 terminfo 라이브러리를 위한 TERMCAP 및 TERMINFO 환경 변수)에 의해 제어된다. 이 변수는 해당 단말기를 입출력에 사용하는 프로그램을 실행하는 단말기 모니터 프로그램에 의해 설정되거나, 때로는 명시적으로 설정된다. 예를 들어:
 getty 프로그램(또는 그에 상응하는 프로그램)은 시스템 데이터베이스(시리얼 포트에 어떤 로컬 단말기가 연결되어 있는지, 로컬 가상 단말기나 시스템 콘솔이 어떤 단말기 유형을 제공하는지 정의하는 inittab 또는 ttymon, launchd 설정 파일)에 따라 TERM 환경 변수를 설정한다.
 원격 단말기에서 접속한 다이얼 업 사용자가 시스템이 해당 다이얼 업 라인에서 일반적으로 예상하는 유형과 다른 단말기를 사용하는 경우, 로그인 직후 TERM 환경 변수를 올바른 유형으로 직접 설정한다. (더 일반적으로는, 시스템 관리자가 다이얼 업 사용자가 가장 자주 사용할 것으로 판단하여 설정한 getty 프로그램의 단말기 유형이 사용자의 단말기와 일치하면 사용자가 직접 변경할 필요가 없다.)
 SSH 서버 데몬(또는 rlogin 데몬과 같은 유사 프로그램)은 TERM 환경 변수를 SSH 클라이언트와 동일한 단말기 유형으로 설정한다.
 의사 단말기를 사용하는 소프트웨어 단말 에뮬레이터는 자신이 에뮬레이션하는 단말기 유형을 지정하기 위해 TERM 환경 변수를 설정한다. 에뮬레이션된 단말기는 실제 단말기 하드웨어와 정확히 일치하지 않는 경우가 많으므로, 단말 에뮬레이터는 전용 유형 이름을 사용한다. 예를 들어 xterm 프로그램은 기본적으로 xterm을 단말기 유형으로 설정한다. GNU Screen 프로그램은 screen을 단말기 유형으로 설정한다.

 작업 제어 

단말기는 작업 제어 기능을 제공한다. 대화형으로 단말기 사용자는 현재 실행 중인 작업을 중단(suspend)시키는 제어 문자를 보낼 수 있으며, 이를 통해 작업을 실행한 대화형 작업 제어 셸로 돌아가거나, 작업을 "백그라운드"로 보내거나 다른 백그라운드 작업을 포그라운드로 전환(필요시 중단 해제)하는 명령을 실행할 수 있다.

 라인 디시플린 

엄밀히 말해, 유닉스에서 단말 장치는 입출력 명령을 통해 장치 하드웨어를 물리적으로 제어하고 문자 입출력을 위한 장치 인터럽트 요청을 처리하는 기본 tty 장치 드라이버와 라인 디시플린(line discipline)으로 구성된다. 라인 디시플린은 실제 장치 하드웨어와 독립적이며, 동일한 라인 디시플린을 여러 제어 단말기를 담당하는 단말기 집중 장치나 의사 단말기에 사용할 수 있다. 사실 라인 디시플린(BSD, AIX 등의 시스템에서는 라인 디시플린들)은 모든 단말 장치에서 동일하다. 로컬 에코, 라인 편집, 입력 모드 처리, 출력 모드 처리, 문자 매핑을 담당하는 것이 바로 라인 디시플린이다. 이 모든 것들은 실제 하드웨어와 무관하며, tty 장치 드라이버가 제공하는 단순한 추상화(문자 전송, 문자 수신, 다양한 하드웨어 상태 설정)를 다룬다.

유닉스 제7판, BSD 시스템 및 MacOS, 리눅스를 포함한 그 파생 시스템에서 각 단말 장치는 여러 라인 디시플린 중에서 전환될 수 있다. AT&T STREAMS 시스템에서 라인 디시플린은 STREAMS 입출력 스택에 푸시(push)하거나 팝(pop)할 수 있는 STREAMS 모듈이다.

 역사 
POSIX 단말 인터페이스는 다양한 유닉스 시스템의 단말 인터페이스에서 유래되었다.

 초기 유닉스: 유닉스 제7판 

유닉스 32V와 유닉스 제7판에서 제공하고 BSD 버전 4에서도 이전 단말 드라이버로 제공했던 단말 인터페이스는 단순했으며, 주로 전신타자기를 단말기로 사용하는 데 맞춰져 있었다. 입력은 한 번에 한 줄씩 입력되었으며, 단말기 자체가 아닌 운영 체제의 단말기 드라이버가 간단한 라인 편집 기능을 제공했다. 편집이 이루어지는 버퍼는 커널에 의해 유지되었다. 단말 입력 읽기를 수행하는 응용 프로그램은 사용자가 라인 편집을 마치기 위해 단말기에서 키를 눌렀을 때만 버퍼의 내용을 전달받았다. 단말기에서 시스템으로 전송된 키는 현재 편집 버퍼의 전체 내용을 지웠고("kill"), 보통 '' 기호와 함께 출력 위치를 새 빈 줄로 옮기는 개행 시퀀스가 표시되었다. 단말기에서 시스템으로 전송된 키는 편집 버퍼 끝의 마지막 문자를 지웠고, 보통 '' 기호로 표시되었다. 사용자는 이것이 이전 문자의 "삭제(rubout)"를 의미함을 인식해야 했다(전신타자기는 종이에 인쇄된 문자를 물리적으로 지울 수 없었기 때문이다).

프로그래밍 관점에서 단말 장치는 송수신 보 레이트(baud rate), (앞서 설명한 라인 편집을 수행하는) "erase" 및 "kill" 문자, (단말기가 제어 단말기인 모든 프로세스에 신호를 생성하는) "interrupt" 및 "quit" 문자, (모뎀 흐름 제어에 사용되는) "start" 및 "stop" 문자, "파일 끝(end of file)" 문자(캐리지 리턴처럼 동작하지만 read() 시스템 호출에 의해 버퍼에서 폐기되어 잠재적으로 길이가 0인 결과를 반환하게 함), 그리고 커널의 단말 드라이버가 로컬 에코를 에뮬레이션할지 여부, 모뎀 흐름 제어 활성화 여부, 다양한 출력 지연 시간, 캐리지 리턴 문자 매핑, 세 가지 입력 모드를 결정하는 다양한 기본 모드 플래그를 가졌다.

세 가지 입력 모드는 다음과 같다.
라인 모드(또는 "cooked" 모드)

라인 모드에서 라인 디시플린은 모든 라인 편집 기능을 수행하고 "interrupt" 및 "quit" 제어 문자를 인식하여 프로세스로 보내는 신호로 변환한다. 단말기로부터 읽는 응용 프로그램은 사용자가 return을 눌러 라인 편집을 완료한 후에야 전체 라인을 전달받는다.
cbreak 모드

cbreak 모드는 두 가지 "한 번에 한 문자씩(character-at-a-time)" 모드 중 하나이다. (스티븐 번은 이를 "반쯤 조리된(half-cooked)", 즉 "레어(rare)" 모드라고 농담조로 불렀다. ) 라인 디시플린은 라인 편집을 수행하지 않으며, 라인 편집 기능을 위한 제어 시퀀스는 일반 문자 입력으로 취급된다. 단말기로부터 읽는 응용 프로그램은 문자가 입력 큐에 들어오는 즉시 전달받는다. 그러나 "interrupt" 및 "quit" 제어 문자와 모뎀 흐름 제어 문자는 여전히 특별하게 처리되어 입력 스트림에서 제거된다.
raw 모드
raw 모드는 다른 하나의 "한 번에 한 문자씩" 모드이다. 라인 디시플린은 라인 편집을 수행하지 않으며, 라인 편집 기능과 다양한 특수 문자("interrupt", "quit", 흐름 제어)를 위한 제어 시퀀스 모두 일반 문자 입력으로 취급된다. 응용 프로그램은 문자를 즉시 전달받으며, 단말 장치에서 온 그대로 수정되지 않은 전체 문자 스트림을 받는다.

이러한 모든 모드와 제어 문자를 조회하고 수정하기 위한 프로그래밍 인터페이스는 ioctl() 시스템 호출이었다. (이는 유닉스 제6판의 stty() 및 gtty() 시스템 호출을 대체한 것이다.) "erase" 및 "kill" 문자는 기본값인 과 에서 변경할 수 있었지만, 수년 동안 단말기 드라이버의 사전 설정된 기본값으로 유지되었다. 로그인 프로세스의 일부로만 단말기 설정을 변경하고 사용자가 사용자 이름과 암호를 입력한 후에 실행되는 시스템 로그인 스크립트를 사용하는 많은 유닉스 시스템에서는, 로그인 및 암호 입력 시의 실수를 전신타자기 단말기에서 물려받은 역사적인 편집 키 문자를 사용하여 수정해야 했다.

 BSD: 작업 제어의 등장 

BSD 유닉스와 함께 작업 제어와 확장된 기능을 갖춘 새로운 단말기 드라이버가 도입되었다. 이러한 확장 기능에는 다음과 같은 추가적인 (역시 프로그래밍 방식으로 수정 가능한) 특수 문자들이 포함되었다:
 "suspend" 및 "delayed suspend" 문자(기본값은 및 — ASCII SUB 및 EM)는 단말기의 제어 프로세스 그룹에 속한 프로세스들에 새로운 SIGTSTP 신호를 생성하게 했다.
 "word erase", "literal next", "reprint" 문자(기본값은 , , — ASCII ETB, SYN, DC2)는 추가적인 라인 편집 기능을 수행했다. "word erase"는 라인 편집 버퍼 끝의 마지막 단어를 지웠다. "literal next"는 라인 편집 버퍼에 모든 특수 문자를 입력할 수 있게 했다(유닉스 제7판에서는 역슬래시 문자를 통해 다소 불편하게 제공되던 기능이었다). "reprint"는 라인 디시플린이 새 줄에 라인 편집 버퍼의 현재 내용을 다시 출력하게 했다(다른 백그라운드 프로세스가 생성한 출력물이 라인 편집 내용과 섞였을 때 유용했다).

이러한 모든 추가 모드와 제어 문자를 조회하고 수정하기 위한 프로그래밍 인터페이스는 여전히 ioctl() 시스템 호출이었는데, 개발자들은 이를 "다소 어수선한 인터페이스"라고 묘사했다. 원래의 유닉스 제7판 기능은 모두 유지되었고 새로운 기능이 추가적인 ioctl() 작업 코드를 통해 더해지면서, 인터페이스는 명백히 비대해졌고 일부 기능 중복이 발생했다.

 시스템 III 및 시스템 V 
시스템 III는 유닉스 제7판에서 플래그를 가져오고 설정하는 작업과 제어 문자를 가져오고 설정하는 작업을 분리했던 ioctl() 작업들을 하나로 통합한 새로운 프로그래밍 인터페이스를 도입했다. 이 인터페이스는 termio 구조체를 사용하여 플래그와 제어 문자를 모두 보유하며, 한 번의 작업으로 이를 가져오거나 설정할 수 있었다. 또한 유닉스 제7판 인터페이스의 일부 플래그를 여러 개의 개별 플래그로 분할하고 몇 가지 추가 기능을 더했으나, 작업 제어나 4BSD의 쿡드 모드 개선 사항은 지원하지 않았다. 예를 들어, 유닉스 제7판의 "cooked", "cbreak", "raw" 모드를 다른 추상화로 대체했다. 신호 발생 문자의 인식은 입력 모드와 독립적이며, 입력 모드는 정준(canonical) 및 비정준(non-canonical) 두 가지만 존재한다. (이를 통해 유닉스 제7판이나 BSD에는 없던 단말 입력 모드인 '신호 발생이 비활성화된 정준 모드'가 가능해졌다.)

시스템 V를 포함한 시스템 III의 후계자들은 동일한 인터페이스를 사용했다.

 POSIX: 통합 및 추상화 
POSIX 표준이 일반 단말 인터페이스의 정의를 통해 해결하고자 했던 주요 문제 중 하나는 난립하는 프로그래밍 인터페이스였다. 표준이 만들어질 무렵에는 단말기의 동작이 시스템마다 상당히 균일해졌고 대부분의 유닉스가 라인 디시플린 개념과 BSD의 작업 제어 기능을 채택했음에도 불구하고, ioctl() 시스템 호출을 통한 단말기 프로그래밍 인터페이스는 엉망이었다. 서로 다른 유닉스들이 서로 다른 (심볼릭) 이름과 플래그를 가진 다양한 ioctl() 작업을 제공했다. 이식 가능한 소스 코드를 작성하려면 비록 관념적으로는 모두 유닉스임에도 불구하고 소프트웨어 플랫폼 간의 차이를 수용하기 위해 상당한 양의 조건부 컴파일을 포함해야 했다.

POSIX 표준은 ioctl() 시스템을 완전히 대체하여, 표준화된 이름과 매개변수를 가진 라이브러리 함수 세트(물론 내부적으로는 플랫폼별 ioctl() 작업을 통해 구현될 수 있음)를 도입했다. 시스템 V 유닉스의 termio 데이터 구조는 POSIX termios 데이터 구조의 템플릿으로 사용되었으며, 그 필드들은 거의 변경되지 않았으나 이제 필드 지정을 위해 별칭 데이터 유형을 사용했다. 이를 통해 구현자들은 C 및 C++ 프로그래밍 언어의 unsigned short나 char 데이터 유형(일부 프로세서 아키텍처에서는 크기가 불편할 수 있음)을 명시적으로 요구하는 대신, 여러 프로세서 아키텍처에 쉽게 이식할 수 있게 되었다.

또한 POSIX는 termios 구조체에 시스템 III 및 시스템 V가 지원하는 제어 문자 외에 suspend 및 delayed-suspend 문자를 포함함으로써 작업 제어 지원을 도입했다. BSD의 쿡드 모드 확장 기능은 추가하지 않았으나, 썬OS 4.x, 시스템 V 릴리스 4, 솔라리스, HP-UX, AIX, 최신 BSD들, MacOS, 리눅스는 이들을 termios의 확장으로 구현했다.

 표준이 정의하는 내용 
 제어 단말기 및 프로세스 그룹 

시스템의 각 프로세스는 단일 제어 단말기를 가지거나 제어 단말기가 전혀 없다. 프로세스는 부모로부터 제어 단말기를 상속받으며, 프로세스에 대한 유일한 작업은 제어 단말기가 없는 프로세스가 제어 단말기를 획득하거나, 제어 단말기가 있는 프로세스가 이를 포기하는 것이다.

제어 단말기를 획득하는 이식 가능한 방법은 정의되어 있지 않으며, 그 방법은 구현에 따라 다르다. 표준은 open() 시스템 호출을 위한 O_NOCTTY 플래그를 정의한다. 이는 제어 단말기를 획득하는 통상적인 방식(제어 단말기가 없는 프로세스가 아직 다른 프로세스의 제어 단말기가 아닌 단말 장치 파일을 O_NOCTTY 플래그 없이 open()하는 것)을 방지하는 방법이지만, 그 통상적인 의미 자체는 선택 사항으로 남겨두고 있다.

각 프로세스는 또한 프로세스 그룹의 구성원이다. 각 단말 장치는 포그라운드 프로세스 그룹이라 불리는 프로세스 그룹을 기록한다. 프로세스 그룹은 단말기 액세스와 신호 전달을 제어한다. 단말기에서 생성된 신호는 단말기의 포그라운드 프로세스 그룹의 구성원인 모든 프로세스에 전송된다. 단말기의 포그라운드 프로세스 그룹의 구성원이 아닌 프로세스가 단말기에서 수행하는 read() 및 write() 입출력 작업은 호출 프로세스에 신호(각각 SIGTTIN 및 SIGTTOU)를 전송하게 하거나 선택적으로 그렇게 할 수 있다. 다양한 단말기 모드 변경 라이브러리 함수는 write()와 동일한 동작을 하지만, write() 자체에 대해 해당 기능이 꺼져 있더라도 항상 신호를 생성한다.

 termios 데이터 구조 
모든 단말기 라이브러리 호출에서 사용되는 데이터 구조는 termios 구조체이며, C 및 C++ 프로그래밍 언어 정의는 다음과 같다:struct termios {
 tcflag_t c_iflag ; // 입력 모드
 tcflag_t c_oflag ; // 출력 모드
 tcflag_t c_cflag ; // 제어 모드
 tcflag_t c_lflag ; // 로컬 모드
 cc_t c_cc[NCCS] ; // 제어 문자
} ;

termios 구조체 내 필드 순서는 정의되어 있지 않으며, 구현체는 비표준 필드를 추가할 수 있다. 실제로 구현체들은 입출력 보 레이트를 기록하기 위해 비표준 필드를 추가해야 한다. 이들은 표준화된 구조체 필드처럼 필드 값을 직접 조작하는 대신, 구현에 정의된 형태대로 구조체에 기록되고 접근자(accessor) 함수를 통해 접근된다.

데이터 유형 별칭인 tcflag_t 및 cc_t, 심볼릭 상수 NCCS, 그리고 다양한 모드 플래그, 제어 문자 이름, 보 레이트를 위한 심볼릭 상수는 모두 표준 헤더인 termios.h에 정의되어 있다. (이것은 시스템 III 및 시스템 V의 유사한 이름의 헤더인 termio.h와 혼동해서는 안 된다. termio.h는 유사한 termio 구조체와 많은 비슷한 이름의 심볼릭 상수를 정의하지만, 이 인터페이스는 시스템 III 및 시스템 V에 특화되어 있으며 이를 사용하는 코드가 다른 시스템에 반드시 이식된다는 보장은 없다.)

구조체의 필드는 다음과 같다(상세 내용은 본문 문서 참조).
c_iflag 입력 패리티, 입력 개행 변환, 모뎀 흐름 제어, 8비트 클린, (시리얼 포트의) "break" 조건에 대한 응답 등을 제어하는 입력 모드 플래그
c_oflag 구현에 정의된 출력 후처리, 출력 개행 변환, 다양한 제어 문자 전송 후의 출력 지연 등을 제어하는 출력 모드 플래그
c_cflag 라인 디시플린보다는 실제 단말 장치를 제어하기 위한 단말 하드웨어 제어 플래그: 문자당 비트 수, 패리티 유형, hangup 제어, 시리얼 라인 흐름 제어
c_lflag 단말 하드웨어보다는 라인 디시플린을 제어하기 위한 로컬 제어 플래그: 정준 모드, 에코 모드, 신호 발생 문자 인식 및 처리, write() 시스템 호출에 의한 SIGTTOU 신호 생성 활성화

라이브러리 함수는 다음과 같다(상세 내용은 본문 문서 참조).
tcgetattr() 단말 장치의 현재 속성 설정을 termios 구조체로 조회한다.
tcsetattr() termios 구조체로부터 단말 장치의 현재 속성 설정을 지정한다. 선택적으로 대기 중인 출력이 배출(drain)되기를 기다리거나 대기 중인 입력을 비울(flush) 수 있다.
cfgetispeed() termios 구조체의 구현 정의 필드로부터 입력 보 레이트를 조회한다.
cfgetospeed() termios 구조체의 구현 정의 필드로부터 출력 보 레이트를 조회한다.
cfsetispeed() termios 구조체의 구현 정의 필드에 입력 보 레이트를 설정한다.
cfsetospeed() termios 구조체의 구현 정의 필드에 출력 보 레이트를 설정한다.
tcsendbreak() 직렬 장치 단말기에 모뎀 "break" 신호를 보낸다.
tcdrain() 대기 중인 출력이 배출될 때까지 기다린다.
tcflush() 대기 중인 입력을 버린다.
tcflow() 흐름 제어를 변경한다.
tcgetpgrp() 단말기의 포그라운드 프로세스 그룹을 조회한다.
tcsetpgrp() 단말기의 포그라운드 프로세스 그룹을 설정한다.

 특수 문자 

termios 데이터 구조의 c_cc[] 배열 멤버는 (프로그래밍 방식으로 수정 가능한) 모든 특수 문자를 지정한다. 배열의 인덱스는 오른쪽 표와 같이 각 특수 문자 유형에 대한 심볼릭 상수이다. (배열의 다른 두 항목은 비정준 모드 입력 처리와 관련이 있으며 아래에서 설명한다.)

프로그래밍 방식으로 수정할 수 없는 특수 문자로는 라인 피드(ASCII LF)와 캐리지 리턴(ASCII CR)이 있다.

 입력 처리 
입력 처리는 단말 장치에서의 read() 시스템 호출 동작과 라인 디시플린의 라인 편집 및 신호 발생 특성을 결정한다. 유닉스 제7판 및 BSD 버전 4의 경우와 달리, 그리고 시스템 III 및 시스템 V의 경우처럼 라인 편집은 정준(canonical) 모드와 비정준(non-canonical) 모드 중 하나로만 작동한다. 둘 사이의 기본적인 차이점은 read() 시스템 호출의 블로킹/논블로킹 요구 사항(open() 또는 fcntl()을 통해 파일 서술자에 지정된 O_NONBLOCK 플래그)의 관점에서 데이터가 "읽기 가능해지는(available for reading)" 시점이다.

 정준 모드 처리 
정준 모드에서는 데이터가 라인 편집 버퍼에 축적되며, 사용자가 (단말기에서) 라인 구분 문자를 보내어 라인 편집이 종료될 때까지 "읽기 가능" 상태가 되지 않는다. 라인 구분 문자는 특수 문자이며, 파일 끝(EOF), 행 끝(EOL), 라인 피드(ASCII LF)가 여기에 해당한다. 앞의 두 문자는 프로그래밍 방식으로 설정 가능하지만, 마지막 문자는 고정되어 있다. 마지막 두 문자는 라인 편집 버퍼에 포함되지만, 첫 번째 문자는 포함되지 않는다.

엄밀히 말하면, 하나 이상의 행이 라인 구분 문자로 구분되어 라인 편집 버퍼에 축적되며(read()가 이를 읽을 때 폐기될 수도 있고 아닐 수도 있음), 라인 편집은 버퍼의 마지막 라인 구분 문자(있을 경우) 이후의 부분에 대해 작동한다. 따라서 예를 들어 "erase" 문자(어떤 문자로 프로그래밍되었든)는 이전 라인 구분 문자까지(포함하지 않음)의 라인 버퍼 마지막 문자만 지운다.

 비정준 모드 처리 
비정준 모드에서는 데이터가 버퍼(구현에 따라 라인 편집 버퍼일 수도 있고, 별도의 "처리된 입력" 및 "원시 입력" 큐를 가질 수도 있음)에 축적되며, termios 데이터 구조의 두 가지 입력 제어 매개변수인 c_cc[MIN] 및 c_cc[TIME] 멤버의 값에 따라 "읽기 가능" 상태가 된다. 둘 다 부호 없는 수치이다(cc_t가 부호 없는 유형의 별칭이어야 하기 때문). 전자는 최소 문자 수를 지정하고, 후자는 10분의 1초 단위의 타임아웃을 지정한다. 네 가지 가능성이 있다.
c_cc[TIME]과 c_cc[MIN]이 모두 0인 경우
이 경우 버퍼의 데이터는 즉시 "읽기 가능"하며, read()는 버퍼에 있는 데이터가 무엇이든 즉시 반환한다(데이터가 없으면 0을 반환할 수 있음).
c_cc[TIME]이 0이 아니고 c_cc[MIN]이 0인 경우
이 경우 버퍼의 데이터는 지정된 타임아웃이 경과한 후 또는 단일 문자가 수신되면 "읽기 가능"해진다. 타이머는 read() 시스템 호출 시작 시 트리거된다. 즉, read()는 지정된 최대 시간 동안 대기하며 데이터가 0일 때 반환할 수도 있고, 수신되는 즉시 데이터를 반환한다.
c_cc[TIME]이 0이고 c_cc[MIN]이 0이 아닌 경우
이 경우 버퍼의 데이터는 지정된 수의 문자가 버퍼에 수신된 후에 "읽기 가능"해진다. 즉, read()는 최소 데이터 양(호출자가 시스템 호출에서 읽으려고 준비한 양보다 클 수 있음)을 기다리며, 데이터를 0개 반환하지 않고 무기한 대기할 수 있다.
c_cc[TIME]과 c_cc[MIN]이 모두 0이 아닌 경우
이 경우 버퍼의 데이터는 지정된 수의 문자가 버퍼에 수신되거나, 마지막 문자가 입력된 후 타임아웃이 만료되면 "읽기 가능"해진다. 첫 번째 문자에 대한 타임아웃은 없다. 즉, read()는 최소 데이터 양을 기다리며, 데이터를 0개 반환하지 않고 무기한 대기할 수 있지만, 버퍼에 읽을 문자가 최소 하나라도 있다면 지정된 타임아웃보다 오래 기다리지 않는다.

 출력 처리 
출력 처리는 시스템 III/시스템 V의 뿌리에서 크게 변하지 않았다. 출력 모드 제어 플래그는 다양한 옵션을 결정한다.
 유닉스의 개행 의미를 많은 단말기가 기대하는 ASCII 의미로 번역하기 위해, 각 라인 피드 문자 앞에 캐리지 리턴을 삽입할 수 있다.
 백스페이스, 가로 탭, 캐리지 리턴, 폼 피드, 라인 피드 등 (전신타자기 등에서) 실제 캐리지 이동을 일으켜 (컴퓨터 관점에서) 상당한 시간이 걸릴 수 있는 다양한 제어 코드를 수행할 수 있도록 단말기에 시간을 줄 수 있다.

 내용주 

 참고 자료 

 
 
 
 
 
 
 

 추가 읽기 
 
 

분류:단말기
분류:POSIX