2009년 11월 9일 월요일

TPTP 프로파일링 Part 1, TPTP 설치 및 메모리 누수 찾기 Java


출처 : http://javacan.tistory.com/115

TPTP를 이클립스에 설치하는 방법과 TPTP를 이용하여 메모리 누수 코드를 찾는 방법에 대해서 살펴본다.

TPTP 설치하기

메모리 누수를 찾는 것은 쉽지 않은데 그 이유는 메모리가 부족한 현상이 어플리케이션을 실행하자 마자 곧 바로 나타나는 것이 아니라 짧게는 1-2시간, 길게는 1주일 후에나 발생하기 때문이다. 예를 들어, 웹 어플리케이션을 구동하면 대략 48시간 정도 후에 시스템에서 OutOfMemoryError 와 같은 치명적 에러가 발생하면서 어플리케이션이 죽는 경우가 있을 것이다. 또한, 메모리가 부족하여 시스템이 죽는 걸 알게 되어도 어떤 코드 때문에 메모리 누수 현상이 발생하는 지 찾는 것 또한 쉽지 않다.

메모리 누수 현상이 발생할 경우 이를 해결하는 가장 좋은 방법은 어플리케이션을 프로파일링 하여 메모리 누수가 될 만한 코드를 찾는 것이다. 프로파일링을 통해서 메모리 누수가 발생하는 코드를 찾을 수 있도록 도와주는 다양한 툴이 존재하는데, 본 글에서는 TPTP가 제공하는 프로파일링 기능을 사용하여 메모리 누수가 예상되는 코드를 찾아내는 방법에 대해서 살펴볼 것이다.

TPTP 설치하기

TPTP는 이클립스 Test & Performance Tool Platform 프로젝트의 약자로서, 이클립스에서 오픈 소스로 진행중인 테스트 및 성능 관련 툴을 위한 플랫폼이다. TPTP는 모니터링, 테스트 자동화, 프로파일링 등 어플리케이션의 문제점을 찾고 해결하는 데 도움이 되는 기능을 제공하고 있다. TPTP는 이클립스 플러그인으로 제공되며, 설치 과정은 아래와 같다.

  1. 이클립스 3.2 설치: http://www.eclipse.org/downloads/
  2. EMF 2.2.1 SDK 버전(EMF, SDO, XSD가 함께 포함된 배포판)을 다운로드 하여 이클립스 폴더에 설치:http://www.eclipse.org/emf/downloads/
  3. UML2 2.0.1 SDK 버전을 다운로드하여 이클립스에 설치:http://download.eclipse.org/tools/uml2/scripts/downloads.php
  4. TPTP 4.2.0.x All Runtime 버전을 다운로드 하여 이클립스에 설치: http://www.eclipse.org/tptp/
EMF, UML2, TPTP 등은 모두 플러그인 형태로 제공되기 때문에, 압축을 풀면 plugins 폴더와 features 폴더가 생성된다. 이 두 폴더를 이클립스 디렉토리에 그대로 복사한 뒤 이클립스를 재실행하기만 하면 모든 설치가 완료된다. 이클립스를 실행한뒤, 'Run' 메뉴를 클릭하면, 아래 그림과 같이 프로파일과 관련된 메뉴가 추가된 것을 확인할 수 있다.

 
위에서 설명한 EMF, SDO, XSD, UML2의 버전은 이클립스 3.2에서 올바르게 동작하는 것으로 이클립스 3.1을 사용할 경우 그에 알맞은 버전을 설치해야 TPTP를 실행할 수 있으니 주의하기 바란다.

메모리 누수를 찾는 과정

자바는 개발자가 별도로 메모리 관리를 하지 않아도 가비지 콜렉터를 통해서 사용되지 않는 객체를 메모리에서 제거해준다. 가비지 콜렉터가 메모리 관리를 수행하는 알고리즘이 단순하진 않지만, 기본적으로 더 이상 참조되지 않는 객체를 가비지로 인식하여 해당 객체를 메모리에서 제거하게 된다. 예를 들어, 아래의 코드를 살펴보자.

    Human h = new Hunam(); // 새로운 Human 객체 생성
    ....
    h = null; // 더 이상 Human 객체를 참조하지 않음

