다음 이전 차례

2. 시작하기

2.1 디버깅

코드를 디버깅하는 가장 좋은 방법은 또 하나의 리눅스 박스를 셋업하고 두 리눅스 박스를 null-modem 케이블로 연결하는 것이다. Miniterm 프로그램을 이용하여 문자들을 전송해보라. Miniterm은 컴파일하기도 쉽고, 키보드에서 입력되는 문자들(특수문자 포함)을 시리얼 포트로 전송할 수 있다. 컴파일할 때 체크해야 할 것은

#define MODEMDEVICE "/dev/ttyS0"

문장이 제대로 설정되어 있는가 하는 것이다. COM1으로 맞추려면 /dev/ttyS0, COM2로 하려면 /dev/ttyS1으로 수정한다. 테스팅을 할 때 가장 중요한 것은 문자가 시리얼 포트로 출력될 때 데이터가 출력 데이터 처리(output processing)을 하지 않고 그대로(raw) 전송이 되는가를 확인하는 것이다. 테스트 과정은 다음과 같다. 두 대의 리눅스 박스에서 각각 miniterm 프로그램을 실행시키고 키보드를 쳐본다. 한 곳에서 타이핑한 문자가 다른 곳에서 그대로 나타나는 지 확인한다.

Null-modem 케이블의 TxD와 RxD가 서로 cross 연결이 되어야 한다. 잘 모르겠으면 Serial-HOWTO 문서의 7장을 본다.

위의 테스트는 한 대의 컴퓨터만 갖고도 가능하다. 사용할 수 있는 시리얼 포트가 두 개 있다면 케이블을 각각의 시리얼 포트에 연결하고 miniterm을 두 개 실행하여 테스트하면 된다.

2.2 포트 세팅

장치 파일인 /dev/ttyS*는 리눅스에서 터미널을 연결하기 위한 목적으로 사용된다. 이 사실은 시리얼 통신 프로그래밍을 하는데 반드시 기억해야 할 사항이다. 예를 들어, 시리얼 포트도 문자 에코를 하도록 설정되어 있다. 이 설정은 보통 데이터 전송시에 바꿔야 할 사항이다. (역자 주: 시리얼 장치 파일도 터미널 장치로 분류되기 때문에 초기 설정은 일반 터미널에서 사용되는 에코가 되도록 설정되어 있는 것이다.)

모든 파라미터들은 프로그램 코드에서 쉽게 설정할 수 있다. 파라미터들은 <asm/termbits.h>에 정의되어 있는 struct termios 구조체에 저장되어 있다.

       #define NCCS 19
       struct termios {
               tcflag_t c_iflag;               /* input mode flags */
               tcflag_t c_oflag;               /* output mode flags */
               tcflag_t c_cflag;               /* control mode flags */
               tcflag_t c_lflag;               /* local mode flags */
               cc_t c_line;                    /* line discipline */
               cc_t c_cc[NCCS];                /* control characters */
       };

이 파일은 모든 flag들을 정의하고 있다. c_iflag(입력 모드 flag)는 모든 입력 처리(input processing)를 정의한다. 입력 처리란 read() 함수에 의해 시리얼 포트로 들어온 데이터를 read에 의해 읽기 전에 데이터들을 c_iflag에 정의한 대로 처리하는 것을 의마한다. c_oflag(출력 모드 flag)는 출력 처리(output processing) 하는 방법을 정의한다. c_cflag(제어 모드 flag)는 baudrate, data bits, stop bits 등의 포트 세팅을 정의한다. c_lflag(local 모드 flag)는 echo를 할 것인지 등을 결정한다. 마지막으로 c_cc(제어 문자) 배열은 EOF(End of File), STOP 등의 제어 동작들을 어떤 문자로 정의할 것인가를 설정한다. 제어 문자의 디폴트 문자는 <asm/termios.h>에 정의되어 있다. 위 flag들에 관한 설명은 termios(3) man page에 나와있다. termios 구조체의 c_line 항목은 POSIX 호환 시스템에서 사용되지 않는다.

2.3 시리얼 장치의 입력 방법

이 섹션에서는 세 가지의 입력 방법을 기술하기로 한다. 응용 분야에 따라서 알맞은 방법을 사용해야 한다. 한 문자씩 읽는 루프를 돌려서 전체 문자열을 받는 방법은 가능하다면 피해야 한다. 내가 이런 방법으로 했을 때, 문자를 잃어버리는 경우가 생긴 반면, 전체 문자열을 한번에 읽을 때는 에러가 발생하지 않았다.

Canonical 입력 처리(Canonical Input Processing)

Canonical 입력 처리는 터미널의 기본 처리 방법이다. 이 방법은 한 줄 단위로 처리하는 다른 프로그램과 통신하는데에 사용할 수 있다. 한 줄은 디폴트로 NL(New Line, ASCII는 LF) 문자, EOF(End of File) 문자, 혹은 EOL(End of Line)에 의해 종료되는 문자열을 의미한다. CR(Carriage Return, DOS/Windows의 디폴트 EOL 문자임) 문자는 디폴트 세팅에서 한 줄의 종료 문자로 인식되지 않는다.

또한 Canonical 입력 처리 모드에서는 ERASE, DELETE WORD, REPRINT CHARACTERS 문자들을 처리할 수 있고, CR 문자를 NL 문자로 변환 처리를 할 수 있다.

Non-Canonical 입력 처리(Non-Canonical Input Processing)

Non-Canonical 입력 처리 모드에서는 한 번 읽을 때마다 정해진 크기의 문자만을 읽어낼 수 있다. 또한 타이머를 두어서 일정 시간까지 read()가 리턴하지 않는 경우 강제 리턴을 할 수 있다. 이 모드는 항상 정해진 크기의 문자들만을 읽어내거나 대량의 문자들을 전송하고자 할 때 사용한다.

비동기 입력

위에서 설명한 두 가지 모드는 동기 방식이나 비동기 방식으로 사용될 수 있다. 동기 방식은 read의 조건이 만족될 때까지 block되는 방식으로서 디폴트로 설정되어 있다. 비동기 방식에서는 read() 함수가 바로 리턴되며, 호출한 프로그램에게 signal을 보낸다. 이 signal은 signal handler(시그널 처리 함수)로 보내진다.

입력장치 멀티플렉싱

위에서 설명한 입력 모드에 해당하진 않지만 여러 개의 장치들을 다루고자 할 때 유용하다. 예를 들어 내 응용 프로그램에서 TCP/IP 소켓과 시리얼 통신에서 동시에 입력을 받아야 했다. 아래 3.4의 예제는 두 개의 서로 다른 장치로부터 동시에 입력을 기다리는 코드이다. 둘 중 한 개의 장치에서 입력이 들어오면 처리를 하고 또 다시 새로운 입력이 올 때까지 기다린다.

아래 3.4의 예제는 복잡해 보일 수 있지만, 리눅스가 multi-processing OS임을 알고 있기에 매우 중요하다. select() 시스템 호출 함수는 입력을 기다리는 동안 CPU에 부하를 주지 않는다. 반면 입력이 들어왔는지 루프를 돌면서 체크하는 polling 방식은 시스템에 부하를 주게 되어 다른 프로세스의 수행 속도를 저하시키게 된다.


다음 이전 차례