카테고리 없음

2.6커널의 타이머와 목록

shellbt 2011. 5. 19. 17:59

이 기사는 "커널 API, Part 2: 지연 가능 함수, 커널 태스크릿 및 작업 큐"(developerWorks, 2010년 3월)에서 시작한 작업 지연이라는 주제를 계속 다룬다. 이번에는 타이머 API(Application Programming Interface)와 모든 작업 지연 스키마의 핵심 요소인 커널 목록 구문에 대해 설명한다. 또한 타이머 및 기타 작업 지연 메커니즘(예: 작업 큐)에서 사용하는 커널 목록 API에 대해서도 살펴본다.

타이머는 모든 운영 체제의 필수 부분이며 여러 타이머 메커니즘이 사용되고 있다. 먼저 Linux 타이머 스키마의 개요를 간단히 살펴본 후 작동 방법을 자세히 살펴보자.

Linux 시간의 원천

Linux 커널에서는 jiffies라는 글로벌 변수를 이용해서 측정하며, 이 변수는 시스템 시동 이후의 틱 수를 식별한다. 틱의 수를 계산하는 방법은 최저 레벨인 실행 중인 특정 하드웨어 플랫폼에 따라 달라지지만 일반적으로 틱은 인터럽트를 통해 증가된다. 틱 속도(jiffies의 최하위 비트)는 구성 가능하지만 최근 x86용 2.6 커널의 경우에는 1틱이 4ms이다(250Hz). jiffies 글로벌 변수는 커널 내에서 다양한 용도로 널리 사용되고 있으며, 대표적으로 타이머의 제한시간 값을 계산하기 위한 현재 절대 시간에 사용된다. (나중에 이에 대한 예제를 설명한다.)

커널 타이머

최근 2.6 커널의 타이머에는 몇 가지 다양한 스키마가 있다. 타이머 API가 대부분의 경우에 적합하기는 하지만 모든 타이머 중에서 가장 단순하고 정확성이 낮다. 이 API를 사용하면 jiffies 도메인(최소 4ms 제한시간)에서 작동하는 타이머를 생성할 수 있다. 또한 시간이 나노초 단위로 정의되는 타이머를 생성할 수 있는 고분해능 타이머(high-resolution timer) API도 있다. 프로세서 및 그 작동 속도에 따라 성능의 차이가 있을 수 있지만 이 API를 사용하면 제한시간을 jiffies 틱 간격 이하로 스케줄할 수 있다.

표준 타이머

표준 타이머 API는 Linux 커널의 초기 버전부터 오랫동안 Linux 커널에 포함되어 있었다. 고분해능 타이머(high-resolution timer)에 비해 정확도가 떨어지기는 하지만 실제 장치를 처리할 때 발생하는 오류 케이스를 처리하는 데 사용되는 일반적인 드라이버 제한시간에 이상적이다. 이러한 제한시간은 실제로 초과되는 경우가 많지 않으며 대부부분의 경우에는 시작되었다가 제거되는 행태를 보인다.

단순 커널 타이머는 타이머 휠을 사용하여 구현된다. 이 아이디어는 1997년 Finn Arne Gangstad에 의해 처음 소개되었다. 이 타이머는 많은 수의 타이머를 관리하는 데 문제점이 있지만 이를 무시하고 전형적인 경우인 합리적인 수의 타이머를 관리할 경우 좋은 성능을 제공한다. (원래 타이머 구현에서는 단순히 만기 순서에 양방향으로 연결된 상태로 타이머를 유지한다. 개념적으로 단순하지만 이 방법은 확장할 수 없다.) 타이머 휠은 버켓의 콜렉션이며, 각 버켓은 타이머 만기를 위한 미래의 시간을 나타낸다. 버켓은 5개의 버켓을 기반으로 하는 로그 시간(logarithmic time)을 사용하여 정의된다. jiffies를 시간 단위로 사용하여 미래의 만기 기간을 나타내는 많은 그룹이 정의된다. (각 그룹은 타이머 목록으로 표시된다). 타이머는 복잡도가 O(1)이고 O(N) 시간 이내에 만기가 발생하는 목록 작업을 사용하여 삽입되며, 타이머 만기는 계단식으로 발생한다. 즉, 타이머의 만기 시간이 줄어들면 타이머가 정밀도가 높은 버켓에서 제거된 다음 정밀도가 낮은 버컷에 삽입된다. 이제 이 타이머 구현을 위한 API를 살펴보자.