위 코드에서 새로 생성한 Human 객체를 h 가 참조하도록 했는데, 어떤 처리를 수행한 후 h 에 null을 할당하였다. 이렇게 되면 생성된 Human 객체는 더 이상 참조되지 않으며, 가비지 콜렉터의 대상이 된다. 객체가 더 이상 참조되지 않는다고 해서 곧바로 메모리에서 제거되는 것은 아니며, 메모리가 부족하거나 인위적으로 가비지 콜렉터를 실행했을 때 비로서 참조되지 않는 객체가 메모리에서 제거된다.

하지만, 개발자의 실수로 더 이상 사용되지 않는 객체를 어디선가 참조하게 되면, 그 객체는 가비지 콜렉터의 대상이 되지 않으며 따라서 지속해서 메모리에 남아 있게 된다. 이런 객체가 많아질수록 메모리는 불필요한 객체로 공간을 낭비하게 되며, 결국 새로운 객체를 생성해서 저장할 메모리 공간이 부족한 상태가 발생하게 된다.

메모리 누수를 발견하는 과정

메모리 누수가 발생하는 이유는 가비지 콜렉터에 의해 메모리에서 제거되어야 할 객체가 계속해서 누군가에 의해 참조되고 있기 때문일 것이다. 따라서, 어떤 객체가 메모리 누수를 일으키는 지 알아내기 위해서는 특정 시점에 메모리에 저장된 객체들의 상태를 알아내는 것이 중요하다. 즉, 현재 어떤 클래스의 객체가 몇개 만들어졌고, 그 중에 몇개가 가비지 콜렉터에 의해 제거되었고, 또, 객체들 사이의 참조 관계는 어떻다라는 것을 알아야 하는 것이다.

대부분의 프로파일링 툴은 특정 시점의 메모리 상태를 알아낼 수 있는 기능을 제공하고 있으며, TPTP가 제공하는 프로파일링 툴 역시 이 기능을 제공하고 있다. 이 기능을 사용하면 특정 시점의 메모리 스냅샵을 생성할 수 있는데, 툴 마다 다르지만 TPTP의 경우는 다음과 같은 메모리 스냅샵을 제공한다.


위 그림을 보면 현재까지 생성된 객체의 개수와 살아있는(active) 객체, 그리고 제거된(collected) 객체의 수 뿐만 아니라 바이트 단위로 현재 사용중인 메모리 사용량까지 표시되어 있다. TPTP 이외의 프로파일링 툴 역시 이와 비슷한 정보를 제공하며, 아래와 같은 절차에 따라 메모리 누수가 발생하는 객체를 찾을 수 있게 된다.


메모리 누수를 발생시킬거라고 의심되는 코드를 실행하기에 앞서 메모리 스냅샵을 구한다. 그리고 나서, 해당 코드를 실행하고 그 다음에 다시 메모리 스냅샷을 구한다. 만약 GC 되어야 할 객체가 잘못된 코드에 의해 GC 되지 않는다면 스냅샷1에서는 존재하지 않았던 (또는 개수가 매우 적었던) 객체의 수가 스냅샷2에서는 증가되어 있을 것이다. 메모리 스냅샷을 구하기에 앞서 GC를 실행하는 이유는 더 이상 참조되지 않는 객체를 메모리에서 제거함으로써 좀더 정확하게 의심 객체를 찾기 위함이다.

TPTP를 사용한 메모리 누수 찾기

TPTP를 사용할 때에도 앞서 설명한 메모리 스냅샷을 사용해서 메모리 누수를 발생시키는 객체를 찾을 수 있으며, TPTP가 제공하는 추가적인 기능을 사용해서 메모리 누수를 발생시키는 메소드까지도 발견해낼 수 있다. 본 절에서는 TPTP를 사용하여 메모리 누수 코드를 발견하는 과정을 살펴보도록 하겠다.

