GTK+ 메모리 관리

GTK+ 포럼에 GTK+ 메모리 관리하기라는 글이 올라왔는데 내용이 간결해서 이를 참고로 다시 정리해 보았습니다.

참조 카운터 (Reference Counting)

모든 GTK 객체는 GObject를 상속하고 있는데, GObject는 메모리 관리를 위해 참조 카운터 기능을 기본적으로 지원합니다. GObject가 새로 생성되면 참조 카운터는 1입니다. 이 참조 카운터는 g_object_ref() / g_object_unref() 함수를 이용해 증가시키거나 감소시킬 수 있습니다. 말 그대로 객체를 사용중이라면(참조하고 있다면) 참조 카운터를 증가시키면 되고, 더 이상 사용하지 않는다면(참조를 안한다면) 참조 카운터를 감소시키면 됩니다. 참조 카운터가 0이 되면 당연히 객체의 모든 리소스는 자동으로 해제됩니다. 하지만 언제나 그렇듯이 예외가 존재하는데 이런 경우 조심하지 않으면 그대로 메모리 누수가 발생하기 쉽상입니다.

첫번째 경우는 객체간에 결합할 때입니다. 가장 흔한 경우가 GtkTreeModel 인터페이스를 구현한 GtkTreeStore / GtkListStore 객체와 GtkTreeView / GtkComboBox 객체를 연결할 때입니다. 예를 들어 gtk_list_store_new() 함수로 만들어진 GtkListStore 객체의 참조 카운터는 1입니다. gtk_tree_view_set_model() 함수로 트리뷰 객체에 리스트 스토어 객체를 연결하면 참조 카운터는 2가 됩니다. 왜냐하면 트리뷰 객체가 리스트 스토어 객체를 참조하기 때문입니다. 많은 예제 프로그램에서 이 함수를 호출한 뒤 g_object_unref() 함수를 이용해 리스트 스토어 객체의 참조 카운터를 감소하는 이유는, 이후 리스트 스토어 객체에 대한 메모리 관리를 더 이상 프로그래머가 할 필요 없이, 트리뷰 객체가 없어질때 리스트 스트어 객체의 참조 카운터를 감소하면서 자동으로 리소스가 정리되도록 하기 위해서입니다.

객체 복사 (Object Copying)

두번째 경우는 객체 데이터를 저장하거나 가져올 때입니다. GtkTreeStore / GtkListStore 객체에서 gtk_tree_model_get() 함수로 데이터를 가져오거나 gtk_list_store_set() / gtk_tree_store_set() 등으로 저장할때, 즉 데이터가 복사될때는 데이터 타입이 GObject 기반이라면 참조 카운터가 증가됩니다. 이미지 데이터를 관리하는데 많이 사용하는 GdkPixbuf 객체도 그 중 하나입니다. 따라서 이러한 객체를 가져온 뒤 사용이 다 끝났다면 g_object_unref() 함수를 이용해 반드시 참조 카운터를 해제해야 합니다.  객체 속성(property)를 설정하거나 가져올때도 마찬가지로 객체 복사 규칙이 적용됩니다. 따라서 g_object_set() / g_object_get() 함수를 사용했을 때도 사용이 끝난 객체에 대한 참조 카운터를 감소해 주어야 합니다. 마찬가지로, GtkCellRenderer 객체의 속성도 동일한 규칙이 적용되므로 유의해야 합니다.

참고로, 객체 복사시 정수 / 실수 타입 등은 무관하지만 문자열은 항상 새로 할당된 메모리에 복사된 문자열이 전달되기 때문에 사용이 끝나면 g_free() 함수로 해제해야 합니다. GBoxed 타입은 참조 카운터가 없기 때문에 항상 새로 할당된 메모리에 복사된 자료 구조가 전달되므로 마지막에 해당 객체의 해제 함수로 리소스를 정리해야 합니다. (G_TYPE_DATE, G_TYPE_STRV, G_TYPE_GSTRING, …)

GInitiallyUnowned 상속 객체 (Descendants of GInitiallyUnowned)