타이머 API

Linux에는 타이머의 생성과 관리를 위한 간단한 API가 있다. 이 API는 타이머 작성, 취소 및 관리를 위한 함수(및 헬퍼 함수)로 구성되어 있다.

타이머는 timer_list 구조체로 정의되며, 이 구조체에는 타이머 구현에 필요한 모든 데이터(컴파일 시 구성되는 선택적 타이머 통계 및 목록 포인터 포함). 사용자의 관점에서 볼 경우, timer_list에는 만기 시간, 콜백 함수(타이머의 만기 시간/여부) 및 사용자 제공 컨텍스트가 있다. 그런 다음 사용자는 타이머를 초기화해야 한다. 초기화는 몇 가지 방법으로 수행할 수 있다. 가장 간단한 방법은 setup_timer를 호출하는 것이다. 이 함수는 타이머를 초기화하고 사용자 제공 콜백 함수 및 컨텍스트를 설정한다. 이 함수를 사용하지 않을 경우에는 사용자가 타이머에서 이러한 값(함수 및 데이터)을 설정하고 init_timer를 호출할 수 있다. init_timer setup_timer에서 내부적으로 호출된다.

void init_timer( struct timer_list *timer );
void setup_timer( struct timer_list *timer, 
                     void (*function)(unsigned long), unsigned long data );

타이머를 초기화한 후에는 사용자가 mod_timer를 호출하여 만기 시간을 설정해야 한다. 일반적으로 만기 시간은 미래이므로 여기에서는 jiffies를 추가하여 현재 시간을 기준으로 하는 오프셋을 설정한다. 사용자는 del_timer를 호출하여 타이머를 삭제할 수도 있다(만기되지 않은 경우).

int mod_timer( struct timer_list *timer, unsigned long expires );
void del_timer( struct timer_list *timer );

마지막으로 사용자는 timer_pending을 호출하여 타이머가 아직 만기되지 않고 보류 중인지 여부를 확인할 수 있다(타이머가 보류 중이면 1이 리턴됨).

int timer_pending( const struct timer_list *timer );

타이머 예제

이러한 API 함수 중 일부를 실제로 살펴보자. Listing 1에서는 단순 타이머 API의 핵심 부분을 보여 주는 단순 커널 모듈을 제공한다.init_module에서 setup_timer를 사용하여 타이머를 초기화한 다음 mod_timer를 호출하여 타이머를 시작한다. 타이머가 만료되면 콜백 함수(my_timer_callback)가 호출된다. 마지막으로 모듈을 제거하면 del_timer를 통해 타이머가 삭제된다. (del_timer의 리턴값을 검사하여 타이머가 사용 중인지 여부를 식별한다.)


Listing 1. 단순 타이머 API 탐색하기
	
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>

MODULE_LICENSE( "GPL" );

static struct timer_list my_timer;

void my_timer_callback( unsigned long data )
{
  printk( "my_timer_callback called (%ld).\n", jiffies );
}

int init_module( void )
{
  int ret;

  printk("Timer module installing\n");

  // my_timer.function, my_timer.data
  setup_timer( &my_timer, my_timer_callback, 0 );

  printk( "Starting timer to fire in 200ms (%ld)\n", jiffies );
  ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(200) );
  if (ret) printk("Error in mod_timer\n");

  return 0;
}

void cleanup_module( void )
{
  int ret;

  ret = del_timer( &my_timer );
  if (ret) printk("The timer is still in use...\n");

  printk("Timer module uninstalling\n");

  return;
}

./include/linux/timer.h에서 타이머 API에 대한 자세한 정보를 볼 수 있다. 단순 타이머 API는 쉽고 효율적이지만 실시간 애플리케이션에 필요한 정확도를 제공하지 않는다. 따라서 최근에 Linux에 추가되어 더 높은 분해능 타이머를 지원하는 기능을 살펴보자.

고분해능 타이머(High-resolution timers)

고분해능 타이머(또는 hrtimer)는 앞에서 설명한 타이머 프레임워크와는 독립된 고정밀 타이머 관리 프레임워크를 제공한다. 이는 두 프레임워크를 병합하기가 복잡하기 때문이다. 타이머는 jiffies 단위로 작동하는 반면 hrtimer는 나노초 단위로 작동한다.

