시리얼 통신을 이용하기 위해서는 제일 먼저 통신 포트에 대한 사용 권한을 얻어야 합니다. 권한 얻기란 별 다른 것이 아니고, 다른 프로그램이 사용하기 전에 먼저 포트를 열기를 하면 사용 권한을 얻게 됩니다. 말을 거창하게 한듯 합니다만 이 말씀은 포트를 한 번 이상 사용 권한을 얻을 수 없다는 말씀이며, 같은 프로그램이라도 한 번 이상 열기를 할 수 없다는 말씀이 되겠습니다.
유닉스에서는 장치를 파일처럼 사용
유닉스 계열의 운영 체제에서는 시스템에 설치된 모든 하드웨어 장치를 파일 개념을 처리할 수 있으며, 부팅할 때 시스템에 설치된 장치를 확인하고 /dev/ 폴더 밑에 하드웨어 장치 별로 "장치명"의 형식에 따라 파일 형식으로 구성하여 줍니다. 프로그램은 이 장치명을 이용하여 표준 입출력 방법으로 사용할 수 있습니다.
아래의 동영상은 시리얼 포트가 하드웨어 장치이지만 파일 처럼 처리할 수 있음을 보여주는 간단한 예를 소개하고 있습니다.
시리얼 통신을 이용하기 위해서는 제일 먼저 통신 포트에 대한 사용 권한을 얻어야 합니다. 권한 얻기란 별 다른 것이 아니고, 다른 프로그램이 사용하기 전에 먼저 포트를 열기를 하면 사용 권한을 얻게 됩니다. 말을 거창하게 한듯 합니다만 이 말씀은 포트를 한 번 이상 사용 권한을 얻을 수 없다는 말씀이며, 같은 프로그램이라도 한 번 이상 열기를 할 수 없다는 말씀이 되겠습니다.
몇가지 장치명을 정리해 보았습니다.
장치명 |
하드웨어 종류 |
/dev/fd0 | 플로피 드라이브, fd0, fd1, fd2, ..... |
/dev/hda | IDE 하드디스크, hda, hdb, hdc, ..... |
/dev/sda | SCSI 하드디스크, sda, sdb, sdc, .... |
/dev/cdrom | cdrom |
/dev/scd | cdrom 이 SCSI 일 경우 |
/dev/dvd | DVD 롬 |
/dev/ttyS0 | 시리얼 통신 포트, ttyS0, ttyS1, ttyS2, .... |
P.C.에는 보통 시리얼 통신 포트가 com1, com2, com3, com4, 4 개가 있습니다만 각 포트별로 I/O 주소는 달라도, com1과 com3, com2와 com4가 같은 IRQ를 사용하고 있기 때문에 DOS에서나 Windows3.1 까지는 com1과 com3 또는 com2와 com4 를 같이 열어 사용할 수 없습니다. 그러나 Windows95이상의 O.S.에서나 리눅스에서는 인터럽트 공유 기술을 이용하여 이와 같은 문제가 없이 모든 포트를 open하여 사용할 수 있습니다.
포트 |
I/O 주소 |
IRQ |
com1 | 3f8 | 4 |
com2 | 2f8 | 3 |
com3 | 3e8 | 4 |
com4 | 2e8 | 3 |
시리얼 포트의 장치명
디렉토리 /dev 로 이동해서 시리얼 포트의 목록을 보면 아래와 같은 내용이 출력됩니다. 도대체 뭔소리인지 하나씩 알아 보겠습니다.
(1) | (2) | (3) | (4) | (5) | (6) | (7) | (8) | (9) | (10) |
crwxrwxrwx | 1 | root | tty | 4 | 64 | Jan | 1 | 2006 | ttyS00 |
crwxrwxrwx | 1 | root | tty | 4 | 65 | Jan | 1 | 2006 | ttyS01 |
crwxrwxrwx | 1 | root | tty | 4 | 66 | Jan | 1 | 2006 | ttyS02 |
crwxrwxrwx | 1 | root | tty | 4 | 67 | Jan | 1 | 2006 | ttyS03 |
(1) 접근 권한을 보면 crwxrwxrwx 로 c로 시작하는 것은 장치가 "문자 장치"임을 알려 줍니다. c 가 아닌 b로 시작한다면 "블록 장치"를 말하는데, 예로 하드디스크와 같이 블럭 단위로 읽거나 쓰기를 하는 장치가 되겠습니다.
(5) 의 4는 메이저 장치 번호, (6)의 64, 65, 66 등은 마이너 장치 번호입니다. 우리가 작성하는 프로그램은 하드웨어 장치를 직접 제어하는 것이 아니라 커널을 통해 제어하게 됩니다. 하드웨어를 파일 개념으로 처리할 수 있는 것도 중간에 커널이 가상 파일을 만들어서 제공하기 때문에 가능 한 것입니다.
프로그램에서 하드웨어 장치에 대해 어떤 작업을 커널에게 요청하면, 커널은 메이저 번호를 가지고 어떤 디바이스 드라이버 사용할 지를 결정하게 됩니다. 디바이스 드라이버는 커널로부터 받은 정보 중 마이너 장치 번호를 가지고 자기에게 할당 된 장치 중 어떤 장치를 제어할 지를 결정하게 됩니다.
위의 장치 목록을 보시면 메이저 번호가 모두 4 로 똑 같습니다. 대신에 마이너 번호만 다르죠. 커널은 메이저 번호로 따라 디바이 드라이버를 선택하고 다음 처리를 넘기면 디바이스 드라이버는 마이너 번호를 가지고 어느 장치를 사용할 지를 결정한다는 얘기가 되겠습니다.
이렇게 하드웨어 장치 제어 흐름을 본다면 ttyS0, ttyS1 과 같은 이름은 별로 중요하지 않죠. 중요한 것은 메이저 장치 번호와 마이너 장치 번호가 되겠습니다.
삭제된 작치명 복구
ttyS0로 또한 파일 개념이기 때문에 접근 권한을 변경할 수 있으며, 삭제할 수 도 있습니다. 만일 삭제되었다면 아래의 명령으로 다시 생성할 수 있습니다.
]# mknod -m 666 /dev/ttyS0 c 4 64
참고로 다른 예를 하나 더 들면 /dev/null 입니다. /dev/null 장치는 가상 장치로 하수구와 같습니다. 필요 없는 것을 이곳에 쓰기를 하면 모두 실행 성공이지만 이것으로 끝입니다. 디버깅을 할 때나 또는 어떤 명령 실행 후 결과가 필요 없을 경우 출력 파이프를 /dev/null로 설정하면 아무 출력이 이루어 지질 않게 되죠.
그러나 null이 뭐야 하고 지워버리는 경우가 있는데, /dev/null 도 아래와 같은 방법으로 생성할 수 있습니다.
]# mknod -m 666 /dev/null c 1 3
통신 포트 열기와 닫기
장치명에 대해서 파일을 사용하듯 open()과 close()함수를 사용하며 되겠습니다.
int main( void)
{
int fd; <- 통신 포트 사용에 대한 파일 디스크립터
fd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK ); <- 통신 포트 열기, 사용 시작close( fd); <- 통신 포트 닫기, 사용 완료
return 0;
}
시리얼 포트를 파일 처럼 처리하기 위하여 파일 디스크립터를 open() 함수를 통해 구하고 있습니다.
- "/dev/ttyS0" 는 com1 에 해당됩니다.
- O_RDWR 은 파일 디스크립터 fd를 읽기와 쓰기가 가능하도록 하기 위한 옵션입니다.
- O_NOCTTY 는 경로명이 터미널 장치일 경우에 사용합니다.
- O_NONBLOCK 는 경로명의 장치가 순차적인 입축력, 죽, FIFO 이거나 또는 블록단위가 아닌 문자단위 입출력 장치일 경우 사용합니다.
어떻게 포트 열기에 대한 프로그램 예제가 이해 되시나요? 그럼 다음 시간에는 통신 속도에 대해 알아 보겠습니다.