메모리 누수가 발생하는 예제 어플리케이션

일단 메모리 누수를 일으키는 코드부터 작성해보도록 하자.

    package test.memoryleak.human;
   
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.HashMap;
    import java.util.Map;
   
    public class HumanTest {
       
        private Map<Integer, Human> tempMap;
       
        public  HumanTest() {
            tempMap = new HashMap<Integer, Human>();
        }
       
        public void testHuman(int i) {
            Human human = new Human(i);
            tempMap.put(human.getId(), human);
           
            if (i > 0 && i % 3 != 0) { // 의도적으로 i가 3의 배수일 때 tempMap에서 제거되지 않도록 함
                tempMap.remove(human.getId());
            }
        }
       
        public static void main(String[] args) throws IOException {
            HumanTest test = new HumanTest();
           
            System.out.println("press enter ....");
            BufferedReader br = null;
            br = new BufferedReader(new InputStreamReader(System.in));
            br.readLine();
           
            try {
                for (int i = 1 ; i <= 10000 ; i++) {
                    test.testHuman(i);
                    if (i == 10) {
                        System.out.println(i +" humans processed");
                        System.out.println("press enter ....");
                        br.readLine();
                    }
                    if (i > 0 && i % 5000 == 0) {
                        try {
                            Thread.sleep(2000);
                        } catch(Throwable ex) {}
                        System.out.println(i+" humans processed");
                        br.readLine();
                    }
                }
                System.out.println("completed.");
            } catch(OutOfMemoryError ex) {
                System.out.println("error: "+ex.getMessage());
            }
            System.out.println("press any key....");
            br.readLine();
        }
    }

위 코드에서 testHuman() 메소드는 Human 객체를 생성한 뒤 임시로 tempMap에 저장한다. 그리고 Human 객체가 사용된 뒤에 마지막에 임시로 저장한 tempMap에서 Human 객체를 제거한다. 하지만, 메모리 누수 현상을 설명하기 위해 의도적으로 i의 값이 3의 배수인 경우에는 tempMap에서 Human 객체를 제거하지 않았다. 따라서, tempMap에 저장된 Human 객체는 tempMap이 Human 객체를 참조하고 있으므로, GC의 대상이 되지 않게 되며, 곧 메모리 누수가 발생하게 된다.

프로파일 시작하기

메모리 스냅샷을 구하기 위해서는 자바 어플리케이션을 프로파일이 가능한 상태로 실행해야 한다. 프로파일이 가능하도록 어플리케이션을 수행하는 과정은 다음과 같다.

  1. 'Run' > 'Profile As' > 'Java Application' 을 실행
  2. Monitor 탭을 선택해서 프로파일 할 옵션을 선택
     
    • 'Java Profiling' 옵션을 선택한 뒤, 'Edit Options'을 클릭한 뒤, 아래와 같이 조사할 클래스의 범위 선택

      [Next] 버튼을 클릭한 다음에 아래 그림과 같이 어플리케이션이 구동될 때 자동적으로 프로파일을 시작하도록 설정
    • 'Basic Memory Analysis' 옵션을 선택
    • 'Execution Time Analysis' 옵션 선택한 뒤, 'Edit Options'를 클릭하여 'Show execution flow graphical details'를 선택
조사할 클래스의 범위를 지정해주지 않으면 모든 클래스에 대해서 프로파일링 하게 되는데, 이 경우 프로파일링에 따른 부하 때문에 어플리케이션을 실행하는 데 방해를 줄 수도 있다. 따라서, 분석해야만 하는 클래스에 대해서만 프로파일링 하도록 클래스 범위를 지정해주는 것이 좋다.

설정이 완료되었다면 'Ok' 버튼을 눌러서 프로파일을 시작해보자. 그럼, 아래와 같은 대화창이 뜰 것이다.


이 대화창은 'Profiling and Logging' 퍼스펙티브로 전환할지 묻는 것으로 'Yes' 버튼을 클릭하면 아래 그림과 같이 프로파일링을 위한 퍼스펙티브로 전환된다.