hrtimer 프레임워크는 일반적인 타이머 API와 다르게 구현된다. 버켓 및 타이머 캐스케이딩 대신 hrtimer는 타이머로 구성된 시간순 데이터 구조를 관리한다. (활성 시간에 처리 시간을 최소화하기 위해 타이머가 시간순으로 삽입된다.) 이 프레임워크에 사용되는 데이터 구조는 레드-블랙 트리이다. 이 트리는 성능에 중점을 둔 애플리케이션에 이상적이며 일반적으로 커널 내의 라이브러리로 사용할 수 있다.

hrtimer 프레임워크는 커널 내에서 API로 사용할 수 있으며 nanosleep, itimers 및 POSIX(Portable Operating System Interface) 타이머 인터페이스를 통해 사용자 공간 애플리케이션에서도 사용된다. 이 프레임워크는 2.6.21 커널에 포함되었다.

고분해능 타이머 API

hrtimer API에는 기존 API와 유사한 점도 있지만 추가적인 시간 제어를 위한 몇 가지 근본적인 차이점도 있다. 첫 번째 차이점은 시간이jiffies가 아닌 ktime이라는 특수 데이터 유형으로 표시된다는 것이다. 이 표시는 이 단위에서의 효율적인 시간 관리와 관련된 일부 세부 사항을 숨긴다. 이 API는 절대 시간과 상대 시간을 구별하므로 호출자가 유형을 지정해야 한다.

기존 타이머 API와 마찬가지로 hrtimer도 구조체로 표현된다. 이 구조체는 사용자 관점(콜백 함수, 만기 시간 등)에서 타이머를 정의하고 관리 정보를 통합한다. (이 경우에는 타이머가 레드-블랙 트리, 선택적 통계 등에 존재한다.)

먼저 hrtimer_init을 통해 타이머가 초기화된다. 이 호출은 타이머, 클록 정의 및 타이머 모드(일회성 또는 다시 시작)를 포함한다. 사용할 클록은 ./include/linux/time.h에 정의되어 있으며 시스템에서 지원하는 다양한 클록을 나타낸다(예: 실시간 클록 또는 시스템 시동과 같은 시작 시점의 시간을 나타내는 모노 클록). 초기화된 타이머는 hrtimer_start를 사용하여 시작할 수 있다. 이 호출은 만기 시간(ktime_t) 및 시간 모드 값(절대값 또는 상대값)을 포함한다.

void hrtimer_init( struct hrtimer *time, clockid_t which_clock, 
			enum hrtimer_mode mode );
int hrtimer_start(struct hrtimer *timer, ktime_t time, const 
			enum hrtimer_mode mode);

hrtimer를 시작한 후에는 hrtimer_cancel 또는 hrtimer_try_to_cancel을 호출하여 취소할 수 있다. 각 함수에는 중지할 타이머에 대한 hrtimer 참조가 들어 있다. 두 함수의 차이점은 다음과 같다. hrtimer_cancel 함수는 타이머를 취소하려고 시도한다. 하지만 타이머가 이미 시작되었으면 콜백 함수가 종료될 때까지 기다린다. 이에 반해 hrtimer_try_to_cancel 함수는 타이머를 취소하려고 시도할 때 타이머가 이미 시작되었으면 오류를 리턴한다.

int hrtimer_cancel(struct hrtimer *timer);
int hrtimer_try_to_cancel(struct hrtimer *timer);

hrtimer_callback_running을 호출하여 hrtimer가 해당 콜백을 활성화했는지 여부를 확인할 수 있다. 이 함수는 타이머의 콜백 함수가 호출되었을 때 오류를 리턴하기 위해 hrtimer_try_to_cancel에 의해 내부적으로 호출된다.

int hrtimer_callback_running(struct hrtimer *timer);

ktime API

이 기사에서는 ktime API를 설명하지 않았다. 이 API는 고분해능에서 시간을 관리할 수 있는 다양한 함수를 제공한다. ./linux/include/ktime.h에서 ktime API를 볼 수 있다.

hrtimer 예제

