이번 연재에서는 안드로이드를 위한 스프링을 사용하는 법을 알아보도록 하자. 

이 연재는 "인증" 이 첫번째 목적이고 , "REST 통신" 이 두번째 목적이다.


일단 관련 프로그램 설치



다음 예제 소스를 분석해 보도록 할 것이다. 


spring for android 홈페이지 : http://projects.spring.io/spring-android/

spring for android 샘플소스 : https://github.com/spring-projects/spring-android-samples


소스를 다운받아서 압축을 해제하면 나오는 프로젝트중에 spring-android-basic-auth 를 볼것이다.

spring-android-basic-auth 안에는 Server / Client 프로젝트가 각각 있는데 


Client 프로젝트는 안드로이드 스튜디오를 통해서 import 한다. (Import project (Eclipse ADT, Gradle, etc,) 



Server 프로젝트는 STS 를 통해서 Import 한다. (Existing Maven Projects) 



Server 실행 

스프링 서버를 import 한후에  프로젝트 오른쪽 클릭 -  Run as - Srping Boot app  클릭하여 서버를 실행시켜준다. 




실행 브라우저를 통한 서버 테스트

안드로이드 클라이언트를 실행하기 전에  브라우저를 통해서 테스트 해본다. 브라우저 띄우고

 localhost:8080/ 입력하면 



아래와 같이 이름/비밀번호 입력창이 뜬다. roy/spring 입력해서 인증한후에 

브라우저에  localhost:8080/getmessage 입력하면 데이터를 그냥 받을수있다. 




Client 실행

이제 안드로이드 스튜디오로 가보자. 먼저 res/values/urls.xml 에서  http://192.168.1.191:8080/   이것처럼  url 을 

자신의 내부ip 로 바꾸어주고 아까 임포트한 프로젝트에서 실행을 해보자. 



기기를 통해서 보면 다음과 같이 나온다.



이름 / 비밀번호를 넣고 Submit 를 누르면 , 토스트를 통해서 서버로 부터 받아온 메세지를 확인할수있다.

다음 연재에는 소스를 분석해 보도록하자. 

http://helloworld.naver.com/helloworld/textyle/407507 펌


NHN Business Platform 서비스플랫폼개발센터 오이석

트랜잭션 관리는 DBMS가 제공하는 여러 기능 중에서 가장 중요하고 기본적인 것 중의 하나로, DBMS 사용자들에게는 공기와 같은 존재입니다. 이 글에서는 우리가 트랜잭션을 커밋하거나 철회했을 때 어떤 일이 일어나는지, 어떻게 DBMS가 트랜잭션을 복구하는지에 대해서 알아보려고 합니다. 어떤 원리로 트랜잭션 관리라는 매직이 이루어지는지 살펴봅시다.

트랜잭션은 무엇인가?

잘 알려진 내용이라 진부한 측면이 있지만, 먼저 트랜잭션이 무엇인지 정의부터 살펴보자. 하나의 논리적 작업 단위를 구성하는 일련의 연산들의 집합을 트랜잭션이라고 한다. 트랜잭션의 예로 계좌 간의 자금 이체가 많이 언급된다. 한 계좌에서 10만 원을 인출하여 다른 계좌로 10만 원 입금하는 이체 작업은 전체 작업이 정상적으로 완료되거나, 만약 정상적으로 처리될 수 없는 경우에는 아무 것도 실행되지 않은 처음 상태로 되돌려져야 한다. 이러한 트랜잭션은 다양한 데이터 항목들을 액세스하고 갱신하는 프로그램 수행의 단위가 된다. 흔히 트랜잭션은 ACID 성질이라고 하는 다음의 네 가지 성질로 설명된다.

  • Atomicity(원자성): 이체 과정 중에 트랜잭션이 실패하게 되어 예금이 사라지는 경우가 발생해서는 안 되기 때문에 DBMS는 완료되지 않은 트랜잭션의 중간 상태를 데이터베이스에 반영해서는 안 된다. 즉, 트랜잭션의 모든 연산들이 정상적으로 수행 완료되거나 아니면 전혀 어떠한 연산도 수행되지 않은 상태를 보장해야 한다. atomicity는 쉽게 'all or nothing' 특성으로 설명된다.
  • Consistency(일관성): 고립된 트랜잭션의 수행이 데이터베이스의 일관성을 보존해야 한다. 즉, 성공적으로 수행된 트랜잭션은 정당한 데이터들만을 데이터베이스에 반영해야 한다. 트랜잭션의 수행을 데이터베이스 상태 간의 전이(transition)로 봤을 때, 트랜잭션 수행 전후의 데이터베이스 상태는 각각 일관성이 보장되는 서로 다른 상태가 된다. 트랜잭션 수행이 보존해야 할 일관성은 기본 키, 외래 키 제약과 같은 명시적인 무결성 제약 조건들뿐만 아니라, 자금 이체 예에서 두 계좌 잔고의 합은 이체 전후가 같아야 한다는 사항과 같은 비명시적인 일관성 조건들도 있다.
  • Isolation(독립성): 여러 트랜잭션이 동시에 수행되더라도 각각의 트랜잭션은 다른 트랜잭션의 수행에 영향을 받지 않고 독립적으로 수행되어야 한다. 즉, 한 트랜잭션의 중간 결과가 다른 트랜잭션에게는 숨겨져야 한다는 의미인데, 이러한 isolation 성질이 보장되지 않으면 트랜잭션이 원래 상태로 되돌아갈 수 없게 된다. Isolation 성질을 보장할 수 있는 가장 쉬운 방법은 모든 트랜잭션을 순차적으로 수행하는 것이다. 하지만 병렬적 수행의 장점을 얻기 위해서 DBMS는 병렬적으로 수행하면서도 일렬(serial) 수행과 같은 결과를 보장할 수 있는 방식을 제공하고 있다.
  • Durability(지속성): 트랜잭션이 성공적으로 완료되어 커밋되고 나면, 해당 트랜잭션에 의한 모든 변경은 향후에 어떤 소프트웨어나 하드웨어 장애가 발생되더라도 보존되어야 한다.

트랜잭션은 다음의 <표 1>과 같이 세 가지 중 하나의 형태로 종료된다. 문제 없이 정상적으로 수행된 경우에는 커밋을 통해서 종료될 것이고, 잘못된 입력이 주어졌거나 일관성 제약 조건을 위배한다거나 하는 상황이 발생되거나 사용자의 요청에 의하여 철회되는 경우가 있으며, 타임 아웃이나 교착 상태 등과 같이 시스템이 감지하는 문제로 인하여 DBMS가 철회하는 경우가 있다.

표 1 트랜잭션의 세 가지 가능한 결과 형태(Gray 외, 1981)

BEGIN

READ

WRITE

READ

WRITE

COMMIT

BEGIN

READ

WRITE

READ

ABORT

BEGIN

READ

WRITE

READ

ç SYSTEM ABORTS TRANSACTION

이 외에도 트랜잭션은 각종 시스템 고장으로 인하여 영향을 받을 수 있으며, DBMS는 이와 같은 상황에서 트랜잭션을 관리해야 한다.

트랜잭션 관리를 위한 DBMS의 전략

트랜잭션 관리를 위한 DBMS의 전략을 이해하기 위해서는 우선 DBMS의 개략적인 구조와 버퍼 관리자 및 트랜잭션 관리와 연관된 버퍼 관리 정책에 대한 이해가 필요하다.

데이터베이스 시스템은 보통 비휘발성 저장 장치인 디스크에 데이터를 저장하며 전체 데이터베이스의 일부분을 메인 메모리에 유지한다. DBMS는 데이터를 고정 길이의 페이지(page)로 저장하며, 디스크에서 읽거나 쓸 때에 페이지 단위로 입출력이 이루어진다. 메인 메모리에 유지하는 페이지들을 관리하는 모듈을 보통 페이지 버퍼(page buffer) 관리자 또는 버퍼 관리자라고 부르는데, DBMS의 많은 주요 모듈 중에서 매우 중요한 모듈 중의 하나이다. DBMS는 각 제품마다 구조가 다르기는 하지만, <그림 1>과 같이 크게 질의 처리기(Query Processor)와 저장 시스템(Storage System)으로 나눠볼 수 있다. MySQL의 경우에는 InnoDB, MyISAM 등과 같이 여러 하부 저장 시스템을 선택할 수 있는데, 이와 같은 모델은 상부의 질의 처리기와 하부의 저장 시스템 간의 명확하게 구분되는 계층(layered) 구조에 해당한다. CUBRID 역시 질의 처리기와 저장 시스템 두 개의 구성 요소로 이루어져 있으며, 질의 처리기와 저장 시스템이 좀 더 밀접하게 연결되어 있다.

343d976a40006a2bc3d59f9243069fc1.png

그림 1 DBMS의 개략적인 구조

DBMS의 많은 구성 요소 중에서 굳이 버퍼 관리자를 소개한 이유는 버퍼 관리 정책이 트랜잭션 관리에 매우 중요한 결정을 가져오기 때문이다. 버퍼 관리 정책에 따라서 트랜잭션의 UNDO 복구와 REDO 복구가 요구되거나 그렇지 않게 된다. 이 부분에 대해서 하나씩 살펴보자.

UNDO는 왜 필요할까?

오퍼레이션 수행 중에 수정된 페이지들이 버퍼 관리자의 버퍼 교체 알고리즘에 따라서 디스크에 출력될 수 있다. 버퍼 교체는 전적으로 버퍼의 상태에 따라서 결정되며, 일관성 관점에서 봤을 때는 임의의 방식으로 일어나게 된다. 즉 아직 완료되지 않은 트랜잭션이 수정한 페이지들도 디스크에 출력될 수 있으므로, 만약 해당 트랜잭션이 어떤 이유든 정상적으로 종료될 수 없게 되면 트랜잭션이 변경한 페이지들은 원상 복구되어야 한다. 이러한 복구를 UNDO라고 한다. 만약 버퍼 관리자가 트랜잭션 종료 전에는 어떤 경우에도 수정된 페이지들을 디스크에 쓰지 않는다면, UNDO 오퍼레이션은 메모리 버퍼에 대해서만 이루어지면 되는 식으로 매우 간단해질 수 있다. 이 부분은 매력적이지만 이 정책은 매우 큰 크기의 메모리 버퍼가 필요하다는 문제점을 가지고 있다. 수정된 페이지를 디스크에 쓰는 시점을 기준으로 다음과 같은 두 개의 정책으로 나누어 볼 수 있다.

  • STEAL: 수정된 페이지를 언제든지 디스크에 쓸 수 있는 정책
  • ¬STEAL: 수정된 페이지들을 최소한 트랜잭션 종료 시점(EOT, End of Transaction)까지는 버퍼에 유지하는 정책

STEAL 정책은 수정된 페이지가 어떠한 시점에도 디스크에 써질 수 있기 때문에 필연적으로 UNDO 로깅과 복구를 수반하는데, 거의 모든 DBMS가 채택하는 버퍼 관리 정책이다.

REDO는 왜 필요할까?

이제는 UNDO 복구의 반대 개념인 REDO 복구에 대해서 알아볼 것인데, 앞서 설명한 바와 같이 커밋한 트랜잭션의 수정은 어떤 경우에도 유지(durability)되어야 한다. 이미 커밋한 트랜잭션의 수정을 재반영하는 복구 작업을 REDO 복구라고 하는데, REDO 복구 역시 UNDO 복구와 마찬가지로 버퍼 관리 정책에 영향을 받는다. 트랜잭션이 종료되는 시점에 해당 트랜잭션이 수정한 페이지들을 디스크에도 쓸 것인가 여부로 두 가지 정책이 구분된다.

  • FORCE: 수정했던 모든 페이지를 트랜잭션 커밋 시점에 디스크에 반영하는 정책
  • ¬FORCE: 수정했던 페이지를 트랜잭션 커밋 시점에 디스크에 반영하지 않는 정책

여기서 주의 깊게 봐야 할 부분은 ¬FORCE 정책이 수정했던 페이지(데이터)를 디스크에 반영하지 않는다는 점이지 커밋 시점에 어떠한 것도 쓰지 않는다는 것은 아니다. 어떤 일들을 했었다고 하는 로그는 기록하게 되는데 이 부분은 아래에서 자세히 설명한다.

FORCE 정책을 따르면 트랜잭션이 커밋되면 수정되었던 페이지들이 이미 디스크 상의 데이터베이스에 반영되었으므로 REDO 복구가 필요 없게 된다. 반면에 ¬FORCE 정책을 따른다면 커밋한 트랜잭션의 내용이 디스크 상의 데이터베이스 상에 반영되어 있지 않을 수 있기 때문에 반드시 REDO 복구가 필요하게 된다. 사실 FORCE 정책을 따르더라도 데이터베이스 백업으로부터의 복구, 즉 미디어(media) 복구 시에는 REDO 복구가 요구된다. 거의 모든 DBMS가 채택하는 정책은 ¬FORCE 정책이다.

정리해보면 DBMS는 버퍼 관리 정책으로 STEAL과 ¬FORCE 정책을 채택하고 있어, 이로 인해서 UNDO 복구와 REDO 복구가 모두 필요하게 된다. 

트랜잭션 관리

지금까지 설명한 UNDO 복구와 REDO 복구를 위해서 가장 널리 쓰이는 구조는 로그(log)이다. Shadow paging(nilavalagan, 2009)이라고 불리는 복구 기법도 존재하지만, 여기서는 보편적으로 사용되는 로그 기법에 대해서만 설명하기로 한다.

로그

로그는 로그 레코드의 연속이며 데이터베이스의 모든 갱신 작업을 기록한다. 로그는 이론적으로는 안정적 저장 매체(stable storage)에 기록된다고 하는데, 안정적 저장 매체는 어떤 경우에도 절대로 손실이 발생하지 않는 이른바 이상적인 매체이다. 바꿔 말하면 현실 상에서는 존재하지 않는다고 봐야 하는데, RAID 등 인프라 시스템의 도움 외에도 DBMS 자체적으로 여러 벌의 로그를 유지하는 등 안정적 저장 매체처럼 동작하게 하는 기법을 사용하기도 한다. 하지만 대부분 DBMS는 성능 상의 이유로 하나의 로그를 유지한다.

로그는 덧붙이는(append) 방식으로 기록되며, 각 로그 레코드는 고유의 식별자를 가진다. 로그 레코드의 식별자를 LSN(Log Sequence Number) 혹은 LSA(Log Sequence Address)라고 부른다. 로그는 항상 뒤에 덧붙이는 방식으로 쓰이기 때문에, 로그 식별자는 단조 증가하는 성질을 가진다. 로그 데이터는 기록할 오브젝트의 타입에 따라서 물리적/논리적 로깅으로 분류할 수 있고, 데이터베이스의 상태 또는 변화를 야기한 전이를 기록하느냐에 따라서 분류할 수 있다.

표 2 로그 데이터 분류(Haerder & Reuter, 1983)

 

State

Transition

Logical

-

액션(DML문, DDL문)

Physical

이전 이미지

이후 이미지

XOR 차이

물리적인 상태 로깅(physical state logging)

이 방법은 DBMS에서 가장 널리 쓰이는 기본적인 로깅 방법인데 이에 해당하는 로그 레코드는 갱신 이전 이미지와 이후 이미지를 모두 다 가지고 있으며, UNDO 복구 때에는 이전 이미지로 현재 이미지를 대체하며, REDO 복구 때에는 이후 이미지를 반영하는 방식으로 복구가 이루어진다. 결국 이런 복구 작업은 이전 이미지 혹은 이후 이미지로 단순히 대체하는 작업으로 이해하면 된다. 예를 들어, UPDATE 문장에 대한 로깅은 수정 이전 이미지(즉, 수정 전 레코드 이미지)와 이후 이미지(새로 갱신하는 레코드 이미지)를 모두 기록하고, UNDO 시에는 수정 이전 이미지로 대체하는 식으로, REDO가 필요한 경우에는 수정 이후 이미지를 반영하는 식으로 이루어진다. 물리적 상태 로깅은 때로는 페이지 수준(예를 들어, 인덱스나 데이터 파일의 헤더 페이지의 변경 로깅)에서 이루어지기도 하고, 레코드 수준에서 이루어지기도 한다.

물리적인 전이 로깅(physical transition logging)

이 방법은 페이지 혹은 레코드에 대해서 이전 및 이후 이미지를 모두 기록하기 보다는 XOR 차이점을 기록하는 방식으로 이루어진다. 복구 시점에서 로그 레코드에 기록된 XOR 이미지와 레코드 이미지를 이용하여 UNDO 복구와 REDO 복구를 수행하게 된다.

논리적인 전이 로깅(logical transition logging)

이 방법은 오퍼레이션 로깅(operation logging)으로도 불리는데, 물리적인 로깅이 결과 값을 기록하는 방식이라면 논리적인 로깅은 어떤 일을 했었는가를 기록하는 방식이다. 예를 들어, a = a + 1과 같은 연산을 로깅할 때 이전 값 0, 이후 값 1을 물리적으로 기록할 수도 있고, a = a + 1 이라는 연산 그 자체를 기록할 수 있다. 이러한 논리적인 로그에 대한 복구 작업은 REDO를 위하여 로그 레코드에 기록된 오퍼레이션을 재수행하거나, UNDO를 위하여 역 오퍼레이션을 수행하는 방식으로 이루어진다.

이런 논리적인 전이 로깅은 로그 레코드의 크기를 크게 줄여준다는 장점이 있다. 하지만 더 중요한 점은 물리적으로 복구하기 쉽지 않은 자료 구조에 대한 로깅을 쉽게 해준다는 점이다. 예를 들어, 인덱스 구조로 많이 사용되는 B+-tree 또는 B-tree 는 split, merge 와 같은 SMO(Structure Modification Operation)를 통해서 레코드의 위치가 계속 변경되기 때문에 로깅 시점과 복구 시점의 데이터 물리적 위치가 같다는 점이 보장되지 않기 때문에(페이지 내의 위치가 다를 수도 있고, 심지어 다른 페이지에 위치할 수도 있다), 물리적인 로그를 통해서 복구하기가 쉽지 않지만, 논리적인 로그를 통해서 보다 쉽게 복구할 수 있다. 즉, 인덱스에 키 값 k와 포인터 p가 저장되었다는 논리 로그에 대한 REDO 복구는 인덱스에 (k, p)를 다시 삽입하는 작업이면 충분하고, UNDO 복구는 (k, p)를 인덱스에서 제거하는 작업을 수행하면 된다.

DBMS 제품들은 위에서 살펴본 물리적인 상태 로깅, 물리적인 전이 로깅, 논리적인 전이 로깅 방법(그 외에도 물리-논리(physiological) - 수정한 페이지를 물리적으로 식별할 수 있지만 해당 페이지 내에서는 논리적으로 기록되는 - 로깅 기법도 존재한다) 중에 하나만을 선택하여 사용하는 것이 아니라 이들을 적절하게 혼용한다. CUBRID도 각각의 로깅 기법이 유리한 경우에 대해서 물리적인 로깅과 논리적인 로깅을 함께 사용하고 있다. 위에서 설명한 3개의 그룹으로 나눠볼 수 있는 로그 레코드가 DBMS 내에 실제로 몇 종류나 필요할까 하는 궁금증이 드는데, DBMS마다 다르지만 CUBRID의 경우에는 UNDO 로그, REDO 로그, 커밋 로그 같은 것을 포함하여 약 40여 종류의 로그 레코드가 존재한다. 같은 로그 레코드라고 하더라도 자료 구조마다 복구 연산이 다르기 때문에 DBMS가 가지고 있는 복구 연산(함수)은 로그 레코드 종류보다는 훨씬 많이 필요한데, CUBRID는 현재 약 100여 개의 복구 연산을 가지고 있다.

여기서 한 가지 더 얘기할 사항은 로그를 통한 UNDO 복구, REDO 복구는 멱등성(idempotent)을 가져야 한다는 점이다. 멱등성은 여러 번 수행하더라도 한 번 수행한 결과와 같아야 한다는 것을 의미하는데, 물리적인 로그를 통한 복구는 자연스럽게 멱등성이 보장되지만, 논리적인 로그를 통한 복구는 그렇게 간단하지 않다. 예를 들어, a++ 연산을 여러 번 반복해서 복구하게 되면 정확하지 않게 복구될 수 있게 되는데, 이런 일을 방지하기 위해서 한 번 수행한 복구 연산을 또 다시 수행하지 않도록 해야 한다. 이를 어떻게 해결하는지는 이후에 살펴보기로 한다.

로그는 어떻게 쓸까?

로그는 로그 타입에 관계없이 다음의 규칙에 따라 써진다.

  • 해당 업데이트가 데이터베이스에 써지기 전에 먼저 관련된 UNDO 정보가 로그에 써져야 한다. 이 원칙을 WAL(Write Ahead Logging)이라고 부른다. 어떤 경우에도 UNDO 복구가 되기 위해서는 반드시 WAL 규칙이 준수되어야 한다.

  • 트랜잭션이 정상적으로 종료 처리되기 위해서는 먼저 REDO 정보가 로그에 써져야 한다. 역시 어떤 경우에도 REDO 복구를 할 수 있기 위해서는 REDO 로그가 적어도 커밋 시점에는 써져야 한다.

DBMS는 로그 레코드를 위한 별도의 버퍼를 유지하는데, 이를 로그 버퍼라고 한다. 로그 버퍼 관리는 DBMS마다 서로 다른 방식으로 구현하는데 로그 버퍼를 통해서 로그 파일에 입출력한다는 점은 같다. 성능을 위해서 로그 버퍼에 로그 레코드를 모았다가 블록 단위로 로그 파일에 출력한다.

트랜잭션들이 동시에 수행을 하면서 각각의 연산에 대해서 로그 레코드를 생성하게 되는데, 이들은 로그 버퍼에 유지되게 되고 몇몇 시점에 로그 파일에 써지게 된다. 로그 버퍼에 유지된 로그 레코드는 (1) 어떤 트랜잭션이 커밋을 요청한 경우, (2) WAL을 해야 하는 경우, (3) 로그 버퍼가 다 소진된 경우, (4) DBMS가 내부적으로 필요로 하는 경우(예를 들어, 체크 포인트(checkpoint) 연산, 로그 관리 연산 등)에 로그 파일에 출력된다. 대부분의 경우는 (1)과 (2)의 경우로 발생되며 (3), (4)로 인해서 수행되는 경우도 있다. 로그 버퍼는 상대적으로 작기(대개 수MB에서 수십MB 수준) 때문에 긴(long) 트랜잭션이 수행 중인 경우에는 로그 버퍼가 소진될 수 있다.

어떤 트랜잭션이 커밋을 요청하는 경우에는 해당 트랜잭션의 마지막 로그 레코드까지 출력하면 되는데, <그림 2>에서 트랜잭션 T1이 커밋할 때 LSN3까지의 로그 레코드가 로그 파일에 써져야 한다.

5205aed6cb95030dbd71df8b2a05a419.png

그림 2 로그 버퍼의 예

로그를 쓰는 일은 왜 느릴까?

로그 레코드가 손실되는 경우가 발생되면 데이터베이스가 완전히 복구될 수 없기 때문에 로그 레코드를 안전하게 쓰는 것이 필요한데, DBMS는 최대한 안전하게 로그를 쓰기 위해서 write 함수(내지는 writev 함수) 호출 외에 fsync 함수(fsync(2) - Linux man page, 2013)를 호출한다. fsync 함수 호출이 디스크에 물리적으로 써지는 것까지 보장하면 좋겠지만 리눅스를 포함한 대부분의 운영 체제는 그렇게까지 보장하지는 않는다. fsync 함수 호출은 매우 느린 연산이고, 커밋을 위해서는 해당 트랜잭션의 로그가 로그 파일에 써져야 하기 때문에 커밋을 하려는 트랜잭션은 fsync 함수 호출이 종료되기를 대기해야 한다.

더 자세히 보면, 로그 버퍼를 쓸 때에는 로그 헤더 정보와 로그 레코드를 써야 하는데, 로그 레코드를 먼저 쓰고 fsync 함수를 실행하고, 로그 헤더를 업데이트한 후에 다시 fsync 함수를 실행해야 한다. 로그는 끝에 추가되는 방식으로 써지기 때문에 이와 같이 하지 않으면 로그 레코드나 헤더가 온전하지 않은 상태로 기록될 수 있게 된다. fsync를 처리할 때 어떤 버퍼 프레임부터 디스크에 쓰게 될지는 DBMS 입장에서는 알 수 없고 DBMS가 원하는 순서대로 디스크에 반영하도록 할 방법도 없기 때문에, 이와 같이 여러 단계를 거쳐 로그를 쓰게 된다.

하지만 로그 버퍼를 로그 파일에 쓸 때에 한 번에 한 페이지만 쓰는 것이 아니라 여러 페이지를 쓰는 경우가 대부분인데, 이런 경우의 로그 쓰기 작업은 더 복잡하게 이루어진다. 예를 들어, 로그 버퍼 Bi부터 Bk까지 출력하는 경우에, 먼저 Bj부터 Bk까지 로그 파일에 쓰고 fsync 함수를 실행하고, 첫 번째 로그 버퍼인 Bi를 쓴 후 다시 fsync 함수를 실행하고 나서, 비로소 로그 헤더를 업데이트하는 절차로 이루어진다.

2b978007f0168bc97f8568300c1a47c9.png

그림 3 로그 버퍼를 로그 파일에 쓰는 순서

대부분의 커밋 연산이 소모하는 시간은 로그 레코드를 로그 파일에 쓰고, fsync 함수를 실행하는 시간이라고 보면 된다. 정확성을 위해서는 fsync 함수를 여러 차례 호출해야 하는데, 일 초에도 수천, 수만 트랜잭션이 커밋을 요청하는 상황을 생각해보면 로깅을 위해서 얼마나 많은 디스크 출력이 있어야 하는지 쉽게 이해할 수 있을 것이다.

로그 쓰기 작업, 즉 커밋 오퍼레이션의 성능을 높일 방법이 없을까?

성능을 위한 몇 가지 기법이 있는데, 먼저 그룹 커밋(group commit)부터 알아보자. 그룹 커밋은 각각의 트랜잭션의 커밋 요구를 개별적으로 처리하기 보다는 모아서 한꺼번에 처리하는 방식이다. 수천 내지는 수만 TPS 수준의 요청이 있다고 했을 때, 한 트랜잭션이 커밋할 때 잠시만 기다리면 다른 트랜잭션들이 역시 커밋을 요청할 것이고, 이들의 요청을 한 번에 처리하게 되면 디스크 출력 횟수를 줄일 수 있으므로 이로 인해 성능을 높일 수 있게 된다. 즉, 그룹 커밋은 여전히 정확성을 보장하면서 각 트랜잭션의 응답 시간(response time)은 약간 희생시키는 경우가 발생되더라도 시스템 전체의 처리량(throughput)을 높이자는 의도이다. 쉽게 생각해서 개별적으로 승용차를 이용하는 방식과 고속 버스를 이용하는 방식을 연상하면 된다. 고속 버스의 경우 정해진 출발 시각까지 대기해야 하지만, 한 번에 이동하는 승객이 많기 때문에 효율은 높다.

그룹 커밋은 (한계 시점 이전까지는) 동시에 요청되는 커밋 요구가 많을수록 그 효율이 높아지는데, 적절한 그룹 커밋 대기 시간을 정하는 것이 시스템 성능에 매우 중요하다. 너무 짧으면 효율이 떨어지게 되고, 너무 길면 응답 시간이 느려지고 효율은 더 이상 높아지지 못하게 된다. 최적의 값은 워크로드 패턴에 따라서 다르며 대게 짧게는 몇 밀리초(ms)에서 길게는 수백 밀리초(ms)까지 설정하기도 하는데, 시스템의 부하에 따라서 시스템이 적응적(adaptive)으로 자동 조정(Helland 외, 1988)하기도 한다.

성능을 위해서 지속성을 살짝 포기할 수는 없을까?

커밋 성능을 극대화하기 위해서 지속성(durability)을 일부 포기하는 방식도 있다. DBMS 제품에 따라서는 매 커밋마다 정확하게 로그를 쓰고 fsync 함수를 실행하는 것이 아니라 보다 나은 성능을 위해 좀 더 느슨하게 로그를 쓰는 옵션을 제공하기도 한다. 예를 들어, InnoDB는 innodb_flush_log_at_trx_commit 파라미터(InnoDB Startup Options and System Variables, 2013)의 설정을 통해서 로그를 쓰는 방식을 조정할 수 있다.

또한, 응용이나 시스템의 성격에 따라서는 비동기 커밋(asynchronous commit) 방식을 사용하기도 하는데, 비동기 커밋은 로그 버퍼에 로그 레코드를 쓰고 곧바로 커밋을 완료하는 방식이다. 즉, 로그 파일에 로그가 써질 때까지 대기하지 않고 커밋을 하게 된다. 로그 레코드는 로그 쓰기 스레드(내지는 프로세스)가 이후에(대개는 매우 짧은 시간 내에 곧바로) 비동기적으로 쓰게 되는데, 트랜잭션의 로그가 미처 써지기 전에 시스템에 장애가 발생되면 해당 트랜잭션은 이미 커밋을 완료했지만 손실되게 된다. 비동기 커밋 방식으로 인해 발생할 수 있는 데이터의 손실은 커밋한 트랜잭션이 변경한 데이터의 유실(loss)이며, 이 때도 데이터의 일관성은 보장된다. 최근의 몇몇 트랜잭션의 커밋 로그가 유실된 것이기 때문에 복구 시점에 DBMS는 마치 해당 트랜잭션이 커밋을 하지 않은 것으로 간주하여 이를 철회(rollback)시키게 된다. 철회를 위한 UNDO 로그는 이미 트랜잭션 수행 중에 WAL 원칙에 따라서 트랜잭션 로그에 써져 있기 때문에, 커밋되지 않은 데이터가 데이터베이스에 반영되지는 않는다. 비동기 커밋은 성능 향상 효과가 크기는 하지만, 손실이 발생할 수 있으므로 응용의 성격에 따라서 신중하게 선택해야 할 필요가 있다. 동시에 커밋 요청이 매우 많이 요구되며 데이터의 일부 유실을 감내할 수 있는 응용 환경에서는 적용을 고려해볼 수 있을 것이다. CUBRID도 그룹 커밋과 비동기 커밋 방식을 모두 제공하고 있고, 설정을 통해서 쉽게 적용할 수 있다.

어떻게 로그로 복구가 이루어지나?

이제 그러면 로그를 통해서 어떻게 복구가 이루어지는지 알아보자. 복구에는 두 가지 종류가 있는데, 사용자의 요청 또는 오류 발생 등으로 인해서 시스템이 트랜잭션을 철회하는 경우와 소프트웨어 문제나 하드웨어 문제 등으로 인해서 장애가 발생하고 데이터베이스 시스템이 재시작 복구(restart recovery)하는 경우가 있다.

트랜잭션 철회는 어떻게?

트랜잭션을 철회하는 경우는 시스템은 정상적으로 동작하고 있는 중이며 특정 트랜잭션만 철회하는 경우인데, 이 때 트랜잭션의 철회는 다음과 같이 이루어진다. 먼저 로그를 역방향으로 탐색하면서 해당 트랜잭션의 UNDO 복구가 필요한 로그를 찾아서 이에 해당하는 UNDO 연산을 수행한다. 역방향으로 로그를 탐색하면서 트랜잭션 수행 순서의 역순으로 UNDO를 수행해야 정확하게 UNDO가 이루어질 수 있다.

UNDO를 수행하고 나면 해당 UNDO 작업에 대한 보상 로그 레코드(CLR, Compensation Log Record)라고 하는 REDO 전용 로그를 쓰게 되는데, UNDO를 하고 난 이후에 다시 UNDO를 해서 복구가 잘못 이루어지지 않도록 하기 위함이다. CLR은 이전 로그 레코드 위치를 UNDO 로그의 이전 로그를 가리키도록 하여 이후에는 한 번 UNDO된 로그를 다시 접근하여 재차 UNDO하게 되는 일이 발생되지 않도록 해준다. 이전 로그를 계속 탐색하면서 해당 트랜잭션의 시작 로그까지 도달하면 해당 트랜잭션의 철회 복구가 완료된 것이다.

장애로 인해 재시작되면 어떻게 복구가 되나?

장애 발생 이후 데이터베이스가 재시작 복구하는 경우에는 크게 3 단계로 복구가 이루어진다.

1 단계는 로그 분석 단계로, 마지막 체크포인트(checkpoint) 시점부터 최근 로그(EOL, End of Log)까지 로그를 탐색하면서 어디서부터 시스템이 복구를 시작해야 하는지, 어느 트랜잭션들을 복구해야 하는지 등등을 알아내는 단계이다.

2 단계는 REDO 복구 단계로 복구를 시작해야 하는 시점부터 장애 발생 직전 시점까지 REDO가 필요한 모든 로그를 REDO 복구를 하는 단계이다. 이 단계에서는 심지어 실패한 트랜잭션의 REDO 로그조차도 REDO를 하게 되는데, 언뜻 보면 불필요한 것으로 생각되지만 이렇게 하면 이후의 복구 단계를 매우 간단하게 하는 효과를 가져다 준다. 이 단계에서는 모든 트랜잭션에 대해서 REDO 복구만 한다는 점이 중요한데, 이러한 REDO 복구가 완료된 시점의 데이터베이스 상태는 장애 발생 시점의 상태와 같게 된다. 이전 상황을 그대로 재현하여 복원한다는 의미로 이 REDO 복구에서 이루어지는 작업을 repeating history(Mohan 외, 1992)라고 부른다.

마지막 3 단계는 UNDO 복구 단계로 로그를 최신 시점부터 다시 역방향으로 탐색하면서 UNDO 복구가 필요한 로그들에 대해서 UNDO 복구를 수행한다. 여기서 수행하는 UNDO는 결국 위에서 설명한 트랜잭션 철회 시에 수행하는 UNDO와 같은 방식으로, repeating history를 통해 데이터베이스 상태를 장애 시점까지 복원해두고 UNDO 복구를 여러 트랜잭션의 철회로 간단하게 해결할 수 있다. 한 트랜잭션만 철회시키는 것이 아니라 여러 트랜잭션을 철회시킨다는 차이점만 존재한다. 이 단계의 UNDO 복구를 개별 트랜잭션의 UNDO와 구별하여 Global UNDO라고도 부른다.

2880ae634676944afdd10bed6a86954f.png

그림 4 재시작 복구 단계와 로그 접근 방향

로그를 통한 복구 과정 중에 특정 로그가 UNDO 내지는 REDO 복구가 필요한 것인지를 판단해야 할 필요가 있다. 이미 로그가 반영되었다면 그 로그에 대한 복구 연산은 필요치 않은데 이는 어떻게 해결할까? 앞서 설명한 바와 같이 모든 로그에는 LSN이라고 하는 식별자가 있는데, 데이터베이스의 모든 페이지는 page LSN을 가지고 있다. 이 page LSN은 페이지가 갱신될 때마다 해당 로그의 LSN으로 갱신된다. 즉, 모든 페이지는 해당 페이지를 마지막으로 갱신한 로그의 식별자를 포함하고 있으므로, 로그를 적용해야 할지 여부는 해당 로그의 LSN과 page LSN을 비교함으로써 판단할 수 있다. Page LSN이 어떤 로그의 LSN보다 예전 것이라면 해당 페이지는 반드시 해당 로그로 복구되어야 한다는 것을 의미하며, 반대로 page LSN이 해당 로그의 LSN과 같거나 더 최신의 값을 가지고 있다면 이 페이지는 해당 로그보다 나중에 쓰인 로그로 이미 갱신되었다는 것을 의미하므로 복구가 필요치 않다는 것을 의미한다. CUBRID는 page LSN으로 페이지 시작 부분의 8바이트의 공간을 사용하므로, 기본 16KB의 페이지를 사용하는 경우 실제 데이터가 저장되는 공간은 page LSN을 위한 공간을 제외한 16376바이트가 된다.

e9bfe03d5dbbde428c1522181eba2dfc.png

그림 6 데이터베이스의 페이지 구성

백업을 이용한 미디어 복구는 어떻게?

디스크 미디어(media)의 문제가 생겼을 때 수행하는 미디어 복구, 일명 아카이브(archive) 복구가 있는데, 이는 데이터베이스의 백업으로부터 복구를 하는 것을 의미한다. 데이터베이스 백업 기법에는 여러 가지가 있는데, 데이터베이스가 수행 도중에 트랜잭션들의 수행을 방해하지 않고 현재 스냅샷(snapshot)을 그대로 복사하는 퍼지(fuzzy) 백업이 CUBRID를 포함한 상용 DBMS가 사용하는 기법이다.

트랜잭션이 수행하고 있는 도중에 데이터베이스 이미지를 복사하는 것이기 때문에 미처 커밋하지 못한 일부 트랜잭션의 이미지가 복사될 수도 있고, 커밋한 트랜잭션의 데이터가 아직 반영되지 못한 채로 복사가 될 수도 있다. 이렇게 퍼지하게 복사한 데이터베이스 백업으로 어떻게 복원(restore)를 할까? 역시나 답은 로그에 있다. 미디어 복구 시에는 데이터베이스 백업과 (이에 포함된) 로그, 혹시 남아 있다면 장애 시점의 로그까지 활용하여 복구를 하게 되는데, (아주 간략하게 설명하면) 데이터베이스 백업은 데이터베이스 파일을 복사한 것이므로 이를 새로 복사해둔 후 데이터베이스를 재시작한다고 생각하면, 미디어 복구 문제는 위에서 설명한 장애 발생 이후에 재시작 복구 작업과 결국 같은 문제가 된다. 결국 로그를 읽어서 퍼지하게 복사했던 데이터베이스 이미지에서 아직 미처 반영되지 못한 커밋했던 트랜잭션들을 다시 REDO해 주고, 결국 커밋 레코드가 포함되지 않은 트랜잭션들은 UNDO해 주면 된다. 이러한 미디어 복구 시점의 재시작 복구를 특별히 roll-forward 복구라고 부르기도 한다.

미디어 장애가 발생했을 때 마지막 데이터베이스 백업 이후의 모든 로그가 남아 있다면 장애 시점까지 손실 없이 데이터베이스를 복원할 수 있다. 불행히도 백업 이후의 일부 로그가 유실되었다면 최소한 백업 시점의 일관성이 유지되는 데이터베이스 시점까지는 복원이 가능하다. 미디어 복구를 이용하여 특정 시점으로 데이터베이스를 복원하는 것도 가능한데, 이는 roll-forward 과정을 현재 시점까지 전체를 수행하는 것이 아니라 DBA가 원하는 특정 시점까지만 수행하면 된다.

커밋을 하면 어떤 일이 일어나나?

커밋을 하면 어떤 일이?

커밋 트리거 혹은 지연된(deferred) 트리거가 정의되어 있다면 해당 트리거가 수행된다. 또한, 트랜잭션 수행 도중에 생성했던 커서(질의 결과 집합)를 정리하게 된다. SQL 표준은 커서를 선언할 때 트랜잭션 커밋 이후에도 커서를 계속 유지하고 결과를 볼 수 있도록 하는 Holdable Cursor(Cursor (databases), 2013)로 선언한 커서만을 유지하고 그 외의 커서는 모두 해제할 것으로 정의하고 있는데, 반면 JDBC에서는 기본으로 Holdable Cursor로 정의하고 있다. 이는 JDBC의 기본 동작이 자동 커밋이기 때문에 사용자의 편의성을 고려한 결정인데, 바깥쪽(outer) 커서에서 얻어온 결과를 기반으로 중첩 루프의 안쪽(inner)에서 다른 질의를 하는 경우에 전체를 묶어서 트랜잭션 처리를 하지 않으면 안쪽 질의가 커밋되는 순간에 바깥쪽 커서마저 닫히게 되는 상황이 발생된다. Holdable Cursor가 지원되면 안쪽에서 커밋을 하더라도 바깥쪽 커서가 계속 유지되기 때문에 트랜잭션 처리를 하지 않아도 된다.

DBMS는 트랜잭션을 수행하는 과정 중의 일부 내부 연산들(예를 들어, 리소스 반환과 같은 물리적인 연산)을 커밋이나 롤백과 같은 트랜잭션 종료 시점까지 지연시키는 경우가 있는데, 이런 연기(postpone)된 연산들이 포함되어 있었다면 커밋 시점에 수행된다. 장애 대비를 위하여 데이터베이스의 복제(replication)를 적용하고 있다면, 복제를 위한 로그를 쓰는 것과 같은 작업을 수행한다. 복제는 DBMS마다 구현 전략이 상이하기 때문에 일반적으로 설명하기는 어려운 측면이 있는데, CUBRID는 트랜잭션 로그를 기반으로 하는 복제를 사용하며 트랜잭션 커밋 시점에 복제를 위한 로그를 쓴다. 트랜잭션을 수행하는 과정 중에 획득한 모든 락(lock)을 해제하고, 트랜잭션이 최종적으로 커밋했다는 로그를 쓴 후에 마지막으로 트랜잭션이 가지고 있던 메모리, 트랜잭션 식별자 등과 같은 리소스들을 반환하고 비로소 트랜잭션이 종료하게 된다.

커밋을 하다가 오류가 발생되면?

응용 프로그램의 커밋 요청으로 위에서 설명한 단계들을 수행하는 과정 중에 오류가 발생되면 어떻게 될까? "끝나기 전까지는 끝난 것이 아니다."라는 말이 가장 적절한 설명이 될 것 같다. 트랜잭션이 커밋이 완료된 것이 아니라면 그것은 수행되지 않은 것과 같게 취급된다. <그림 7>에서 사용자의 커밋 요청이 오면 일단 해당 트랜잭션의 상태는 'partially committed' 상태가 된다. 문제 없이 커밋할 수 있으면 'committed' 상태로 완료되지만, 그렇지 않은 경우에는 다른 오류 발생과 마찬가지로 'failed' 상태를 거쳐 결국 'aborted' 상태에 다다르게 된다.

9bfdf8875b6a11f4c3274e0677d46596.png

그림 7 트랜잭션 상태 다이어그램(Silberschatz 외, 2010)

 

마치며

지금까지 DBMS가 어떻게 트랜잭션을 관리하는가라는 주제로 트랜잭션 관리의 주요 원리들을 간략하게 살펴보았다. DBMS의 트랜잭션 관리는 워낙 방대하고 깊숙한 주제라 본 글에서는 주요 개념들을 개괄적으로 다루었는데, 이 부분에 대해서 좀 더 관심이 있으면 참고 문헌에 수록된 자료들을 살펴보면 된다. 데이터베이스 시스템 전반에 대한 이해가 필요하다면 "Database System Concepts"(Silberschatz 외, 2010)를, 트랜잭션 복구와 관련하여 좀 더 궁금하면 "Recovery Mechanisms in Database Systems"(Kumar & Hsu, 1998)를, 트랜잭션 처리 전반에 대해 구현 수준까지 깊게 이해하고 싶다면 "Transaction Processing: Concepts and Techniques"(Gray & Reuter, 1993)를 추천한다.

참고 자료

[1] "Cursor (databases)." (2013). http://en.wikipedia.org/wiki/Cursor_(databases)#.22WITH_HOLD.22에서 검색됨

[2] "fsync(2) - Linux man page." (2013). http://linux.die.net/man/2/fsync에서 검색됨

[3] "InnoDB Startup Options and System Variables." (2013). http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit에서 검색됨

[4] Gray, J., & Reuter, A. (1993). "Transaction Processing: Concepts and Techniques." Morgan Kaufmann Publishers.

[5] Gray, J., Mcjones, P., Blasgen, M., Lindsay, B., Lorie, R., Price, T. 외. (June, 1981). The Recovery Manager of the System R Database Manager. "Computing Surveys, Vol. 13, No.2", 228.

[6] Haerder, T., & Reuter, A. (December, 1983). Principles of Transaction-Oriented Database Recovery. "ACM Computing Survey, Vol. 15, No. 4".

[7] Helland, P., Sammer, H., Lyon, J., Carr, R., Garrett, P., & Reuter, A. (1988). "Group Commit Timers and High-Volume Transaction Systems." Tandem Technical Report 88.1.

[8] Kumar, V., & Hsu, M. (1998). "Recovery Mechanisms in Database Systems." Prentice Hall PTR.

[9] Mohan, C., Haderle, D., Lindsay, B., Pirahesh, H., & Schwarz, P. (March, 1992). ARIES: A Transaction Recovery Method Supporting Fine-Granularity Locking and Partial Rollbacks Using Write-Ahead Logging. "ACM Transactions on Database Systems, Vol. 17", 94-162.

[10] nilavalagan. (2009). "Shadow Paging Recovery Technique." https://www.classle.net/book/shadow-paging-recovery-technique에서 검색됨

[11] SilberschatzAbraham, KorthF.Henry, & SudarshanS. (2010). "Database System Concepts." McGraw-Hill.

'RDBMS (PostgreSQL)' 카테고리의 다른 글

[DB/분산] 초보자를 위한 CAP 이론  (1) 2016.04.29
JDBC 트랜잭션  (0) 2015.07.30
PostgreSQL 조인  (0) 2015.05.13
Efficient Use of PostgreSQL Indexes  (0) 2015.05.13
PostgreSQL 날짜&시간 사용하기  (0) 2015.05.13

http://roundhere.tistory.com/73 펌 


안드로이드 서비스(Services)


안드로이드 서비스는 U.I 없이 백그라운드에서 실행되는 기능을 말합니다.

예를들어 네트워크 통신,음악재생,I/O 작업등의 작업등이 해당된다.


* 서비스 타입

Started 타입

이 서비스는 startService() 호출하면 시작됩니다.

한번 시작되면 백그라운드에서 무기한으로 실행됩니다. 보통은 작업이 완료되면 스스로 종료됩니다.

예를들어 파일 다운로드, 음악재생 등이 있습니다.

Bound 타입

bindService() 호출후에 시작됩니다. 클라이언트와 서버 와 같이 동작합니다.

액티비티는 서비스에게 어떤 요청을 하고 서비스는 결과값을 반환합니다.

Bound 서비스는 여러 액티비티와 연결될 수 있습니다.


* 주의할점

서비스는 메인 스레드에서 실행됩니다. (서비스는 쓰레드가 아님, 별도의 프로세스도 아님)  

서비스가 CPU 자원을 많이 소모하는 작업이라면 서비스안에 스레드를 생성해서 작업하는게 좋습니다.

앱이 실행중일때만 필요한 기능이라면 스레드를 사용하는게 맞고 

앱이 실행중이지 않을때 실행되어야 한다면 서비스를 이용해야 한다.




안드로이드 어플은 액티비티, 서비스, 리시버들이 서로 엮이며 돌아가게 설계되는 경우가 보통이다.  액티비티는 보통 하나의 화면에 대응되어 사용자가 어플과 인터액션하고 서비스나 리시버 같은 다른 컴포넌트를 시작/중지시키는 그런 일을 한다.  "서비스는 UI에서 인터액션을 하면서 동시에 백그라운드로 음악듣기와 같은 것을 구현할 때 유용한 메커니즘이다" 라고 한다.  이 말은 보통의 스레드 활용을 연상시킨다.  그러면 안드로이드 서비스는 스레드와 어떤 관계인가?   


안드로이드에서 View를 포함하는 UI 오브젝트들은 자신을 콘트롤하는 스레드가 하나이어야 하기 때문에 안드로이드는 UI 오브젝트들이 UI 스레드에서만 돌게 만들었다.  그래서 UI스레드가 아닌 스레드들이 UI 오브젝트를 콘트롤하려면 Handler, Activity.runOnUIThread(Runnable), View.post(Runnable)나 AsyncTask를 써야 한다 (참고-1, 참고-2).  이런 스레드 활용 방법은 JAVA와 같은 개념이기에 심플하다.  


안드로이드 서비스에서 액티비티의 UI 오브젝트에 접근하려면 통상 그 서비스에서 intent를 날리고, 액티비티내에는 BroadcasterReceiver 인스턴스 객체를 임베드하고 onReceive() implementation에서 UI 오브젝트를 접근하는 형태를 이용한다.  이 구조는 스레드 만들면서 Runnable job을 던져주고, 적당한 때 스레드 스타트 하는 것보다 더 비동기적이다.  


서비스는 별도의 스레드인가?  아니다.  여기 첫머리에 분명 "서비스는 스레드가 아니다" 라 한다.  그럼 서비스는 무엇이냐?  존재 이유는?  위에서 말하기를, 


- 어플리케이션이 시스템에게 "여기 백그라운드에서 수행해야 할 일이 있거든" 알려주기 위해서란다.  그래서 manifest에 나타낸다.  그런데 서비스가 단순 백그라운드 작업 처리용도라면 통상적인 별도 스레드 만들어서 녀석에게 필요한 백그라운드 일을 던져 주면 되잖아.  뭐가 다르지?  아, 안드로이드 서비스는 별도의 라이프사이클이 있지.  Context.startService로 시작한 서비스는 액티비티가 죽던 살던 어찌 되던지 한번 돌기 시작하면 stopSelf()나 Context.stopService로 중지시키기 전까지는 여간해서는 계속 돈다.  반면 Context.bindService로 구동/바인딩 된 서비스는 바인딩된 서비스 클라이언트가 더 이상 존재하지 않으면 시스템이 서비스를 없앨수 있다 한다.  서비스도 언제든 죽을 수 있다 하지만, 일단 돌기 시작한 서비스는 stopService() 같은 것으로 명확히 중지시키기 전까지는 (상대적으로) 계속 돈다고 생각할 수 있나?  


서비스가 부모 어플리케이션이나 프로세스 상태와 무관하게 유지되는 백그라운드 작업처리 장치임에 비해 안드로이드 스레드는 자바 경우와 같이 부모 프로세스가 살아있는 동안에 runnable, running과 blocked 상태를 뱅글뱅글 거쳐 일을 마치면 dead 상태에 들어가는 단순한 흐름을 갖고 있다.  


- A facility for an application to expose some of its functionality to other applications.  (서비스를 갖고 있는) 어플리케이션이 다른 어플리케이션에게 자신의 기능을 공표할 수 있는 기능.  아니 이게 무슨 말인감?  클래스들의 모든 public 메소드가 다른 클래스에게 "나 이런 것 할 수 있으니 필요하면 불러 써" 하는 것 아닌감?  다시 잘 보자...  음, 어플리케이션간에 알려주는 관계라...  AIDL 서비스 얘기같다.


이곳에서 안드로이드가 멀티태스킹 지원을 어떻게 하려고 하는 지 읽어보니 안드로이드의 서비스, 스레드, 백그라운드 작업, 프로세스, 어플리케이션간의 관계에 대해 좀 더 이해가 깊어진다.  음. 안드로이드에서 일단 실행된 어플리케이션은 사용자가 죽이더라도 완전히 퇴출시키지 않도록 하고, 그러면서 (어플리케이션 바꾸기) 할 때 스왑공간이 부족하니 어플리케이션이 사용하는 메모리를 타이트하게 관리하도록 했다고.  이처럼 상충되는 요구조건은 사용자에게 보다 seamless한 UX를 제공하기 위해서란다.   


복잡하게 생각하지 말고 대충 생각해야 겠다.  


1. UI 스레드에서 시간이 요하는 태스크 처리가 필요하고, 계속 현재 UI 스레드가 foreground에서 놀고 있을 가능성이 많으면 간단히 별도 스레드를 만들거나 AsyncTask로 처리한다.  


2. 만약 부모 스레드가 더 이상 foreground가 아닐때에나, 그 스레드를 소유한 어플이 중지되었거나 관계없이 백그라운드에서 서비스가 계속 살아있으면서 일을 해야 하면 서비스로 구현한다.  그리고는 서비스내에서 스레드를 만들어 서비스가 수행해야 하는 작업을 스레드가 담당하도록 한다.  작업량이 많거나 작거나 관계없이 별도 스레드에서 하도록 한다.  안전하게...   만약 어플리케이션의 UI 스레드가 돌고 있다고 생각되면 Handler 클래스를 이용해도 되고, 잘 모르겠으면 BroadcastReceiver 클래스를 이용하여 어플리케이션 UI를 관장하는 스레드에게 접근한다.  BroadcastReceiver 클래스는 돌고 있지 않던 프로세스도 깨울 수 있다.    





'임베디드' 카테고리의 다른 글

UART 란 ?  (0) 2015.06.26
GPIO 란 ?  (0) 2015.06.26
임베디드 추천 블로그  (0) 2015.06.12

'임베디드' 카테고리의 다른 글

SPI 란 ?  (0) 2015.06.26
GPIO 란 ?  (0) 2015.06.26
임베디드 추천 블로그  (0) 2015.06.12



지퍄요 Gernel Purpose I/O 이야기. 
Embedded System에서 GPIO를 빼면 무슨 이야기가 되겠습니까. I/O는 Digital 회로와 외부 세계와의 통로이지요. Input/ Output이니까요. Digital회로는 혼자서도 잘 먹고 잘 살겠지만, 손발이 있어야겠죠. 그게 I/O라는 거에요. 
 
보통 MCU에는 본연의 임무를 하는 pin들이 있는데요, 예를 들면, Hardware적으로 정해져 있는 CS라든가, WE라든가 하는 정해진 일을 하는 pin이 있는가 하면, 어떤 특별한 임무를 갖지 않고, User가 원하는 대로 I/O로 사용할 수 있는 pin들이 있어요. - 그런데, 재미 있는 건 GPIO가 특별한 functionality도 가질 수 있는 속성이 있어요. 예를 들면, 어떤 pin은 어떤 chip에 대한 CS로 사용할 수도 있지만, GPIO로도 쓸 수 있다 라는 건데, 이런걸 Alternative Functionality라고 부른답니다. 어떤 pin은 이런 Alternative Functionality를 갖는가 하면, GPIO 전용으로만 쓰이는 pin들도 있어요. 꼭 MCU의 User Manual을 잘 읽어 보세요 -
 
이런걸 일컬어 GPIO라고 부르는데, GPIO라는 건 GP + I/O라는 말이죠. GPIO는 말 그대로 , IO니까, Input을 받거나, Output을 내놓거나 하는 데 쓰여용. 이런 GPIO는 MCU하나에 적으면 10개 많으면 200개까지 준비되어 있다 카더라~요. 막상 Design 하려는 System에 GPIO는 넉넉하게 준비되어야지, 그렇지 않으면 망연자실 할 수 있으니까 MCU를 선정할 때 조심하세요. ARM base의 MCU입장에서의 GPIO는 AMBA bus에 연결된 SOC Peripheral 이에요. APB bus에 연결된 Slave라고 보시면 되는 거죠. 그러니까, 이런 GPIO들도 Register를 가지고 있고, CPU입장에서는 Register를 통해서 control 가능하답니다. 
 
그 control이라는 게 I/O로 쓸 수 있다고 했으니까, 어쩔 때는 Input, 어쩔 때는 Output으로 써야 하니까, 이런 I/O는 Register를 통해서 Programmable하답니다. Input, Output으로 원하는 대로 사용 가능해야 하니까요. Register를 통해서 GPIO를 control해야 하는 것에는, 3가지가 있고요, 요 3가지가 모두 제대로 설정되어야 GPIO를 내 수족처럼 control할 수 있으니까 명심하세요.
 
1) Pin의 Mode를 정할 수 있어요.
   → GPIO로 쓸 꺼냐, Alternative Functionality로 쓸 꺼냐.
   → Alternative로 설정하게 되면 GPIO로는 못쓰니까, 여기서 게임 오바. 
   → 게임 오바 되면 Hardware적으로 정해진 임무를 수행하게 됩니다요. 