위 화면에서 좌측 영역은 프로파일과 관련된 기능을 제공하며, 우측 영역은 프로파일링 정보를 보여준다. 이제 어플리케이션에서 메모리가 누수되는 지점을 찾을 준비가 완료되었다.

메모리 스냅샵으로부터 메모리 누수되는 객체 추측

메모리 누수가 발생하는 지점을 찾기 위해서는 먼저 어떤 객체가 메모리 누수의 범인인지 찾아내야 한다. 이를 위해서는 앞서 설명했듯이 메모리 스냅샷 정보가 필요하다. 예제 어플리케이션은 testHuman() 메소드를 10,000번 실행하는 데, 처음 10번째 까지 실행한 뒤 엔터키 입력을 기다린다. 콘솔 화면에서 엔터키를 눌러보면 아래 그림과 같이 10번 실행된 뒤 키 입력을 기다릴 것이다.


이 상태에서 메모리 스냅샷을 살펴보자. 메모리 스냅샵을 구하기 전에 GC를 먼저 실행해야 하는데, GC는
 아이콘을 클릭하거나 팝업 메뉴를 통해서 실행할 수 있다. GC를 실행한 뒤, 'Basic Memory Analysis'를 더블 클릭해서 메모리 스냅샷을 구해보자. 아래와 비슷한 메모리 스냅샷 결과가 출력될 것이다.


위 그림을 보면 Human 객체가 총 10개 생성되었고 그 중에 4개가 여전히 메모리에 존재하고, 6개는 GC에 의해 메모리에서 제거되었음을 알 수 있다. 4개의 Human 객체가 메모리 존재한다는 사실을 통해서 Human 객체가 메모리 누수를 일으킬 수 있는 가능성을 확인할 수 있다. 이제 콘솔에서 다시 한번 엔터키를 눌러보자. 그럼, 아래 그림과 같이 메모리 부족 현상이 발생함을 알 수 있다.


메모리 스냅샷을 확인하기 위해 GC를 실행하고, 'Memory Statistics' 화면을 'Refresh' 해보자. 그럼, 아래와 같이 메모리 상태에 변화가 생겼을 것이다.


위 그림을 보면 Human 객체가 904개가 생겼는데 이 중에 302개나 메모리에 존재한다는 것을 알 수 있으며, 이를 통해 Human 객체가 누수되고 있다는 사실을 확신할 수 있게 되었다.

객체 참조 그래프로부터 GC 방해 요소 추측

Human 객체가 메모리 누수의 범인이라는 점을 밝혀냈으므로, 이제 어떤 객체가 Human 객체를 참조하고 있는지를 알아내야 한다. 가비지 콜렉터는 기본적으로 어떤 객체에 의해서도 참조되지 않는 객체에 대해서 메모리 반환을 수행하기 때문에, Human 객체가 메모리에 남아 있다는 것은 곧 Human 객체를 누군가가 참조하고 있다는 뜻이된다.

객체 참조 상태를 확인하기 위해서는 먼저 객체 참조 정보를 수집해야 한다. 아래 그림과 같이 팝업 메뉴 또는 해당 아이콘을 클릭함으로써 현재 상태의 객체 참조 그래프를 구할 수 있다.


객체 참조 정보를 구했다면, 이제 아래와 같이 실행하여 객체 참조 정보를 확인하도록 하자.


객체 참조 정보에서 Human 객체의 참조 정보를 확인하면 아래 그림과 같은 그래프를 확인할 수 있다. 아래 그래프를 보면 Human 객체를 HashMap 객체가 참조하고 있고, 이 HashMap 객체를 HumanTest 가 참조하고 있다는 것을 알 수 있다. 즉, HumanTest 객체의 (HashMap 타입의) tempMap 필드가 Human 객체를 저장하고 있다는 것을 확인할 수 있다.



클래스 인터액션 그래프로부터 누수 예상 메소드 추측