GTK 객체 중에서 GObject를 직접 상속하지 않고, GtkObject 객체를 상속받는 객체들이 있습니다. GtkWidget / GtkAdjustment / GtkCellRenderer 등이 대표적이므로, GtkWidget을 상속하는 대부분의 위젯이 이러한 객체입니다. GtkObject 상속도를 보면 특이하게 GInitiallyUnowned 객체를 상속받는데 이 객체의 참조 카운터 동작 방식은 위에서 설명한 것과 조금 다르기 때문에 더 깊은 이해가 필요합니다.

GInitiallyUnowned 객체는 생성되면 초기에 참조 카운터가 0입니다. 대신 부동 참조(floating reference) 상태에 있게 됩니다. 누군가가 g_object_ref_sink() 함수를 호출하면 떠있는(floating) 참조가 참조 카운터로 변환되며 닻을 내리게(sink) 됩니다. 이후에는 일반적인 GObject 참조 카운터와 동일하게 동작합니다. 여기서 누군가는 대부분 객체를 자식(child)으로 갖는 부모(parent) 객체입니다. 즉, gtk_container_add() / gtk_box_pack_start() 등과 같은 함수를 이용하여 위젯을 결합하면 상위 위젯이 g_object_ref_sink() 함수를 호출합니다.

GTK+ 프로그래밍시 위젯을 만들고 상위 위젯에 넣는 작업은 매우 빈번한데 만일 이 과정에 생성하는 모든 위젯 객체 리소스를 프로그래머가 관리해야 한다면 끔찍해질 겁니다. 부동 참조(floating reference) 개념은 이러한 수고를 덜어주는데 유용합니다. 모든 위젯은 생성 후 상위 위젯에 추가되어도 참조 카운터는 1밖에 안되고, gtk_widget_destroy() 등을 이용하여 최상위 위젯을 없애면 모든 하위 위젯 객체는 자동으로 참조 카운터가 0이 되어 메모리가 해제됩니다.

참고로, 어떤  위젯을 부모 위젯에서 떼어낸 뒤 다른 부모 위젯에 넣기 위해서는 제일 먼저 해당 위젯의 참조 카운터를 증가시켜야 합니다. 왜냐하면 부모 위젯에서 떼어낼 때도 참조 카운터가 자동으로 감소하기 때문에, 떼어내는 순간 객체가 사라지기 때문입니다. 부모 위젯이 더 이상 자식 위젯을 참조 하지 않기 때문에 떼어내는 순간 자식 위젯의 참조 카운터가 감소됩니다.

문자열 / 문자열 배열 / 리스트 (Strings, String Arrays and Lists)

GLib 라이브러리는 매우 많은 문자열 관련 API를 제공합니다. 또한 특정 객체에서 어떤 결과나 내부 자료를 얻어올때는 문자열 배열이나 리스트(GList) 등도 많이 사용합니다. 이 경우 프로그래머는 반드시 자신이 사용하는 API 문서를 꼼꼼하게 잘 읽어야 합니다. 대부분의 API 문서는 결과로 넘겨지는 데이터 사용이 끝난 뒤 어떻게 해야 하는지를 분명히 명시하고 있기 때문에 이에 따라 리소스를 처리 해야합니다.

따라서 예전부터 GLib / GTK+ 프로그래밍시에는 편집기의 자동 완성 (auto-complete) 기능을 잊어버리고, 번거롭더라도 DevHelp프로그램이나 웹브라우저를 이용해 API 리퍼런스를 분명히 열람한 뒤 정확하게 API를 사용할 것을 권장하고 있습니다.

만일, API 문서에 명확하게 메모리 관리 / 객체 참조 카운터 방식이 명시되지 않았거나 무언가 개운치 않다면, 해당 API 소스 코드를 참고하는 것이 가장 확실한 방법입니다.

Posted in Development | Tagged , | Comments Off

inotify 를 이용한 파일 시스템 감시

inotify 는 커널 2.6.13 이후 버전에서 사용가능하며 파일시스템의 접근, 변경, 삭제등의 동작을 알려주는 기능을 수행합니다.
모니터링 시스템에서 유용하게 사용가능하며 이벤트 감지의 통보를 파일시스템의 파일에 저장하며 바로 이벤트를 감지할 수 있으므로 다른 응용에도 사용 가능할 것으로 보입니다.

위키 백과 사전에도 설명되어 있듯이  3가지 api 를 지원합니다.

  • int inotify_init ()
  • int inotify_add_watch (int fd, const char* pathname, int mask)
  • nt inotify_rm_watch (int fd, int wd)

