07 안드로이드 바인더 IPC (1)
7.1 리눅스 메모리 공간과 바인더 드라이버
바인더 이해에 앞서 안드로이드 기반 커널인 리눅스 커널의 메모리 공간을 이해할 필요가 있음.
안드로이드의 프로세스는 리눅스와 마찬가지로 프로세스만의 고유한 가상 주소 공간 에서 실행. 가상 주소 공간은 사용자 공간 과 커널 공간 으로 나뉨.
프로세스는 각자 독립된 주소 공간을 가지고 별개로 동작. 프로세스가 다른 프로세스에게 데이터를 전달하는 방법은 프로세스 간 공유가 가능한 커널 공간을 이용하는 것.
커널 입장에서 봤을 때 프로세스는 하나의 작업 단위일 뿐이고 프로세스의 사용자 공간이 별도로 존재한다 하더라도 커널 공간에서 실행하는 태스크의 데이터와 코드는 서로 공유.
예시로 안드로이드를 탑재한 휴대폰에서 카메라로 찍은 사진을 화면에 보여주는 경우, 커널 공간을 통해 카메라를 동작시키는 프로세스가 화면 출력 담당 프로세스에게 화면 출력요청 메시지 전달. 즉, 두 프로세스 사이의 메시지를 주고 받는 IPC 를 수행.
바인더 는 원래 프로세스 간의 통신을 수행하는 IPC(Inter Process Communication) 도구이지만 안드로이드에서는 다른 프로세스에 있는 함수를 마치 현재 프로세스에 존재하는 함수처럼 사용할 수 있게 해주는 RPC(Remote Procedure Call) 를 지원하는데에 주로 이용.
바인더는 프로세스들이 사용자 공간을 공유하지 않는다는 제약 조건상에서 프로세스 간 통신을 제공하기 위해 커널 공간에서 동작하는 Binder(IPC) Driver(바인더 드라이버) 라는 추상화된 드라이버 이용.
안드로이드에서 바인더 드라이버를 추가해서 프로세스간 통신을 수행하는 이유
-
바인더는 리눅스의 뛰어난 메모리 관리 기법을 그대로 채용함으로써 커널 공간을 통한 데이터 전달 시 데이터의 신뢰성 확보 가능.
-
사용자 공간에서 접근할 수 없는 공간인 커널 공간을 이용해 데이터를 주고 받기 떄문에 IPC 간의 보안 문제 해결.
7.2 안드로이드 바인더 모델
서비스 클라이언트가 바인더를 통해 서비스 서버의 foo() 함수를 호출하는 과정으로부터 바인더 드라이버의 역할을 확인해 보면,
-
서비스 클라이언트는 바인더 IPC를 통해 서비스 서버의 foo() 함수를 호출하는 것과 같은 효과를 내기 위해 바인더 IPC 데이터(혹은 IPC 데이터)를 서비스 서버로 전달.
-
바인더 드라이버 는 foo() 함수를 호출하기 위한 바인더 IPC 데이터를 서비스 클라이언트에서 서비스 서버로 전달하는 역할. 즉, 프로세스 간 통신의 중재자 역할.
-
바인더 IPC 데이터는 상대편 프로세스의 함수 호출 정보들. 바인더 드라이버가 수행하는 IPC의 데이터 단위.
IPC 데이터 는 사용하고자 하는 서비스에 해당하는 번호와 호출할 함수명, 바인더 프로토콜(BINDER PROTOCOL)로 구성. 이러한 구성 요소는 IPC 데이터의 각 변수로 저장,
-
서비스 번호 : 안드로이드에서 동작 중인 여러 서비스를 구분하기 위한 것.
-
함수명 : 서비스 서버에서 동작하는 여러 서비스 가운데 서비스 클라이언트가 호출할 함수를 찾기 위한 것.
-
바인더 프로토콜 : 바인더 드라이버와 바인더를 이용하는 프로세스 간의 IPC 데이터를 처리하는 규약.
IPC 데이터의 포맷
-
핸들: 서비스를 구별하는 번호로, 바인더 드라이버에서 이 값을 통해 어떤 서비스에 바인더 IPC 데이터를 전달해야 하는지 결정.
-
RPC 코드: 서비스의 포함된 함수 중 어느 함수에 데이터를 전달해야 하는지 나타냄.
-
RPC 데이터: RPC 코드가 가리키는 함수에 전달할 인자 값.
-
바인더 프로토콜: IPC 데이터의 처리 방법.
7.2.1 바인더 IPC 데이터의 전달
바인더 드라이버는 시스템 콜 을 통해 접근 가능.
-
open() 시스템 콜 은 바인더 드라이버의 binder_open() 함수와 연결. 이 시스템 콜을 통해 바인더 드라이버의 파일 디스크립터를 얻음.
-
mmap() 시스템 콜 은 바인더 드라이버의 binder_mmap() 함수와 연결. 이 시스템 콜을 통해 커널 내에서 IPC 데이터를 수신하기 위한 공유 공간 확보.
-
ioctl() 시스템 콜 은 바인더 드라이버의 binder_ioctl() 함수와 연결. 이 시스템 콜을 통해 바인더 드라이버에 IPC 데이터를 전달.
ioctl() 함수는 ioctl(파일 디스크립터, ioctl 명령어, 데이터 타입)
의 형태로 사용. 첫 번째 인자는 바인더 드라이버를 OPEN 할 때 반환되는 파일 디스크립터. 세 번째 인자는 두 번째 인자인 명령어의 종류에 따라 달라짐.
7.2.2 바인더 IPC 데이터의 흐름
서비스 클라이언트에서 서비스 서버에 존재하는 서비스의 함수를 호출하는 과정을 보여주는 그림은 아래와 같음.
-
서비스 클라이언트에서 서비스 서버의 foo() 함수를 호출하기 위해 RPC를 시도함. 하위 계층에서 제공하는 동작을 통해 서비스 계층에서 RPC 수행.
-
서비스 계층 아래에 있는 계층에서는 서비스 계층의 RPC를 지원하기 위한 바인더 IPC 데이터 생성.
-
서비스 계층: 특정 기능을 수행하는 서비스의 함수가 존재하는 계층. 서비스 클라이언트는 이 계층에서 사용하고자 하는 서비스의 함수를 가상으로 호출하고, 서비스 서버는 서비스 클라이언트가 요청한 서비스의 함수를 실제로 호출.
-
RPC 계층: 서비스 클라이언트는 이 계층에서 서비스의 함수를 호출하기 위한 RPC 코드와 RPC 데이터 생성. 서비스 서버는 전달받은 RPC 코드를 토대로 함수를 찾고 RPC 데이터 전달.
-
IPC 계층: RPC 계층에서 만든 RPC 코드와 RPC 데이터를 바인더 드라이버에 전달하기 위한 바인더 IPC 데이터로 캡슐화.
* 바인더 드라이버에게 전달은 ioctl() 시스템콜 이용.
-
바인더 드라이버 계층: IPC 계층으로부터 전달받은 바인더 IPC 데이터를 통해 서비스를 가진 서버를 찾은 후 IPC 데이터를 전달. 이 때, 서버로의 데이터 전달 여부는 바인더 IPC 데이터의 바인더 프로토콜을 파악하여 결정.
7.2.3 바인더 프로토콜
바인더 프로토콜 은 바인더 IPC 데이터에 포함되어 IPC 계층에서 바인더 드라이버로 전달되거나 바인더 드라이버에서 IPC 계층으로 전달.
IPC 계층 -> 바인더 드라이버 방향의 전송은 BINDER COMMAND PROTOCOL
, 접두사 BC_ . 바인더 드라이버 -> IPC 계층 방향의 전송은 BINDER RETURN PROTOCOL
, 접두사 BR_ .
// 표 7-2
// 그림 7-9
-
서비스 클라이언트는 BC_TRANSACTION 프로토콜을 통해 바인더 드라이버에 IPC 데이터 전달을 명령.
-
바인더 드라이버는 전달받은 IPC 데이터를 토대로 바인더 프로토콜이 BC_TRANSACTION이면 IPC 데이터의 핸들을 통해 서비스 서버를 찾음.
-
이후 바인더 드라이버는 바인더 프로토콜을 BR_TRANSACTION으로 변경. 이를 IPC 데이터에 실어서 서비스 서버로 전달.
-
서비스 서버는 전달받은 바인더 프로토콜이 BR_TRANSACTION인지 체크한 후, IPC 데이터를 분석해서 서비스 사용자가 요청한 함수를 호출.
BR_TRANSACTION과 BC_TRANSACTION은 바인더 IPC 데이터 송신 측이 수신 측으로 데이터를 전달할 때 사용하는 프로토콜. 반대로 IPC 데이터를 전달받은 수신측이 IPC 데이터에 대한 응답 형태로 다시 IPC 데이터 송신측으로 데이터를 전달하는데 이를 바인더 IPC 응답 데이터 라고 함. 이 때 사용되는 바인더 프로토콜은 BC_REPLY
와 BR_REPLY
.
바인더 프로토콜은 송신/수신 측 모두 알고있는 규약. 바인더 IPC를 이용하는 프로세스와 바인더 드라이버는 헤더 파일에 바인더 프로토콜 정의.
7.2.4 RPC 코드와 RPC 데이터
바인더를 통해 상대방의 함수를 호출할 때, 상대방의 함수 이름과 전달 될 인자를 전달해야 함.
서비스 클라이언트는 서비스 서버에 존재하는 서비스의 함수를 사용하기 위해 각 함수에 해당하는 식별자를 바인더 IPC 데이터에 담아 전달. 이를 RPC 코드 라고 함. 함수의 인자 역시 IPC 데이터에 담아 전달하는데 이는 RPC 데이터.
서비스 서버가 가진 서비스의 함수를 호출하려면 서비스 클라이언트에서는 반드시 서비스 서버가 가진 RPC 코드를 알아야 함.
7.2.5 바인더 어드레싱
안드로이드에는 다양한 서비스를 모두 목록화해서 관리하는 컨텍스트 매니저(Context Manager) 라는 프로세스가 있는데, 이는 서비스마다 핸들(바인더 IPC의 목적지 주소로 사용)이라는 번호 값을 할당하고, 서비스의 추가/검색 등의 관리 기능을 수행. * 컨텍스트 매니저의 핸들값은 0으로 이미 지정되어 있음.
바인더 드라이버는 IPC 데이터의 핸들을 가지고 서비스 서버를 찾는데, 이러한 과정을 바인더 어드레싱(Binder Addressing) 이라고 함.
바인더 어드레싱을 위해서 먼저, 서비스 서버는 자신이 가진 서비스에 대한 접근 정보를 컨텍스트 매니저에 등록. 서비스 서버가 ADD_SERVICE
라는 RPC 코드와 등록할 서비스 이름(RPC 데이터), 핸들을 0으로 지정한 IPC 데이터를 바인더 드라이버에게 전달.
// 그림 7-11
(추후 내용 추가 예정)