HumanTest 클래스의 tempMap 필드가 Human 객체를 참조하기 때문에 Human 객체가 메모리에서 제거되지 않고 메모리 누수를 일이키고 있다는 것을 알게 되었다. 이쯤 되면 대충 어떤 부분에서 잘못된 코드를 작성했는 지 유추해 낼 수 있다. 하지만, 많은 클래스가 존재하고 참조 문제를 일으키는 범인 클래스가 다양한 곳에서 사용되는 경우가 있다. 이런 경우에는 클래스들 간의 인터액션을 살펴봄으로써 어떤 메소드에서 문제가 있는 지를 찾아낼 수 있다. 아래 그림과 같이 'UML2 Class Interaction' 메뉴를 실행해보자.


그럼, 클래스 사이의 메소드 호출 관계 및 실행 순서를 시퀀스 다이어그램을 통해서 확인할 수 있다. 이 그래프를 살펴보면 아래와 같은 부분을 발견할 수 있을 것이다.


위 그림에서 파란색 박스 부분은 올바르게 실행된 testHuman() 메소드의 경우이다. 파란색 부분은 HashMap의 put과 remove가 호출된 것을 확인할 수 있다. 즉, Human 객체를 생성한 뒤 HashMap에 저장했다가 메소드를 종료하기 전에 HashMap에 제거했다는 것을 유추할 수 있다. 반면에 빨간색 박스 부분은 메소드를 리턴하기 전에 remove 메소드가 호출되지 않는 것을 볼 수 있는데, 이는 HashMap에 저장된 Human 객체가 메소드가 testHuman() 메소드가 리턴되기 전에 HashMap에서 제거되지 않는다는 것을 유추할 수 있다. 즉, TestHuman 클래스의 testHuman() 메소드가 메모리 누수를 발생시키는 코드를 포함하고 있다는 것을 알아낸 것이다.

이제 남은 작업은 메모리 누수를 발생시키는 코드를 찾아서 문제를 제거하는 것만 남았다!!

결론

본 글에서는 TPTP가 제공하는 프로파일링 기능을 사용하여 누수되고 있는 객체를 찾아내고, 해당 객체를 누수 시키는 코드를 찾아나가는 과정을 살펴보았다. 본 글에서 소개한 방식을 통해서 여러분은 메모리 누수를 일으키는 문제의 코드를 비교적 빠르게 찾아낼 수 있게 되었을 것이다. 다음 글에서는 TPTP를 사용하여 어플리케이션의 병목(bottleneck) 부분을 찾는 방법에 대해서 살펴볼 것이며, 이를 통해 어플리케이션의 실행 속도를 향상시키는 방법을 배우게 될 것이다.

관련링크:

* 작성자의 허락없이 본 글을 무단으로 복사 및 유포하는 것을 금합니다. 
Posted by madvirus

2009년 11월 6일 금요일

libcurl 설치


8) libcurl 설치
http://curl.haxx.se/libcurl/ 에서 curl-7.10.2.tar.gz

./configure --disable-ipv6 --with-ssl=/usr/local/ssl (if only ssl was built here)
make
make install (libcurl.so is now in /usr/local/lib)

참고 : curl 은 포트를 열어 정보를 가져오거나 정보를 보내는데 쓰이는 강력한 툴이더군요.
저는 https 사이트의 페이지를 긁어오느라 설치하게 되었습니다. 다른 방법이 없더군요.
post와 get 방식 동시 지원됩니다.
설치참고 : http://curl.haxx.se/libcurl/php/install.html

SSL

SSL(Secure Socket Layer)은 넷스케이프사에서 전자상거래 등의 보안을 위해 개발하였다. 이후 TLS(Transport Layer Security)라는 이름으로 표준화되었다. SSL은 특히 네트워크 레이어의 암호화 방식이기 때문에 HTTP 뿐만 아니라 NNTP, FTP등에도 사용할 수 있는 장점이 있다. 기본적으로 Authentication, Encryption, Integrity를 보장한다.

2009년 11월 5일 목요일

유니코드

