본문 바로가기

무언가 만들기 위한 지식/SPARC Assembler

SPARC Assembler File i/o (input/output) Trap Service_파일입출력


이번에 알아볼 것은 Trap Service에 관한 것이다.
이 트랩서비스는 System Call이다.

System Call :
입출력 제어 등 운영 체계(OS)의 기능을 요청하기 위해 응용 프로그램에서 사용되는 메커니즘. 시스템 호출에서는 특수 프로그램 명령어를 사용하여 컴퓨터가 감시자 모드나 보호 모드로 변경되면 OS가 하드웨어 장치나 기억 장치의 접근과 같은 동작을 수행한다

사용되는 명령어는 [ta]로 사용된다. ta를 호출하기 전에는 %g1에 미리 정의된 정수를 넣어줘야 한다.
%g1에 들어가게되는 종류는 다음과 같다.

%g1  Service Request 
 1 exit
 2 fork
 3 read
 4 write
 5 open 
 6 close
 7 無 
 8 create

%g1에는 7개의 제공되는 기능중에 사용할 기능의 번호를 넣어준후 ta 0를 호출하면 된다.
물론 각 서비스마다 사용되는 인자는 조금씩 다르지만 %o Register를 이용하여 전달하는 점과 결과가 %o0로 반환된다는 사실은 모두 동일하다. (trap Service에는 7번이 없다. - 정확한 이유는 모름, 설계상 그런 것 같음)

Trap과 Interrupt의 차이점 :
트랩은 스위치로 제어되던 정지나 분기의 고전적 개념에서 발전된 것으로, 흔히 다수의 내부 트리거나 트랩 기능이 컴퓨터 내에 존재하며 기대 또는 예측할 수 없는 사건들에 의해 발생된다. 트랩과 인터럽트의 차이점은, 트랩은 발생하는 시점이 프로그램의 일정한 지점이라는 점에서 동기적인 반면, 인터럽트는 주로 하드웨어적인 요소나 프로그램 외부 상황에 따라서 발생 시점이 일정하지 않다는 점에서 비동기적이다.

그럼 이제 이 Trap Service를 직접 활용하여 볼 것이다. 그중 exit와 read, write, open, close, create를 활용하여 파일 입출력 프로그램을 구현할 것이다.( 뭐 이래저래 fork빼고 다쓰는구나 -.,-)

트랩서비스에서 사용되는 인자는 UNIX상의 I/O와 같다.

위 와 같은 기능을 어셈블리어로 구현한다.
참고사항
int nRead=read(int fd, char* buf, int n);
int nWritten=write(int fd, char* buf, int n);
int fd=open(char* name, int flags, int perms);
int fd=create(char* name, int perms);
close(fd);
fd: file descriptor
buf : 파일 저장할 공간
n : 읽거나 기록될 문자수
name : 파일명
flag : 0(read), 1(write), 2(둘다)... 
perms : 파일 접근 권한


바로 코드를 보도록 하자.
해당 코드위치에는 [data] 라는 파일이 존재한다고 전제한다.


[data] file의 내용은 위와 같다. (내용은 살짝 뻥...)

[작성환경 : solaris sparc machine  작성툴 : Vi editor   컴파일러 : gcc]

소스 내용을 분석해보자.
7~16 Line : 파일 Open 및 에러처리 루틴이다. 보면 인자에 %o0는 파일명, %o1 에는 0(0이 곳 READ를 뜻함.) %o2에는 접근제한으로 0값을 넣었다. 여기서 중요한 것은 13번 줄 bcc명령어를 이용하여 오류처리를 한다는 것이다.
프로그램 종료는 %g1에 1값을 넣어줌으로서 exit를 호출하였다. 여기서 open 하는 data파일은 읽어들이기 위함이다.
18~25 Line : 이부분은 복사한 char들을 저장하기 위해 하나의 파일을 생성하는 것이다. 이때 접근 제한을 0660을 준다. 이는 8진수로 6하나는 이진수 3개로 표현되는데 6=110 에서 첫번째부터 read/write/excute를 뜻한다. 0666에서는 처음부터 Owner/Group/Others를 뜻한다. 여기서도 예외처리를 bcc로 하는 모습을 볼 수 있다.
40~49 Line : data로 부터 파일을 읽어온다. 자세히 보면 28 Line에서 Branch하기 전에 %o0에 파일 디스크립터를 넘겨준 모습을 볼 수 있다. 그후 저장될 buf(%fp를 이용하여 메모리 공간을 넘겨준다.), 그 다음으로 읽을 char형의 개수를 체크한다.
여기서 눈여겨 볼것은 buf를 넘겨준후 읽어야할 개수를 따로 지정해주는 이유이다. 처음 C언어 작성시에는 배열 크기만큼읽으면 되지 이걸 또 왜정해야하지? 라는 의문이 들었다. 생각해 보면 그 이유가 여기에 있다. %o0에는 메모리 시작 주소만 넘겨준다. 이 경우 메모리 시작만 알지 끝을 모른다. 특히 스택 메모리상에서 주소를 낮은 값을 보내주기 때문에 개수를 확실히 알아야 한다. 그 때문에 읽는 개수를 알려줘야하는 것이다.
분기 체크는 addcc로 한다, 이는 read와 write의 return값이 읽거나 쓴 개수이기 때문이다.
(0이면 더이상 읽을 것이 없고, 음수면 에러다. 즉 cc코드로 가능)
30~38 Line : ouput이라는 파일에 읽어온 데이터를 쓰는 부분이다. 다른 부분은 거의 동일하다.
51~57 Line : 파일을 close하고 main을 종료하는 부분이다.

※ 읽고 쓰는 부분은 한번에 100정도 잔뜩 읽거나 10을 10번 돌면서 읽을 수 있다. 10을 10번 읽으면 코드는 while을 통해 좀 복잡해지더라도 메모리를 확실히 절약할 수 있다.
(필자는 두개 테스트를 위해 main에서의 %sp를 -200으로 잡아놓았었다.- 현재 코드는 10개의 문자를 돌아가면서 10byte메모리를 이용하고 읽고 쓰는 모습이다.)

결과값은 다음과 같다.


첫번째는 data를 출력한 모습
두, 세번째는 컴파일후 실행한 모습
네번째는 output파일의 모습을 출력한 모습이다. 보기에는 똑같이 복사된것처럼 보인다.
다섯번째는 cmp명령어를 이용하여 두개의 파일을 비교해보았다.
(결과 아무런 표시 없는 걸 보면 완전 똑같은 파일이라는 것을 알 수 있다.)