2) Pin의 상태 (Mode)를 활성화 하면서 Data Direction을 정할 수 있어요. 
   → 지금부터 Pin이 사용 가능하도록 Init 하겠다. 
   → Input이냐, Output이냐를 결정하겠다. 
3) 자~ 읽어 보자 라든가, 자~ 값을 써보자를 할 수 있겠습니다. 
 
요런 넘들이 모두 Register로 준비되어 있는 거죠. 게임오바인 Alternative Functionality는 제껴 두고요, 본격적으로 GPIO만 다뤄보도록 하시죠. 어차피 Alternative Functionality로 pin을 설정하게 되면 Hardware적으로 Path 자체가 해당 Alternative Function의 회로에 연결되므로, GPIO로서의 자격을 상실하게 되니까, 그닥 파고들 이유가 없지요. 
 
GPIO는 Hardware적으로 pin이 한 개 밖에 없는데, 이 pin을 Input과 Output 모두로 사용가능 하다는 것인데, 이런 GPIO는 Hardware적으로는 3 상태 버퍼 - Tristate Buffer -로 구현이 된답니다. 3상태 Buffer라는 건 1, 0, High impedance (Hi-Z) 요런 3가지 종류의 상태를 가질 수 있는 Buffer를 말해요. Tristate Buffer를 살펴 보면, Switch가 1일 때는 I값이 O에 그대로 전달되고요, Swtich가 0일 때는 I값이 O에 전달되지 않고 회로가 열려 버려서, 마치 회로가 Open 된 것처럼 되는 거죠. 보통 Tristate Buffer의 3상태가 Input/ Output/ High Impedance로 헷갈리는데, 그렇게 보시면 안되고, 1, 0 (Output), High impedance (Input)라고 보셔야 해요. 
 
  
 
 
이렇게 Open 된 것처럼 된 상태를 Hi-Impedance (Hi-Z)라고 읽고요, I (Input)와 O (Output)의 연결된 부분이 끊어져서 I와 O는 서로 연관성이 없어지게 되지요. I가 무신 값을 갖든 O와는 상관 없고요. O가 무신 값을 갖든 I에게도 영향을 미치지 못해요. 왜 Input으로 쓸 때는 High Impedance 상태 이어야 하는가를 파고 들어오는 분들을 위하여,더 쉽게 예를 든다면, Output으로 pin을 사용할 때는 S(witch)가 1이고요, 0이거나 1의 값을 갖겠죠. 이때 Input으로 pin을 만들어 놓고 읽으려고 보니까, 아까 Output으로 만들어 놓은 값이 읽힐 수 밖에 없어요. 그런다고 Output을 0으로 일부러 만들거나 해서 읽어 들이면 계속 0만 읽히겠죠. 우연찮게 Input이 0이면 다행인데, 외부 input이 1이라도 되는 날에는 0과 1이 서로 맞지 않아서 문제가 발생하고요. 그러면 내가 원하는 값을 읽지 못하니까, S(witch)를 0으로 만들어 놓고, I와 O를 isolation시키는 거죠. 결국 High impedance란 반대편의 Input을 그대로 받아들이는 상태라고 보시면 됩니다. 이 pin에 물려 있는 다른 편의 pin에 값이 1이면 이쪽도 1, 0이면 이쪽도 0 이런 식의 상태죠. 
 