유니코드(Unicode)는 전세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준이다. 유니코드 협회(Unicode Consortium)가 제정하며, 최신판은 2008년 4월에 공개된 유니코드 5.1이다. 이 표준에는 ISO 10646 문자 집합, 문자 인코딩, 문자 정보 데이터베이스, 문자들을 다루기 위한 알고리즘 등을 포함하고 있다.

유니코드의 목적은 현존하는 문자 인코딩 방법들을 모두 유니코드로 교체하려는 것이다. 기존의 인코딩들은 그 규모나 범위 면에서 한정되어 있고, 다국어 환경에서는 서로 호환되지 않는 문제점이 있었다. 유니코드가 다양한 문자 집합들을 통합하는 데 성공하면서 유니코드는 컴퓨터 소프트웨어국제화지역화에 널리 사용되게 되었으며, 비교적 최근의 기술인 XML, 자바, 그리고 최신 운영 체제 등에서도 지원하고 있다.

유니코드에서 한국어 발음을 나타날 때는 예일 로마자 표기법의 변형인 ISO/TR 11941을 사용하고 있다.

ASCII

아스키(ASCII, American Standard Code for Information Interchange, 미국 정보 교환 표준 부호)는 영문 알파벳을 사용하는 대표적인 문자 인코딩이다. 아스키는 컴퓨터와 통신 장비를 비롯한 문자를 사용하는 많은 장치에서 사용되며, 대부분의 문자 인코딩이 아스키에 기초를 두고 있다.

아스키는 1967년에 표준으로 제정되어 1986년에 마지막으로 개정되었다. 아스키는 7비트 인코딩으로, 33개의 출력 불가능한 제어 문자들과 공백을 비롯한 95개의 출력 가능한 문자들로 이루어진다. 제어 문자들은 역사적인 이유로 남아 있으며 대부분은 더 이상 사용되지 않는다. 출력 가능한 문자들은 52개의 영문 알파벳 대소문자와, 10개의 숫자, 32개의 특수 문자, 그리고 하나의 공백 문자로 이루어진다.

아스키가 널리 사용되면서 다양한 아스키 기반의 확장 인코딩들이 등장했으며, 이들을 묶어서 아스키라고 부르기도 한다. 대표적으로 7비트 인코딩을 유지한 ISO/IEC 646과, 원래 아스키 코드 앞에 비트 0을 넣어 8비트 인코딩을 만든 IBM 코드 페이지ISO 8859가 있다. 이 인코딩들은 언어군에 따라 같은 숫자에 서로 다른 문자가 배당된 경우가 많다.

GNU

GNU 프로젝트 (GNU project, 그누 프로젝트)는 리처드 스톨만이 완전한 공개 운영 체제GNU 시스템을 만들기 위한 목적으로 시작했다. 이 프로젝트는 1983년 9월 27일 유즈넷 net.unix-wizard 그룹을 통해 일반에 알려졌다. 스톨만은 첫 선언문에 이은 "GNU 선언문"을 비롯한 여러 글들을 통해서, "초기 전산 공동체에 지배적이었던, 협동 정신을 되돌리자"라고 주장했다.

GNU는 "GNU는 유닉스가 아니다."란 의미를 갖는 영어 문장 "GNU's Not UNIX"의 약자로, 원래의 문장 안에 자신이 이미 들어 있는 재귀 약자이다. 스톨만은 GNU를 그누로 읽자고 제안한다. 유닉스는 이미 널리 쓰이던 독점 소프트웨어 운영 체제로, 유닉스의 아키텍처는 기술적으로 믿을만 한 것으로 증명되어 있어, GNU 시스템은 유닉스와 호환될 수 있도록 만들어졌다. 유닉스 아키텍처는 개별적인 요소들이 따로 따로 작성되는 것을 허용한다. 또, 이미 공개되어 있던 조판 소프트웨어 텍(TeX)이나, X 윈도우도 쓸 수 있는 장점이 있었다.

