스터디 7주차-1
바인더 심층 탐구
바인더의 개념
Android는 리눅스 기반이다. 그러므로 스마트폰과 같은 모바일 기기를 지원하기 위해 Android에서 기본으로 제공하는 시스템 기능은 모두 서버 프로세스 형태로 제공된다.
즉, 화면을 그리거나(Surface Flinger) 소리를 내고 조절(Audio Flinger)하는 기능 등을 사용하려면 사용자 모드(User Mode)에서 동작하는 별도의 프로세스에 화면을 그리고 소리를 내라고 요청해야 한다.
즉, 예를 들어 내가 만든 응용프로그램에서 Android SDK가 제공하는 위치 정보를 얻는 API를 호출할 때 내부적으로는 Location 서비스를 제공하는 Linux 프로세스로 요청을 보내고 결과를 응답받아 처리한다. 카메라를 사용할 때도 마찬가지로 Camera 서비스와 상호 연동한다.

(애플리케이션 프로세스의 Location 서비스와 Camera 서비스 호출)
그렇다면 Linux에서 제공하는 소켓, Pipe 등과 같은 IPC(Inter Process Communication)를 사용하지 않고 왜 바인더라는 메커니즘을 새로 만들었을까? 바로 성능 때문이다.
앞서 살펴본 것처럼 Android의 모든 시스템 기능은 서버 프로세스로 제공되기 때문에 프로세스 사이에 최적화된 통신 방법이 필요하고 그 고민의 결과가 바인더이다. 바인더는 모든 프로세스가 공유하는 커널 메모리를 참조하게 함으로써 메모리 복사 오버헤드를 최소화한다. 뿐만 아니라 C++을 이용해 작성된 RPC(Remote Procedure Call) 프레임워크를 제공하여 생산성이 높다.
시스템에서 제공하는 기능을 서버 프로세스 형태로 제공하고 바인더 메커니즘을 통해 요청과 응답을 받는 구조이다. 이와 같은 Android 플랫폼의 구조에는 어떤 장점이 있을까?
- 손쉬운 기능 확장/제거: 새로운 시스템 서비스를 추가하거나 기존의 기능을 제거하기 쉽다.
- 손쉬운 포팅: 새로운 프로세서로 포팅 시 변경 부위가 작다. 포팅을 위한 툴체인도 제공된다.
- 손쉬운 테스트: 테스트가 컴포넌트 단위로 한정되어 전체를 테스트하는 번거로움이 줄고 엄격하게 테스트할 수 있다.
- 분산 시스템 지원: 바인더 기반의 프로세스 통신을 하기 때문에 컴포넌트 간 위치 투명성이 보장된다.
상세히…
사실 바인더 메커니즘의 아이디어는 간단하다.
“모든 프로세스가 공유할 수 있는 영역에 요청 내용과 응답 내용을 쓰고, 각 프로세스가 그 메모리 주소를 참조하게 하자.”라는 것이다. 이를 위해 커널 공간이 사용된다.

커널 공간을 사용하도록 바인더 드라이버가 구현되어 있다.
바인더 드라이버의 역할은 각 프로세스가 매핑해 놓은 메모리 주소와 커널 공간의 메모리 주소를 변환하여 참조해 사용할 수 있도록 하는 것이다.

Linux에서 사용하는 표준 방법인 ioctl 시스템 함수를 이용해 바인더 드라이버를 사용할 수 있다. 이와 같은 일련의 메커니즘을 바인더 IPC라고 한다.

(바인더 드라이버를 통한 프로세스 간 사용자 데이터 송수신 구조 그림)
바인더 IPC를 이용해 주고받는 데이터를 가공해서 RPC(Remote Procedure Call)로 만들어 주는 C++ 프레임워크가 제공되고 시스템 서비스를 만들 때 사용한다. 다른 프로세스에서 제공하는 함수를 마치 내 함수처럼 사용할 수 있게 되는 것이다.

(바인더 드라이버에서 송수신된 데이터를 RPC로 해석하는 C++ 프레임워크)
바인더 IPC 는 RPC 형태이기 때문에, 실제 사용되는 곳은 안드로이드 서비스에서 사용된다. 서비스 클라이언트 (사용자), 서비스 서버 (서비스) 와의 통신 과정에서 사용된다. 따라서 안드로이드 서비스에 대해서 약간은 알고 있어야만 바인더의 실제 사용 예를 이해할 수 있다.
안드로이드 서비스의 분류는 위와 같은데, 시스템 서비스는 안드로이드에서 기본 기능 (GPS, Wifi 등) 을 제공하기 위해서 보통 사용되고, 애플리케이션 서비스가 실제 개발자들이 Application 을 통해 구현하는 서비스라고 볼 수 있다.
로컬 서비스란, 사용자(액티비티)와 서비스가 같은 프로세스에서 동작하는 것이다. 이 경우 둘은 같은 프로세스이기 때문에 IPC 가 필요하지 않다. 따라서 서비스의 생명주기는, 서비스를 생성한 액티비티와 동일하다.
원격 서비스란, 사용자(액티비티) 와 서비스가 서로 다른 프로세스에서 동작하는 것이다. 그렇기 때문에 IPC 가 필요하다. 따라서 원격 서비스의 동작원리를 살펴보는 것이, 안드로이드 서비스에서 실제 바인더가 사용되는 부분이 어디인지를 확인할 수 있다.
안드로이드 바인더 모델
- 핸들은 서비스를 구별하는 번호, 어떤 서비스에 바인더 IPC 데이터를 전달해야 하는지를 결정
- RPC 코드와 RPC 데이터는 서비스에 호출할 함수와 함수의 인자
- 바인더 프로토콜은 IPC 데이터의 처리 방법
바인더 IPC 데이터의 전달
-
open() 시스템 콜은 바인더 드라이버의 binder_open() 함수와 연결. 이 시스템 콜을 통해 바인더 드라이버의 파일 디스크립터를 얻음.
-
mmap() 시스템 콜은 바인더 드라이버의 binder_mmap() 함수와 연결. 이 시스템 콜을 통해 커널 내에서 IPC 데이터를 수신하기 위한 공유 공간 확보.
-
ioctl() 시스템 콜은 바인더 드라이버의 binder_ioctl() 함수와 연결. 이 시스템 콜을 통해 바인더 드라이버에 IPC 데이터를 전달.
ioctl() 함수는 ioctl(파일 디스크립터, ioctl 명령어, 데이터 타입) 의 형태로 사용. 첫 번째 인자는 바인더 드라이버를 OPEN 할 때 반환되는 파일 디스크립터. 세 번째 인자는 두 번째 인자인 명령어의 종류에 따라 달라짐.