hrtimer API의 사용법은 Listing 2에서 보듯이 매우 간단하다.init_module 내에서 상대적인 제한시간(이 경우에는 200ms)을 정의하여 시작할 수 있다. hrtimer_init 호출을 사용하여 hrtimer를 초기화한(모노 클록 사용) 다음 콜백 함수를 설정한다. 마지막으로 앞에서 작성한ktime 값을 사용하여 타이머를 시작한다. 타이머가 시작되면my_hrtimer_callback 함수가 호출되면서 HRTIMER_NORESTART를 리턴한다. 따라서 타이머가 자동으로 다시 시작되지 않는다. cleanup_module 함수에서 hrtimer_cancel을 사용하여 타이머를 취소하면서 정리한다.


Listing 2. hrtimer API 탐색하기
	
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>

MODULE_LICENSE( "GPL" );

#define MS_TO_NS(x)	(x * 1E6L)

static struct hrtimer hr_timer;

enum hrtimer_restart my_hrtimer_callback( struct hrtimer *timer )
{
  printk( "my_hrtimer_callback called (%ld).\n", jiffies );

  return HRTIMER_NORESTART;
}

int init_module( void )
{
  ktime_t ktime;
  unsigned long delay_in_ms = 200L;

  printk("HR Timer module installing\n");

  ktime = ktime_set( 0, MS_TO_NS(delay_in_ms) );

  hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
  
  hr_timer.function = &my_hrtimer_callback;

  printk( "Starting timer to fire in %ldms (%ld)\n", delay_in_ms, jiffies );

  hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );

  return 0;
}

void cleanup_module( void )
{
  int ret;

  ret = hrtimer_cancel( &hr_timer );
  if (ret) printk("The timer was still in use...\n");

  printk("HR Timer module uninstalling\n");

  return;
}

hrtimer API에는 지금까지 살펴본 것보다 훨씬 더 많은 특성이 있다. 한 가지 흥미로운 특성은 콜백 함수의 실행 컨텍스트(예: softirq 또는 hardiirq 컨텍스트)를 정의할 수 있다는 것이다. ./include/linux/hrtimer.h의 포함 파일에서 hrtimer API에 대한 자세한 정보를 볼 수 있다.

커널 목록

앞에서 언급한 대로 목록은 유용한 구조이기 때문에 커널에서는 일반적인 용도로 사용할 수 있는 효율적인 구현을 제공한다. 또한 지금까지 살펴본 API에도 목록이 있다. 양방향 연결 목록 API를 이해하면 이 효율적인 데이터 구조를 사용하여 개발 작업을 수행하고 목록을 활용하는 커널의 코드를 이해하는 데 많은 도움이 된다. 이제 커널 목록 API를 간단하게 살펴보자.

이 API는 목록 헤드(앵커)뿐만 아니라 구조체 내 목록 포인터를 나타내는 데도 사용되는 list_head 구조체를 제공한다. 목록 기능이 포함된 샘플 구조체를 살펴보자(Listing 3 참조). 이 샘플 구조체에는 오브젝트 연결에 사용되는 list_head 구조체가 추가되어 있다. 그리고 일부 GCC 매직(./include/kernel/kernel.h에 정의된 list_entry  container_of)을 통해 구조체의 어디에서나 list_head 구조체를 추가할 수 있으며 목록 포인터에서 수퍼 오브젝트로 역참조할 수 있다.


Listing 3. 목록 참조를 사용하는 샘플 구조체
	
struct my_data_structure {
	int value;
	struct list_head list;
};

모든 목록 구현에서와 마찬가지로 목록에 대한 앵커 역할을 수행할 목록 헤드가 필요하다. 이 작업은 일반적으로 목록에 대한 선언 및 초기화를 제공하는 LIST_HEAD 매크로를 통해 수행된다. 이 매크로는 사용자가 오브젝트를 추가할 수 있는 list_head 오브젝트 구조체를 작성한다.

LIST_HEAD( new_list )

또한 LIST_HEAD_INIT 매크로를 사용하여 수동으로 목록 헤드를 작성할 수 있다(예를 들어, 목록 헤드가 다른 구조체에 있는 경우).

기본 초기화가 완료되면 list_add  list_del 함수를 포함한 여러 함수를 사용하여 목록을 조작할 수 있다. 이제 이 API의 사용법을 더 잘 보여 주는 예제 코드를 살펴보자.

목록 API 예제