GNU 프로젝트는 누구나 자유롭게 "실행, 복사, 수정, 배포"할 수 있고, 누구도 그런 권리를 제한하면 안 된다는 사용 허가권(License) 아래 소프트웨어를 배포한다. 카피레프트로 불리는 이런 생각은 GPL(GNU 일반 공중 사용 허가서)에 나타나 있다.

1985년에 스톨만은 GNU 프로젝트를 철학적, 법률적, 금융적으로 지원하기 위해 자선단체자유 소프트웨어 재단(FSF, Free Software Foundation)을 세웠다. 이 재단은 GNU를 개발할 프로그래머들도 고용했다. 그러나, 프로젝트의 대부분은 자원 봉사자들이 개발했으며, 앞으로도 그럴 것이다. GNU가 눈길을 끎에 따라, 이를 주목한 회사들은 GNU 소프트웨어의 개발이나 판매 및 기술 지원을 돕기 시작했다. 이 가운데 가장 두드러지고 성공적인 것은 (현재는 레드햇의 일부인) Cygnus Solutions이다.

1990년까지, GNU 시스템엔 확장 가능한 문서 편집기(이맥스), 뛰어난 최적화 컴파일러(GCC), 그리고 표준 유닉스 배포판의 핵심 라이브러리유틸리티가 있었다. 하지만, 여기엔 주요 구성요소인 커널이 빠져 있었다.

GNU 선언문에 서, 스톨만은 "기본적인 커널은 있지만 유닉스를 흉내내려면 아직 더 많은 기능이 필요하다"라고 했다. 여기서 그가 지칭한 것은 MIT에서 개발하여 자유롭게 배포했고, 유닉스 7번째 판과 호환되는 TRIX라는 원격 작업 호출 커널(remote procedure call kernel)이었다. 1986년 12월, 이 커널을 고치는 작업이 시작됐다. 하지만, 개발자들은 결국 TRIX를 기반으로 새 커널을 만드는 것은 어렵다는 결론을 내렸다. 주된 이유는 TRIX는 "애매하고(잘 안 쓰이고?) 비싼 68000 box"에서만 동작했고, 따라서 그 상자에서 쓰이기 전에 다른 구조로 옮겨야(ported) 했기 때문이다. 1988년 즈음에, 카네기멜론 대학교에 서 개발되던 Mach 통신-전송 커널(Mach message-passing kernel)을 그 대체품으로 고려했지만, 이것은 처음에 이것을 개발한 사람들이 AT&T 소유의 코드를 지우면서 지연되었다. 처음엔, 이 커널은 Alix라고 불렸지만, 나중에 개발자 Michael Bushnell는 HURD라는 이름을 선호하여, Alix란 이름은 하부 구조로 옮겨지고 마침내 완전히 떨어졌다. 결국은, HURD의 개발은 기술적이고 개인적인 충돌로 지지부진해 지고 말았다.

1991년리누스 토르발스는 유닉스 호환의 리눅스 커널을 작성하여 GPL 라이선스 아래에 배포했다. 다른 여러 프로그래머들은 인터넷을 통해 리눅스를 더욱 발전시켰다. 1992년 리눅스는 GNU 시스템과 통합되었고, 이로서 완전한 공개 운영 체제가 탄생되었다. GNU 시스템들 가운데 가장 흔한 것이, "GNU/Linux" 또는 "Linux 배포판"이라고 불리는 바로 이 시스템이다. (2003년 기준으로, 허드(HURD)는 여전히 개발 중이며, 리눅스를 대신하여 허드를 사용한 GNU 시스템을 비공식 실험판으로 사용할 수 있다.)

또한, 비공개 유닉스 시스템에도 GNU의 구성 요소들이 오리지널 유닉스 프로그램을 대신하여 들어 있는 경우도 많다. 이는 GNU 프로젝트를 통해 쓰여진 프로그램들이 질적으로 우수하다는 사실을 증명하는 것이다. 종종, 이런 구성요소들은 "GNU 툴"로 불리기도 한다. 다수의 GNU 프로그램은 마이크로소프트 윈도맥 오에스 텐 등으로 포팅되기도 했다.


http://ko.wikipedia.org/wiki/GNU