바인더 IPC 데이터의 흐름
■ 서비스 계층 : 특정 기능을 수행하는 서비스의 함수가 존재하는 계층이다. 서비스 클라이언트는 이 계층에서 사용하고자 하는 서비스의 함수를 가상으로 호출하고, 서비스 서버는 서비스 클라이언트가 요청한 서비스의 함수를 실제로 호출한다.
■ RPC 계층 : 서비스 클라이언트는 이 계층에서 서비스의 함수를 호출하기 위한 RPC 코드와 RPC 데이터를 생성한다. 서비스 서버는 전달받은 RPC 코드를 토대로 함수를 찾고 RPC 데이터를 전달한다.
■ IPC 계층 : RPC 계층에서 만든 RPC 코드와 RPC 데이터를 바인더 드라이버에 전달하기위한 바인더 IPC 데이터로 캡슐화하는 역할을 한다. 실제로 서비스 클라이언트는 서비스 서버에 존재하는 서비스의 함수를 사용하기 위해 각 함수에 해당하는 식별자를 바인더 IPC 데이터에 담아 전달하는데, 이를 RPC 코드라 하며 함수의 인자 역시 IPC 데이터에 담아 전달하는데, 이것은 RPC 데이터라고 한다.
■ 바인더 드라이버 계층 : IPC 계층으로부터 전달받은 바인더 IPC 데이터를 통해 서비스를 가진 서비스서버를 찾은 후 IPC 데이터를 전달한다.
바인더 어드레싱
바인더 드라이버는 IPC 데이터의 핸들을 가지고 서비스 서버를 찾는데, 이러한 과정을 바인더 어드레싱(Binder Addressing)이라고 정의
다양한 서비스를 모두 목록화해서 관리하는 컨텍스트 매니저라는 특별한 프로세스가 서비스마다 핸들(바인더 IPC의 목적지 주소로 사용)라는 번호 값을 할당하고, 서비스 추가/검색 등의 관리 기능을 수행
■ 서비스 등록
바인더 어드레싱을 위해 서비스 서버는 자신이 가진 서비스에 대한 접근 정보를 컨텍스트 매니저에 등록해야 한다.
안드로이드 부팅 단계에서 끝나고, 등록이 끝나면 컨텍스트 매니저의 서비스 목록, 바인더 드라이버의 바인더 노드, 그리고 서비스 서버들의 서비스가 연결되어 다른 프로세스가 사용가능한 상태가 된다.
서비스 서버와 컨텍스트 매니저 사이의 IPC 그림
■ 서비스 검색
서비스 클라이언트와 컨텍스트 매니저 사이의 IPC 그림
■ 서비스 사용
서비스 클라이언트와 서비스 서버 사이의 IPC
※ mmap
일반적으로 커널 공간에서 사용자 공간으로 데이터를 전달하기 위해서는 copy_to_user() 함수를 이용해서 사용자 공간으로 데이터를 복사한다.
그러나 대용량의 데이터를 응용 프로그램과 주고 받아야 하는 상황에서 read, write, ioctl 같은 시스템 콜 함수를 쓴다면 매우 비효율적이며, 커널 공간이 부족할수 있다. -> 문제점을 해결하기 위해 mmap을 사용, 응용 프로그램이 커널의 물리 메모리에 직접 접근 가능하도록 해 준다.
바인더를 이용하는 프로세스는 바인더 드라이버의 mmap() 함수를 통해 사용자 공간과 매핑된 바인더 mmap 영역을 만든다. 그리고 바인더 드라이버는 커널 공간의 데이터 수신영역에 데이터를 저장만하고, 사용자 공간에 mmap 된 영역의 정보만 알려준다. 따라서 프로세스는 사용자 공간의 mmap 된 영역의 주소만 알고 있어도 커널 공간에 존재하는 데이터를 참조 할 수 있다.
이 함수의 주요 목적은 사용자 프로세스가 전달한 크기만큼 커널 공간에 IPC 데이터 수신 버퍼를 확보하는 것이다.
정리 및 요약
바인더는 안드로이드의 서비스를 보유한 서비스 서버, 서비스를 사용하고자 하는 서비스 클라이언트, 서비스의 위치를 알려주는 컨텍스트 매니저, 그리고 이들 모두를 중재하는 바인더 드라이버로 구성된다.
(출처 : https://d2.naver.com/helloworld/47656
http://blog.daum.net/tlos6733/120?category=16034
https://dev-ahn.tistory.com/89?category=613307)