간단한 사용예는 다음과 같습니다.

#include <sys/inotify.h>

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )

int main( int argc, char **argv )
{
  int fd, wd;
  struct inotify_event *event;

  fd = inotify_init();
  wd = inotify_add_watch(fd, "/home/jklee3", IN_MODIFY | IN_CREATE | IN_DELETE );

  read( fd, buffer, BUF_LEN );

  event = ( struct inotify_event * ) &buffer[0];

  if ( event->mask & IN_CREATE ) {
    if ( event->mask & IN_ISDIR )
      printf( "The directory %s was created.\n", event->name );
    else
      printf( "The file %s was created.\n", event->name );
  }

  if ( event->mask & IN_DELETE ) {
    if ( event->mask & IN_ISDIR )
      printf( "The directory %s was deleted.\n", event->name );
    else
      printf( "The file %s was deleted.\n", event->name );
  }

  if ( event->mask & IN_MODIFY ) {
    if ( event->mask & IN_ISDIR )
      printf( "The directory %s was modified.\n", event->name );
    else
      printf( "The file %s was modified.\n", event->name );
  }

  inotify_rm_watch( fd, wd );
  close( fd );

  return 0;
}
Posted in Development | Tagged , | Leave a comment

emacs 편집기 활용 팁

emacs를 써보고 싶은 생각에 emacs-snapshot을 설치하고 손에 익혀 가면서 공부하고 있습니다. vim만 사용하다가 갑자기 emacs를 쓰다보니 단축키가 헷갈리지만 emacs도 vim만큼 소스코드를 작성하는 재미가 있습니다.

emacs를 처음 시작하면서 참조했던 사이트나 수정해야 했던 몇가지 팁을 정리했습니다.

1. 한글설정 하기

emacs 23 개발버전인 emacs-snapshot을 설치하고 다음과 같이 설정파일에 입력하면 사용하고 싶은 한글 글꼴을 설정할 수 있습니다.

;; 원하는 글꼴로 변경합니다.
(set-fontset-font "fontset-default" '(#x1100 . #xffdc)  '("Malgun Gothic" . "unicode-bmp"))
(set-fontset-font "fontset-default" '(#xe0bc . #xf66e)  '("New Gulim" . "unicode-bmp"))
(set-fontset-font "fontset-default" 'kana '("Meiryo" . "unicode-bmp"))
(set-fontset-font "fontset-default" 'han '("Microsoft YaHei" . "unicode-bmp"))

참조 사이트: http://kldp.org/node/76157/360514#comment-360514

2. vim에 있는 set nu 명령 그대로 이용하기

vim에 있는 set nu 명령 역시 emacs에도 있습니다. emacs 설정 파일에 다음과 같이 입력하면 소스코드 옆에 라인번호가 나옵니다.