Listing 4에서는 수많은 목록 API 함수를 탐색하는 간단한 커널 모듈을 제공한다(./include/linux/list.h에서 많은 함수를 찾아볼 수 있음). 이 예제에서는 두 개의 목록을 작성하고, init_module 함수를 이용하여 목록을 채운 다음 cleanup_module 함수에서 목록을 조작한다.

먼저 일부 데이터와 두 개의 목록 헤더를 포함하는 데이터 구조(my_data_struct)를 작성한다. 이 예제에서는 한 오브젝트를 여러 목록에 동시에 삽입할 수 있다는 것을 보여 준다. 그런 다음 두 개의 목록 헤드(my_full_list  my_odd_list)를 작성한다.

init_module 함수에서 10개의 데이터 오브젝트를 작성한 다음 list_add 함수를 사용하여 목록에 로드한다(my_full_list에 모든 오브젝트를 로드하고 my_odd_list에 모든 홀수 값 오브젝트를 로드함). list_add는 두 개의 인수를 사용한다. 첫 번째 인수는 사용할 오브젝트 내의 목록 참조이고 두 번째 인수는 목록 앵커이다. 이는 목록 참조가 있는 수퍼 오브젝트를 식별하는 커널의 내부 기능을 사용하여 데이터 오브젝트를 여러 목록에 로드할 수 있음을 보여 준다.

cleanup_module 함수는 목록 API의 몇 가지 추가 기능을 보여 준다. 첫 번째 기능은 목록 반복을 쉽게 수행할 수 있도록 지원하는list_for_each 매크로이다. 이 매크로를 사용하려면 현재 오브젝트에 대한 참조(pos)와 반복할 목록 참조를 제공해야 한다. 각 반복마다list_head 참조가 리턴되며, 리턴된 참조를 list_entry에 제공하여 컨테이너 오브젝트(사용자의 데이터 구조)를 식별할 수 있다. 사용자의 구조와 구조 내의 목록 변수를 지정한다. 이 변수는 내부적으로 컨테이너를 역참조하는 데 사용된다.

홀수 목록을 표시하기 위해 또 하나의 반복 매크로인 list_for_each_entry를 사용한다. 이 매크로는 사용자의 데이터 구조를 자동으로 제공하므로 list_entry를 수행하지 않아도 되기 때문에 더 간단하다.

마지막으로 할당된 요소를 해제하기 위해 list_for_each_safe를 사용하여 목록을 반복한다. 이 매크로를 사용하면 목록 항목을 제거(반복 작업의 일부로서 수행할 작업)하지 않으면서 안전하게 목록을 반복할 수 있다. list_entry를 사용하여 데이터 오브젝트를 찾은(커널 풀에 다시 해제하기 위해) 다음 list_del을 사용하여 목록의 항목을 해제한다.


Listing 4. 목록 API 탐색하기
	
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>

MODULE_LICENSE( "GPL" );

struct my_data_struct {
  int value;
  struct list_head full_list;
  struct list_head odd_list;
};

LIST_HEAD( my_full_list );
LIST_HEAD( my_odd_list );


int init_module( void )
{
  int count;
  struct my_data_struct *obj;

  for (count = 1 ; count < 11 ; count++) {

    obj = (struct my_data_struct *)
            kmalloc( sizeof(struct my_data_struct), GFP_KERNEL );

    obj->value = count;

    list_add( &obj->full_list, &my_full_list );

    if (obj->value & 0x1) {
      list_add( &obj->odd_list, &my_odd_list );
    }

  }

  return 0;
}


void cleanup_module( void )
{
  struct list_head *pos, *q;
  struct my_data_struct *my_obj;

  printk("Emit full list\n");
  list_for_each( pos, &my_full_list ) {
    my_obj = list_entry( pos, struct my_data_struct, full_list );
    printk( "%d\n", my_obj->value );
  }

  printk("Emit odd list\n");
  list_for_each_entry( my_obj, &my_odd_list, odd_list ) {
    printk( "%d\n", my_obj->value );
  }

  printk("Cleaning up\n");
  list_for_each_safe( pos, q, &my_full_list ) {
    struct my_data_struct *tmp;
    tmp = list_entry( pos, struct my_data_struct, full_list );
    list_del( pos );
    kfree( tmp );
  }

  return;
}

