[인사이드 안드로이드]
Chapter7 - 안드로이드 바인더 IPC
리눅스 메모리 공간과 바인더 드라이버
바인더를 이해하려면 먼저 안드로이드의 기반 커널인 리눅스 커널의 메모리 공간을 이해할 필요가 있다. 총 4GB에 달하는 가상 주소 공간은 3GB의 사용자 공간과 1GB의 커널 공간으로 나뉜다. 사용자 코드와 관련 라이브러리는 사용자 공간의 코드 영역, 데이터 영역, 스택 영역에서 동작하고, 커널 공간에서 동작해야 할 코드는 커널 공간의 각 영역에서 동작한다. 그리고 프로세스는 다음과 같이 각자 독립된 주소 공간을 가지고 별개로 동작한다.
자신의 독립된 공간을 가진 프로세스가 다른 프로세스에게 데이터를 전달할 때는 프로세스간 공유가 가능한 커널 공간을 이용한다. 프로세스는 독립적인 사용자 공간을 가지지만 커널 공간은 공유할 수 있다. 커널 공간을 이용해 두 프로세스 사이의 메시지를 주고 받는 IPC를 수행한다.
기존 리눅스에서도 IPC 도구를 제공하지만, 안드로이드에서는 단순히 메시지를 전달하는 IPC 개념이 아니라 상대방 프로세스에 존재하는 함수까지 호출할 수 있는 바인더라는 IPC 도구를 채택해 프로세스 간의 RPC를 지원한다.
바인더는 프로세스들이 사용자 공간을 공유하지 않는다는 제약 조건상에서 프로세스간 통신을 제공하기 위해 다음처럼 커널 공간에서 동작하는 Binder(IPC) Driver라는 추상화된 드라이버를 이용한다.
안드로이드에서 바인더 드라이버를 추가해서 프로세스 간 통신을 수행하는 이유는 다음과 같다. 바인더는 사용자 공간에서 접근할 수 없는 공간인 커널 공간을 이용해 데이터의 신뢰성을 확보할 수 있으며 IPC 간의 보안 문제도 동시에 해결할 수 있다.
안드로이드 바인더 모델
IPC 데이터는 함수 호출과 관련된 내용, 즉 사용하고자 하는 서비스에 해당하는 번호와 호출할 함수명, 바인더 프로토콜로 구성된다.
서비스 번호는 안드로이드에서 동작중인 여러 서비스를 구분하기 위한 것.
함수명은 서비스 서버에서 동작하는 여러 서비스 가운데 서비스 클라이언트가 호출할 함수를 찾기 위한 것.
바인더 프로토콜은 바인더 드라이버와 바인더를 이용하는 프로세스 간의 IPC 데이터를 처리하는 규약.
핸들은 서비스를 구별하는 번호
RPC 코드와 RPC 데이터는 서비스에서 호출할 함수와 함수의 인자
바인터 프로토콜은 IPC 데이터의 처리 방법
바인더 IPC 데이터의 전달
바인더 드라이버는 문자 디바이스 드라이버처럼 open()이나 ioctl()과 같은 시스템 콜을 통해 접근 가능하다. 다음 그림은 시스템 콜과 바인더 드라이버에 포함된 파일 연산 함수와의 연결 관계를 보여준다.
① 바인더를 통해 RPC를 시도하는 응용 프로그램은 open() 시스템 콜을 통해 바인더 드라이버의 파일 디스크립터를 얻는다.
② mmap() 시스템콜을 통해 커널 내에서 IPC 데이터를 수신하기 위한 공유 공간을 확보한다.
③ ioctl() 함수의 인자로 바인더 드라이버에 IPC 데이터를 전달한다. ioctl(파일 디스크립터, ioctl 명령어, 데이터 타입)
바인더 IPC 데이터의 흐름
바인더 IPC 데이터가 서비스 클라이언트에서 서비스 서버로 전달되기 까지 어떠한 과정을 거치는지 살펴보자.
그림은 서비스 클라이언트에서 서비스 서버에 존재하는 서비스의 함수를 호출하는 과정을 보여준다.
- 서비스 계층 : 특정 기능을 수행하는 서비스의 함수가 존재하는 계층이다. 서비스 클라이언트는 이 계층에서 사용하고자 하는 서비스의 함수를 가상으로 호출하고, 서비스 서버는 서비스 클라이언트가 요청한 서비스의 함수를 실제로 호출한다.
- RPC 계층 : 서비스 클라이언트는 이 계층에서 서비스의 함수를 호출하기 위한 RPC 코드와 RPC 데이터를 생성한다. 서비스 서버는 전달받은 RPC 코드를 토대로 함수를 찾고 RPC 데이터를 전달한다.
- IPC 계층 : RPC 계층에서 만든 RPC 코드와 RPC 데이터를 바인더 드라이버에 전달하기위한 바인더 IPC 데이터로 캡슐화하는 역할을 한다.
- 바인더 드라이버 계층 : IPC 계층으로부터 전달받은 바인더 IPC 데이터를 통해 서비스를 가진 서비스서버를 찾은 후 IPC 데이터를 전달한다.
서비스 클라이언트가 바인더 드라이버에게 ioctl() 시스템콜을 통해 바인더 IPC 데이터를 전달하면 바인더 드라이버는 이 데이터를 서비스 서버에게 전달한다. 이때 바인더 드라이버는 IPC 데이터에서 바인더 프로토콜을 파악하여 데이터 전달 여부를 결정한다.
바인더 프로토콜
바인더 프로토콜은 바인더 IPC 데이터에 포함되어 IPC 계층에서 바인더 드라이버로 전달되거나 바인더 드라이버에서 IPC 계층으로 전달된다. 바인더 프로토콜은 전달 방향에 따라 두 가지로 분류된다.
바인더 프로토콜은 흔히 네트워크 상에서 쓰는 프로토콜처럼 데이터를 송신하는 측과 수신하는 측에서 모두 알고 있는 규약이다. 따라서 바인더 IPC를 이용하는 프로세스와 바인더 드라이버는 헤더 파일에 바인더 프로토콜을 정의하고 있다.
다음 그림은 바인더 프로토콜에 따른 바인더 드라이버와 IPC 데이터 수신 측의 동작을 간략히 나타낸 것이다.
RPC 코드와 RPC 데이터
서비스 클라이언트는 서비스 서버에 존재하는 서비스의 함수를 사용하기 위해 각 함수에 해당하는 식별자를 바인더 IPC 데이터에 담아 전달하는데, 이를 RPC 코드라고 한다. 그리고 함수의 인자 역시 IPC 데이터에 담아 전달하는데, 이것은 RPC 데이터라고 한다.
다음은 Audio Flinger 서비스를 사용하는 MP# 애플리케이션의 예이다.
바인더 어드레싱
안드로이드에는 다양한 서비스를 모두 목록화해서 관리하는 컨텍스트 매니저(Context Manager)라는 특별한 프로세스가 있다. 컨텍스트 매니저는 서비스마다 핸들(바인더 IPC의 목적지 주소로 사용)이라는 번호 값을 할당하고, 서비스의 추가/검색 등의 관리 기능을 수행한다.
바인더 드라이버는 IPC 데이터의 핸들을 가지고 서비스 서버를 찾는데, 이러한 과정을 바인더 어드레싱(Binder Addressing) 이라고 정의한다. 바인더 어드레싱을 위해 서비스 서버는 자신이 가진 서비스에 대한 접근 정보를 컨텍스트 매니저에 등록해야 한다. 이러한 서비스 등록 과정에서 서비스 서버는 ADD_SERVICE라는 RPC 코드와 등록할 서비스 이름(RPC 데이터), 그리고 핸들을 0으로 지정하고 IPC 데이터에 실어 바인더 드라이버에 전달한다.
다음 그림은 서비스 서버가 자신의 서비스를 컨텍스트 매니저에게 등록 할 때의 바인더 어드레싱을 나타낸다.
다음으로 서비스 클라이언트가 서비스를 검색 할 때의 바인더 어드레싱을 나타낸다.
마지막으로 서비스 클라이언트는 이전 단계에서 전달받은 참조 데이터의 번호를 핸들에 저장하고, 사용할 서비스의 함수에 해당하는 RPC 코드와 RPC 데이터를 IPC 데이터에 담아 서비스 서버로 전달하여 서비스A가 가진 함수를 호출한다.
다음 그림은 바인더 어드레싱을 통해 서비스를 사용하는 과정을 보여준다.