(add-hook 'find-file-hook (lambda ()(linum-mode 1)))
;; 하단에 커서가 위치한 라인 넘버 표시
(line-number-mode 1)
;; 하단에 커서가 위치한 컬럼 넘버 표시
(column-number-mode 1)

참조 사이트: http://www.emacswiki.org/emacs/LineNumbers

3. 커서 라인 하이라이팅 하기

아래와 같이 입력하면 커서가 위치한 라인이 하이라이팅됩니다. 전경색, 배경색만 사용하고 싶은 색깔로 설정하면 됩니다.

(global-hl-line-mode 1)
(set-face-background 'hl-line "#222")
(set-face-foreground 'highlight nil)
(set-face-foreground 'hl-line nil)

참조 사이트: http://www.emacsblog.org/2007/04/09/highlight-the-current-line

4. 한줄씩 삭제하기

vim의 dd 명령과 비슷한 효과를 나타냅니다.

(defun nuke-line()
  "Kill an entire line, including the trailing newline character"
  (interactive)
  (setq previous-column (current-column))
  (end-of-line)
 
  (if (= (current-column) 0)
    (delete-char 1)
    (progn
      (beginning-of-line)
      (kill-line)
      (delete-char 1)
      (move-to-column previous-column))))
 
;; 원하는 단축키로 설정합니다.
(global-set-key [f8] 'nuke-line)

참조 사이트: http://homepages.inf.ed.ac.uk/s0243221/emacs

5. 도움이 되는 사이트

이 문서에서 emacs 단축키를 나열하는 것보다 잘 정리된 사이트를 참조하는 것이 더 도움이 될 것 같아서 링크를 남깁니다.

이멕스, vim 의 설정파일을 공유하는 사이트: http://www.dotfiles.com
KLDP wiki의 emacs 튜토리얼 사이트: http://wiki.kldp.org/wiki.php/EmacsGdbEtagsCscope
Emacs 메뉴얼 사이트: http://www.gnu.org/software/emacs/manual/html_node/emacs/index.html#Top

Posted in Development | Tagged | Leave a comment

콘솔 프로그램의 출력에 색깔 입히기 – Log::Simple::Color

주의: [콘솔 프로그램의 출력에 색깔 입히기 - Log::Simple::Color]의 가장 최근 판은 이곳에서 확인할 수 있습니다.

시작하며

거의 모든 작업을 GUI 환경에서 처리하는 요즘에도 업무의 자동화나 일괄 처리 때문에
터미널의 명령줄에서 실행하는 프로그램의 사용의 빈도는 여전히 잦습니다.
보통 이런 프로그램은 작업의 진행 내역을 알려주기 위해 표준 출력이나 표준 에러에
유용한 정보를 출력하거나 또는 로그에 기록을 남깁니다.
이 중에서도 특히 표준 출력이나 표준 에러에 정보를 출력하는 경우
글자나 배경에 색깔을 입혀서 가독성을 높힐 수 있습니다.
ANSI 를 지원하는 리눅스 계열의 시스템과 윈도우즈 시스템에서
출력물에 색깔을 입혀서 사용자 친화적인 프로그램을 만듭니다.

준비물

명령줄 프로그램을 작성하기 위해 많이 사용하는 스크립트 언어로는 펄(Perl)이 있습니다.
CPAN 모듈을 이용해서 리눅스와 윈도우즈 환경 모두에서 동작하는 프로그램의 뼈대를 작성합니다.

관련 연구

Term::ANSIColor

Term::ANSIColor 는 ANSI 회피 문자를 사용해서 화면에 출력하는 글자 또는 배경에 색을 입히는 모듈입니다.
당연히 ANSI를 지원하는 터미널에서 동작하며 지원하지 않는 터미널에서는 ANSI 회피 문자열 그 자체가
화면에 나타납니다.

다음은 녹색 배경에 밑줄과 굵은 속성을 가진 노란색 글씨와 굵은 속성의 파란색 글씨를 출력하는 예제입니다:

  #!/usr/bin/perl

  use 5.010;
  use strict;
  use warnings;
  use Term::ANSIColor qw(:constants);

  say BOLD, UNDERLINE, YELLOW, ON_GREEN, "ugly", RESET;
  say "This text is normal.";

  say BOLD, BLUE, "This text is in bold blue.", RESET;
  say "This text is normal.";

Win32::Console

윈도우즈 콘솔은 ANSI 회피 문자를 지원하지 않기 때문에 다른 기능을 사용해야합니다.
Win32::Console 모듈은 윈도우즈 콘솔과 문자 모드를 다룰 수 있는 기능을 제공합니다.
윈도우즈 콘솔은 굵은 글씨나 밑줄 등의 속성을 지원하지 않으므로 색상만 변경할 수 있습니다.

다음은 녹색 배경에 노란색 글씨와 파란색 글씨를 출력하는 예제입니다:

  #!/usr/bin/perl

  use 5.010;
  use strict;
  use warnings;
  use Win32::Console;

  my $console = Win32::Console->new(STD_OUTPUT_HANDLE);
  my $reset = $console->Attr;

  $console->Attr($FG_YELLOW | $BG_GREEN);
  say "ugly";

  $console->Attr($reset);
  say "This text is normal.";

  $console->Attr($FG_BLUE);
  say BOLD, BLUE, "This text is in bold blue.", RESET;

  $console->Attr($reset);
  say "This text is normal.";

구현

다음은 Term::ANSIColor 과 Win32::Console 모듈을 이용해 작성한
간단한 로그 모듈 Log::Simple::Color 입니다:

  package Log::Simple::Color;

  use strict;
  use warnings;

  sub say { print @_, "\n" }

  my $console;
  my %color_of;
  my %msg;

  my $default_level = 'info';
  my %log_level_of = (
      debug   => 0,
      info    => 1,
      warning => 2,
      error   => 3,
  );

  my %default_msg = (
      debug => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{debug} < $log_level_of{$self->level};
          say @args;
      },
      info => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{info} < $log_level_of{$self->level};
          say @args;
      },
      warning => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{warning} < $log_level_of{$self->level};
          say @args;
      },
      error => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{error} < $log_level_of{$self->level};
          say @args;
      },
      default => sub {
          my ( $self, $mode, @args ) = @_;
          return if $log_level_of{default} < $log_level_of{$self->level};
          say "[$mode] ", @args;
      },
  );

  my %linux_msg = (
      debug => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{debug} < $log_level_of{$self->level};
          say @{ $color_of{debug} }, @args, @{ $color_of{default} };
      },
      info => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{info} < $log_level_of{$self->level};
          say @{ $color_of{info} }, @args, @{ $color_of{default} };
      },
      warning => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{warning} < $log_level_of{$self->level};
          say @{ $color_of{warning} }, @args, @{ $color_of{default} };
      },
      error => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{error} < $log_level_of{$self->level};
          say @{ $color_of{error} }, @args, @{ $color_of{default} };
      },
      default => sub {
          my ( $self, $mode, @args ) = @_;
          return if $log_level_of{default} < $log_level_of{$self->level};
          say "[$mode] ", @args;
      },
  );

  my %window_msg = (
      debug => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{debug} < $log_level_of{$self->level};
          $console->Attr(@{ $color_of{debug} });
          say @args;
          $console->Attr(@{ $color_of{default} });
      },
      info => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{info} < $log_level_of{$self->level};
          $console->Attr(@{ $color_of{info} });
          say @args;
          $console->Attr(@{ $color_of{default} });
      },
      warning => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{warning} < $log_level_of{$self->level};
          $console->Attr(@{ $color_of{warning} });
          say @args;
          $console->Attr(@{ $color_of{default} });
      },
      error => sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{error} < $log_level_of{$self->level};
          $console->Attr(@{ $color_of{error} });
          say @args;
          $console->Attr(@{ $color_of{default} });
      },
      default => sub {
          my ( $self, $mode, @args ) = @_;
          return if $log_level_of{default} < $log_level_of{$self->level};
          say "[$mode] ", @args
      },
  );

  if ($^O eq 'linux') {
      eval 'use Term::ANSIColor qw(:constants);';
      if (!$@) {
          eval(
                '$color_of{default} = [ RESET          ];'
              . '$color_of{debug}   = [ CYAN           ];'
              . '$color_of{info}    = [ YELLOW         ];'
              . '$color_of{warning} = [ WHITE, ON_BLUE ];'
              . '$color_of{error}   = [ WHITE, ON_RED  ];'
          );
          $msg{debug}   = $linux_msg{debug};
          $msg{info}    = $linux_msg{info};
          $msg{warning} = $linux_msg{warning};
          $msg{error}   = $linux_msg{error};
          $msg{default} = $linux_msg{default};
      }
      else {
          say "Recommand CPAN Perl Module: [Win32::Console]";
          $msg{debug}   = $default_msg{debug};
          $msg{info}    = $default_msg{info};
          $msg{warning} = $default_msg{warning};
          $msg{error}   = $default_msg{error};
          $msg{default} = $default_msg{default};
      }
  }
  elsif ($^O eq 'MSWin32') {
      eval 'use Win32::Console;';
      if (!$@) {
          eval(
                '$console = Win32::Console->new(STD_OUTPUT_HANDLE);'
              . '$color_of{default} = $console->Attr;'
              . '$color_of{debug}   = [ $FG_CYAN             ];'
              . '$color_of{info}    = [ $FG_YELLOW           ];'
              . '$color_of{warning} = [ $FG_WHITE | $BG_BLUE ];'
              . '$color_of{error}   = [ $FG_WHITE | $BG_RED  ];'
          );
          $msg{debug}   = $window_msg{debug};
          $msg{info}    = $window_msg{info};
          $msg{warning} = $window_msg{warning};
          $msg{error}   = $window_msg{error};
          $msg{default} = $window_msg{default};
      }
      else {
          say "Recommand CPAN Perl Module: [Win32::Console]";
          $msg{debug}   = $default_msg{debug};
          $msg{info}    = $default_msg{info};
          $msg{warning} = $default_msg{warning};
          $msg{error}   = $default_msg{error};
          $msg{default} = $default_msg{default};
      }
  }
  else {
      $msg{debug}   = $default_msg{debug};
      $msg{info}    = $default_msg{info};
      $msg{warning} = $default_msg{warning};
      $msg{error}   = $default_msg{error};
      $msg{default} = $default_msg{default};
  }

  sub new {
      my ($class, %param) = @_;

      my $self = bless {
          level => $default_level,
      }, $class;

      $self->level($param{level}) if exists $param{level};

      return $self;
  }

  sub debug   { $msg{debug}->(@_) }
  sub info    { $msg{info}->(@_) }
  sub warning { $msg{warning}->(@_) }
  sub error   { $msg{error}->(@_) }

  sub level {
      my ( $self, $level ) = @_;

      return $self->{level} unless $level;

      if    ( $level =~ m/^debug$/i )   { $self->{level} = 'debug'; }
      elsif ( $level =~ m/^info$/i )    { $self->{level} = 'info'; }
      elsif ( $level =~ m/^warning$/i ) { $self->{level} = 'warning'; }
      elsif ( $level =~ m/^error$/i )   { $self->{level} = 'error'; }
      else                              { $self->{level} = 'info'; }
  }

  1;