자, 이런 상태의 Buffer를 GPIO에 이용한다는 것은 S를 0으로 두었을 때 High Impedance 상태로 놓고, Read pin으로 이용할 수 있고요. S를 1로 두고서, 원하는 값을 O로 출력 가능하게 할 수 있겠죠. 이런 I, S, O를 차례로, GPIO_OUT, GPIO_OE, O는 GPIO PAD 출력 값이라고 하고, 자, input 부분까지 합쳐서 그리면, 이런 식이 되겠네요. 
 
GPIO_OUT은 output 값 자체
GPIO_OE는 Output enable로서, S(witch)
GPIO PAD는 직접 MCU외부로 삐죽 나와 있는 pin
 
 
 
 
으흐흐. 참 쉽죠~ PAD나 Pin이나 같은 말입니다. pin으로 나와 있는 녀석은 pin, Ball type으로 된 녀석들은 pad 뭐 그런 거에요. 
 
그러면, Spec에는 어떤 식으로 묘사가 될까요? 실제Pin number와 GPIO번호 그리고 Alternative Functionality가 나와 있네요. 그러니까, Default로는 GPIO로 사용되지만, Alternative Signal로 설정하면, GPIO로서가 아니고 PSC_SYNC0, PSC0_D1등의 Serial 통신의 Sync와 Data pin으로 사용 된다는 의미 이지요.
 
 
이런 GPIO는 Pull up과 Pull down도 설정을 통해 걸어줄 수 있는데요, 기본적으로 PU, PD가 모두 지원되는 pin도 있고 PU만, PD만 뭐 이런 식의 Hardware적인 기본 값들이 있는 녀석들도 있어요. 그러니까, 편리한 대로 Default PU/PD를 걸어줄 수도 있죠. 자세히 보기에는 긴~ 이야기가 있는데, GPIO에 Interrupt도 걸 수 있답니다. GPIO의 rising edge일 때, 또는 falling edge일 때 interrupt를 걸어서, 그런 일이 벌어질 때, 뭔가를 처리할 수도 있답니다. 와하하. 
 
 Qualcomm Solution중에 보면 BIO_TRISTATE()라는 Macro하고 BIO_OUT(), BIO_IN() 라는 MACRO가 있어요. BIO_TRISTATE가 Input/ Output으로 사용할 꺼다 라는 거고, BIO_OUT()이 output으로 사용했을 때, 출력 값. BIO_IN()이 input으로 사용했을 때, pin의 상태 값을 읽어 들이는 MACRO 랍니다. 
 
 Output으로 사용할 때, 1, 0으로 값을 내보낼 때는 Output Buffer에 1이나 0을 쓰고 나서 Output 설정을 하거나 (Output 설정하는 순간 값이 Pin에 나옴) Output 설정을 하고 Output Buffer에 1이나 0을 써도 상관없지만 (1이나 0을 쓰는 순간 값이 Pin에 나옴) Read로 사용할 때는 읽어 들인 후 Input 설정을 하게 되면 Read값은 당연히 정상적인 값이 아니겠죠. 머 당연한 이야기지만 실수 하는 경우도 있더라구요. 그냥 그렇다구요~