헤드 대신 목록의 후미에 추가하고(list_add_tail), 목록을 결합하고(list_splice), 목록의 컨텐츠를 테스트하는(list_empty) 등의 기능을 제공하는 여러 다양한 함수가 있다. 참고자료에서 커널 목록 함수에 대한 자세한 정보를 확인할 수 있다.

추가 주제

이 기사에서는 필요에 따라 기능을 분리하는 기능(타이머 및 고정밀 hrtimer API) 뿐만 아니라 코드 재사용을 위해 코드를 일반화하는 기능(목록 API)을 보여 주는 몇 가지 API를 살펴보았다. 일반 타이머는 일반 드라이버 제한시간에 효율적인 메커니즘을 제공하는 반면 hrtimer는 정밀도 높은 타이머 기능을 위한 높은 서비스 품질을 제공한다. 목록 API는 매우 일반적이만 효율적이고 다양한 기능을 갖춘 인터페이스를 제공한다. 커널 코드를 작성하게 되면 이러한 세 API 중 하나 이상을 실행할 것이므로 분명히 살펴볼 만한 가치가 있다.


참고자료

교육

  • "커널 API, Part 2: 지연 가능 함수, 커널 태스크릿 및 작업 큐"(developerWorks, 2010년 3월)에서 다른 작업 지연 스키마(태스크릿 및 작업 큐)에 대해 알아보자. 

  • 캐스케이딩 타이머 휠에 대한 세부 사항을 포함한 일반 타이머 구현에 대한 자세한 정보를 보려면 Linux 커널 메일링 목록에 게시된 Ingo Molnar의 글을 읽어보자. 이 게시물에서는 타이머 API 및 그 원리뿐만 아니라 구현에 대해서도 자세히 설명한다. 

  • Thomas Gleixner는 Linux 커널 메일링 목록에 게시한 ktimers 패치에 대한 글을 통해 타이머 서브시스템과 타이머 및 고분해능 타이머의 분할에 대해 잘 설명하고 있다. 이 게시물에서는 상위 레벨 서브시스템에서 타이머를 사용하는 방법에 대해서도 살펴보고 있다. 

  • 커널 내에서 타이머를 위한 기본 기능을 제공하는 Linux 시간 시스템의 변경 사항을 자세히 살펴보려면 Hrtimers and Beyond: Transforming the Linux Time Subsystems(PDF)를 읽어보자. 이 논문에서는 전반적인 시간 서브시스템의 설계와 여러 타이머 API를 지원하는 데 필요한 변경 사항에 대해 자세히 설명한다. 

  • Linux 목록 API에서는 GCC 확장을 사용하여 목록 포인터 구조가 포함된 오브젝트의 컨테이너를 식별한다(typeof  offsetof사용). GCC 확장에 대해 알아보려면 "GCC hacks in the Linux kernel"(developerworks, 2008년 11월)을 읽어보자. 

  • KernelNewbies의 FAQ/LinkedLists에는 목록 및 일부 내부 기능이 잘 설명되어 있다. 

  • developerWorks Linux 영역에서 Linux 개발자 및 관리자를 위한 수백 편의 사용법 기사 및 튜토리얼과 더불어 다운로드, 토론 포럼 및 다양한 자원을 볼 수 있다. 

  • developerWorks 기술 행사 및 웹 캐스트를 통해 다양한 IBM 제품 및 IT 산업 주제에 대한 최신 정보를 얻을 수 있다. 

  • 무료 developerWorks Live! briefing을 통해 최신 IBM 제품 및 도구에 대한 정보뿐만 아니라 IT 업계의 최신 경향까지도 빠르게 확인할 수 있다. 

  • developerWorks on-demand demos에서는 입문자를 위한 제품 설치 및 설정부터 숙련된 개발자를 위한 고급 기능까지 망라된 다양한 데모를 제공한다. 

  • Twitter의 developerWorks 페이지를 살펴보자. 

제품 및 기술 얻기

  • 자신에게 가장 적합한 방법으로 IBM 제품을 평가해 보자. 시험판 제품을 다운로드하거나 온라인으로 제품을 사용해 보거나 클라우드 환경에서 제품을 사용하거나 SOA Sandbox에서 SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다. 

토론

  • developerWorks community에 참여하자. 개발자 중심 블로그, 포럼, 그룹 및 Wiki 검색 중에 다른 developerWorks 사용자와 의견을 교환해 보자.