사용

Log::Simple::Color 를 사용하기 위해서는 생성자를 이용해서 객체를 먼저 만듭니다:

  use Log::Simple::Color;

  my $log = Log::Simple::Color->new;

객체가 지원하는 메소드 종류는 다음과 같습니다:

  • $log->level
  • $log->debug
  • $log->info
  • $log->warning
  • $log->error

완전한 예제는 다음과 같습니다:

  #!/usr/bin/perl 

  use strict;
  use warnings;
  use Log::Simple::Color;

  my $log = Log::Simple::Color->new;

  for my $level (qw/ incorrect_mode debug info warning error /) {
      print "$level: [", $log->level, "] -> [", $log->level($level), "]\n";

      $log->debug("This is a debug message");
      $log->info("This is an info message");
      $log->warning("This is a warning message");
      $log->error("This is an error message");
  }

정리하며

로그메시지는 프로그램의 동작 상태를 확인할 수 있는 효과적인 방법입니다.
특이사항이나 문제가 발생한 부분처럼 중요하기 때문에 눈에 띄어야 한다면
중요도 별로 색깔을 서로 다르게 입혀서 효율적으로 로그를 관리할 수도 있습니다.
비록 윈도우즈 기본 터미널은 ANSI 를 지원하지 않기 때문에 서로 다른 두 가지 모듈을
사용해서 구현했지만 모듈로 한번 만들어놓으면 간단한 콘솔 스크립트를 작성할 때
편리하게 사용할 수 있습니다.