'임베디드' 카테고리의 다른 글

SPI 란 ?  (0) 2015.06.26
UART 란 ?  (0) 2015.06.26
임베디드 추천 블로그  (0) 2015.06.12


관리자가 회원관리 ( 가입 ) 을 직접하는것이 아니라,  사용자 스스로 인증을 하기위해 사용합니다.

이메일 기반 인증 시스템을 만들기위해서 2단계를 거치게된다.


첫째,  인증시스템 변경



        * 사이트관리 - 플러그인 - 인증 - 인증관리로  갑니다.



       * 이메일 기반 인증에 관리모드 활성 눈모양을 클릭해서 켜줌. 




     * 위와 같이 이메일 기반 인증으로 콤보박스를 바꾸어줍니다.





둘째,  SMTP 서버 설정 




싸이트관리 - 플러그인-메세지출력-이메일 로 갑니다.




       위의 빈칸을 채웁니다. 

       저같은 경우는 대구대학교 무들 시스템을 만들었기때문에 대구대학교의  SMTP  서버를 이용하였습니다.

       서버관리자분에게  SMTP 호스트와 SMTP 암호를 얻고  발신전용 이메일 주소를 만들어달라고해서 채웁니다.

     SMTP 서버를 직접 설치하여 사용할수도 있겠습니다.


   

    설정을 마치고 나면 아래와 같이 싸이트에 새 계정 만들기가 로그인페이지에 나타납니다.


 위와 같이 새 계정 만들기 버튼을 클릭하면 




다음과 같은 페이지가 뜨고 , 해당 이메일로 확인 메일이 날라가게된다. 



+ Recent posts