Posted in Development | Tagged , , , | Comments Off

리눅스 커널 2.6.28 릴리스

리누스 토발즈가 크리스마스 선물로 2.6.28 버전 커널을 내놓았군요. 늘 그랬듯이 변동사항 중에서 관심있는 사항에 대해서만 정리해 보았습니다. 자세한 내용은 물론 커널 뉴비에서 확인이 가능합니다.

Ext4 파일시스템 공식 지원

Ext3 파일시스템과 하위 호환성을 유지하면서 대안을 각광받고 있는 Ext4 파일시스템이 드디어 실험(experimental) 딱지를 떼고 안정 버전으로 바뀌었습니다. 더 큰 용량 지원, 더 좋아진 성능과 안정성을 기반으로 리눅스의 주 파일 시스템으로 사용될 것 같습니다.

조금 더 자세히 살펴보면, Ext3는 최대 16TB, 파일 크기는 2TB까지 지원했지만, Ext4는 1EB, 파일 크기는 16TB까지 지원합니다.(1EB = 1024PB, 1PB = 1024TB, 1TB=1024GB) Ext3는 한 디렉토리에 32000개 항목만 지원하지만, Ext4는 무한대의 항목이 가능합니다. 또한 Ext4는 전통적인 Unix 파일시스템의 간적 블럭 맵핑 방식 대신 최신 파일시스템에서 사용하는 익스텐트(extents) 기법을 적용하여 큰 파일을 처리하는데 성능을 개선했습니다. 블럭 할당 알고리즘도 개선하고, 악명놓은 fsck 실행 시간도 개선했고, 그외 많은 부분에서 분명 Ext3 파일시스템의 한계를 극복했습니다.

하지만 아직 대부분의 배포판에서 사용하는 GRUB 부트매니저가 지원하지 않고(우분투나 데비안에서는 grub-pc 패키지를 설치하면 됨) 있지만 대부분 배포판의 다음 배포판에서는 다음 릴리스부터 공식적으로 지원할 것 같습니다.(다른 편법은 /boot 디렉토리만 ext3 파일시스템을 사용하면 됩니다) 물론 기본 Ext3 파일 시스템을 Ext4 파일시스템으로 마운트해서 사용할 수도 있고, Ext3 -> Ext4 변환도 가능합니다.

차세대 리눅스 파일시스템으로 각광받고 있는 Btrfs 파일시스템이 안정화될 때까지 사용하기 위한 임시 방편이라는 말도 있지만, 향후 2~3년 정도는 Ext3의 자리를 물려받아 대세가 될 파일시스템임은 분명할 것 같습니다.

GPU 메모리 관리자 GEM 추가

리눅스 커널이 윈도우 커널과 비교될때 가장 취약적으로 지적받던 그래픽 카드 관련 기능은 많은 이슈와 논란을 거쳤지만 결국 리눅스 커널은 프레임버퍼(FB)나 3D 가속을 위한 인터페이스 정도(DRI)만 지원했습니다. 그러던 것이 이제서야 드디어 본격적으로 커널에서 X 서버와 유기적으로 동작하기 위한 기능이 추가되기 시작했는데, 기존 X서버에서 처리하던 GPU 메모리 관리 기능이 리눅스 커널이 처리하게 된 것입니다. (비디오 모드 설정기능은 2.6.29에서 공식 지원할 듯)

아직 i915 드라이버 기반 최신 인텔 그래픽 칩셋만 지원하지만, 페도라 10 배포판은 이미 이 기능을 이용하여 부팅부터 GDM 로그인까지 화면 깜박임없는 부팅을 지원하고 있습니다. (물론 이를 위해 사용하는 최신 X서버가 NVidia 같은 바이너리 드라이버가 제대로 동작하지 않아 말도 많았지만) 시간은 걸리겠지만 다른 그래픽 드라이버도 모두 이 방식을 지원하게 될 것이고, 리눅스 기반 그래픽 환경도 성능과 안정성에서 다른 운영체제와 차이점이 더 줄어들 것으로 생각됩니다. 물론 드라이버 개발자는 더 골치 아파지겠지만…

디스크 충격 방지 기능

노트북에서 많이 사용하는 충격 방지 기능은 디스크를 사용하지 않을때 헤드를 언로드해서 충격이 발생해도 디스크 표면에 영향을 안끼치는 방식입니다. 이번 릴리스부터 리눅스 커널은 /sys/block/*/device/unload_heads 파일에 밀리초 단위의 정수형을 쓰면 그 시간동안 헤드를 언로드하고, 시간이 지나면 다시 원래대로 되돌리는 기능을 지원합니다. 하지만 모든 디스크를 지원하는 것도 아니고, 어떤 디스크는 오동작을 일으킬 수도 있기 때문에 정확히 지원하는 모델인지 확인하고 사용해야 합니다.

기타

그외에도 VM 메모리 관리자를 다시 작성해 많은 메모리 장착시 성능 개선, 커널 부팅 최적화를 도와주는 CONFIG_BOOT_TRACER 옵션 추가,  Atheros L2(atl2) 드라이버 공식 지원, Ultra Wide Band / Wireless USB 지원, SSD(solid-state drive) 디스크 지원 강화, ALSA 드라이버 1.0.18 탑재 등 역시나 이번에도 많은 변화가 있었으니 더 관심있는 분은 이 기사도 읽어보시길… :)

Posted in Development | Tagged , | Comments Off