'OS'에 해당되는 글 130건

  1. 2012.05.29 공유 메모리 (shared memory)
  2. 2012.05.24 poll을 이용한 채팅서버
  3. 2012.05.23 AIX 32bit/64bit 컴파일
  4. 2012.05.23 Compiler 옵션
  5. 2012.05.23 gcc 옵션 [출처] gcc 옵션 간단 정리|작성자 열이
  6. 2012.05.21 pthread 함수 간단한 요약 정리
  7. 2012.05.21 스레드 조건 변수
  8. 2012.05.21 pthread_cond_timedwait()를 사용한 이벤트 동기화 사용 예제
  9. 2012.05.21 pthread
  10. 2012.05.18 정적 라이브러리, 공유 라이브러리 (3)

공유 메모리 (shared memory)

OS/리눅스 & 유닉스 2012. 5. 29. 16:53

Contents

1 공유메모리 (shared memory)
1.1 개요
1.2 공유메모리는 어떻게 할당되는가
1.3 shmget
1.4 shmat
1.5 shmdt
1.6 shmctl
2 공유메모리 제어하기
2.1 공유 메모리 정보 확인
2.2 /proc 파일 시스템으로 제어하기


1 공유메모리 (shared memory)

보통 프로세스에서 사용되는 메모리영역은 해당 프로세스만이사용할수 있다. 하지만 때때로 여러개의 프로세스가 특정 메모리영역을 사용했으면 하는때가 있을것이다. 

System V IPC 설비중의 하나인 "공유메모리"를 통해서 이러한일을 할수있다.

1.1 개요

모든 프로세스는 자신의 업무를 수행하기 위해서 필요한 자료를 저장하기 위한 메모리 공간을 가지게 된다. 이러한 메모리공간에는 CPU에 의해 수행되는 명령어들, 프로그램 시작시 정의되고 초기화된 데이타, 프로그램 시작시 정의되었지만 초기화 되지 않은 데이타, 함수호출에 필요한 정보, 동적할당이 이루어지는 데이타등 이 들어가게 된다.

프로세스는 시작시 혹은 실행중에 이러한 데이타를 저장하고 사용하기 위한 메모리 공간을 커널에 요구하여서 할당받아 사용하게 되는데, 이러한 메모리공간은 기본적으로 메모리를 요청한 프로세스만이 접근가능하도록 되어있다. 하지만 가끔은 여러개의 프로세스가 특정 메모리 공간을 동시에 접근해야할 필요성을 가질때가 있을것이다.

공유메모리는 이러한 작업을 위한 효율적인 방법을 제공한다. 

공유메모리는 여러 IPC 중에서 가장 빠른 수행속도를 보여준다. 

그이유는 하나의 메모리를 공유해서 접근하게 되므로, 데이타 복사와 같은 불필요한 오버헤드가 발생하지 않기 때문으로, 빠른 데이타의 이용이 가능하다. 그러나 하나의 프로세스가 메모리에 접근중에 있을때, 또다른 프로세스가 메모리에 접근하는 일이 발생하면 자칫 데이타가 홰손될수 있을것이므로, 한번에 하나의 프로세스가 메모리에 접근하고 있다는걸 보증해줄수 있어야 할것이다. 

이러한 작업을 위해서 Unix 에서는 Semaphore 라는 또다른 공유자원을 제어할수 있도록 해주는 도구를 제공해준다. 이번 문서에서는 Semaphore 를 다루지는 않을것이다. 이것은 다른 문서에서 다루도록 하고 여기에서는 단지 공유메모리에 대해서만 다루도록 할것이다. 

다음은 공유메모리에 관련된 함수들의 모음이다. 
#include <sys/types.h>
#include <sys/shm.h>

int shmget(key_t key, int size, int shmflg)
void *shmat( int shmid, const void *shmaddr, int shmflg )
int shmdt( const void *shmaddr)
int shmctl(int shmid, int cmd, struct shmid_ds *buf)

1.2 공유메모리는 어떻게 할당되는가

위의 함수들을 설명하기 전에 우선 공유메모리가 어떻게 할당되고, 어떤 과정을 통해서 접근가능한지에 대해서 우선 알아보도록 하겠다.

공유메모리의 생성요청은 최초 공유메모리 영역을 만드는 프로세스가 커널에 공유메모리 공간의 할당을 요청함으로써 이루어지며, 만들어진 공유메모리는 커널에 의해서 관리 되게 된다.

이런 이유로 한번만들어진 공유메모리는 운영체제를 리부팅하거나, 직접 공유메모리 공간을 삭제시켜주지 않은한, 공유메모리를 사용하는 모든 프로세스가 없어졌다고 하더라도, 계속적으로 유지되게 된다. 

프로세스가 커널에게 공유메모리 공간을 요청하게 되면, 커널은 공유메모리 공간을 할당시켜주고 이들 공유메모리공간을 관리하기 위한 내부자료구조를 통하여, 이들 공유메모리를 관리하게 된다. 이 자료는 shmid_ds 라는 구조체에 의해서 관리되며 shm.h 에 정의되어 있다.
struct shmid_ds
{
    struct         ipc_perm shm_perm;    // 퍼미션
    int            shm_segsz;            // 메모리 공간의 크기
    time_t         shm_dtime;            // 마지막 attach 시간 
    time_t         shm_dtime;            // 마지막 detach 시간 
    time_t         shm_ctime;            // 마지막 변경 시간
    unsigned short shm_cpid;             // 생성프로세스의 pid
    unsigned short shm_lpid;             // 마지막으로 작동한 프로세스의 pid
    short          shm_nattch;           // 현재 접근한 프로세스의 수
};
Unix 버젼에 따라서 멤버변수들이 약간씩 차이를 보일수 있다. 
  • shm_perm
    공유메모리는 여러개의 프로세스가 동시에 접근 가능하므로, 파일과 같이 그 접근권한을 분명히 명시해줘야 한다.
  • shm_segsz
    할당된 메모리의 byte 크기이다
  • shm_atime
    가장최근의 프로세스가 세그먼트를 attach한 시간
  • shm_dtime
    가장최근의 프로세스가 세그먼트를 detach한 시간
  • shm_ctime
    마지막으로 이 구조체가 변경된 시간
  • shm_cpid
    이 구조체를 생성한 프로세스의 pid
  • shm_lpid
    마지막으로 작동을 수행한 프로세스의 pid
  • shm_nattch
    현재 접근중인 프로세스의 수
이러한 공유메모리에 접근을 하기 위해서는 고유의 공유메모리 key 를 통해서 접근가능해지며, 이 key값을 통해서 다른 여러개의 공유메모리들과 구분되어 질수 있다. 


1.3 shmget

shmget 은 커널에 공유메모리 공간을 요청하기 위해 호출하는 시스템 호출 함수이다. key 는 바로 위에서 설명했듯이 고유의 공유메모리임을 알려주기 위해서 사용된다. shmget 을 이용해서 새로운 공유메모리 영역을 생성하거나 기존에 만들어져있던 공유메모리 영역을 참조할수 있다. 

첫번째 아규먼트는 여러개의 공유메모리중 원하는 공유메모리에 접근하기 위한 Key 값이다. 이 Key 값은 커널에 의해서 관리되며, Key 값을 통해서 선택적인 공유메모리에의 접근이 가능하다. 
두번째 아규먼트는 공유메모리 의 최소크기 이다. 새로운 공유메모리를 생성하고자 한다면 크기를 명시해주어야 한다. 존재하는 메모리를 참조한다면 크기는 0으로 명시한다.
3번째 아규먼트는 공유메모리의 접근권한과, 생성방식을 명시하기 위해서 사용한다. 아규먼트의 생성방식을 지정하기 위해서 IPC_CREAT 와 IPC_EXCL 을 사용할수 있다. 아래 이들에 대해서 설명을 해두었다. 

  • IPC_CREAT
    key 를 이용 새로운 공유메모리 공간을 만든다. 
  • IPC_EXCL
    IPC_CREAT와 같이 사용되며, 공유메모리 공간이 이미 존재할경우 error 를 되돌려준다. 

  • 만약 IPC_CREAT 만 사용된다면 shmget()은 새로 생성되는 공유메모리공간을 지시하는 공유메모리공간 "식별자" 되돌려준다. 만약 입력된 key 값이 지시하는 공유메모리 공간이 이미 존재하고 있다면 존재하는 공유메모리 공간의 "식별자"를 되돌려준다. IPC_EXCL 과 IPC_CREAT 를 같이 사용할경우, 공유메모리 공간이 존재하지 않으면 새로 생성시켜주며, 존재할경우에 error 를 되돌려준다. 

    3번째 아규먼트는 이외에도 권한을 지정해줄수도 있다. 권한은 파일권한과 동일하게, 유저, 그룹, Other 에 대한 읽기/쓰기 권한을 지정할수 있다. 단 실행권한은 줄수 없도록 되어 있다. 아래와 같이 사용가능하다. 
    int shmid; 
    key_t keyval; 
     
    keyval = 1234; 
    shmid = shmget(keyval, 1024, IPC_CREAT | 0666));  
    if (shmid == -1) 
    { 
        return -1; 
    } 
     

    1.4 shmat

    일단 공유메모리 공간을 생성했으면, 우리는 공유메모리에 접근할수 있는 int 형의 "식별자" 를 얻게 된다. 우리는 이 식별자를 shmat 를 이용해서 지금의 프로세스가 공유메모리를 사용가능하도록 "덧붙임" 작업을 해주어야 한다.

    첫번째 아규먼트는 shmget을 이용해서 얻어낸 식별자 번호이며, 
    두번째 아규먼트는 메모리가 붙을 주소를 명시하기 위해 사용하는데, 0을 사용할경우 커널이 메모리가 붙을 주소를 명시하게 된다. 특별한 사항이 없다면 0을 사용하도록 한다.
    세번째 아규먼트를 이용해서, 우리는 해당 공유메모리에 대한 "읽기전용", "읽기/쓰기가능" 모드로 열수 있는데, SHM_RDONLY를 지정할경우 읽기 전용으로, 아무값도 지정하지 않을경우 "읽기/쓰기 가능" 모드로 열리게 된다. 

    1.5 shmdt

    프로세스가 더이상 공유메모리를 사용할필요가 없을경우 프로세스와 공유메모리를 분리 하기 위해서 사용한다. 이 함수를 호출할 경우 단지 현재 프로세스와 공유메모리를 분리시킬뿐이지, 공유메모리 내용을 삭제하지는 않는다는 점을 기억해야 한다. 공유메모리를 커널상에서 삭제 시키길 원한다면 shmctl 같은 함수를 이용해야 한다.

    shmdt 가 성공적으로 수행되면 커널은 shmid_ds 의 내용을 갱신한다. 즉 shm_dtime, shm_lpid, shm_nattch 등의 내용을 갱신하는데, shm_dtime 는 가장 최근에 dettach (즉 shmdt 를 사용한)된 시간, shm_lpid 는 호출한 프로세세의 PID, shm_nattch 는 현재 공유메모리를 사용하는 (shmat 를 이용해서 공유메모리에 붙어있는) 프로세스의 수를 돌려준다. shmdt 를 사용하게 되면 shm_nattch 는 1 감소하게 될것이며, shm_nattch 가 0 즉 더이상 붙어있는 프로세스가 없다라는 뜻이 될것이다. shm_nattch 가 0이 되어있을때 만약 이 공유메모리가 shm_ctl 등에 의해 삭제표시 가 되어 있다면, 이 공유메모리는 삭제되게 된다. 

    1.6 shmctl

    이것은 공유메모리를 제어하기 위해서 사용한다. 즉 shmid_ds 를 직접 제어함으로써, 해당 공유메모리에 대한 소유자, 그룹 등의 허가권을 변경하거나, 공유메모리를 삭제혹은, 공유메모리의 잠금을 설정하거나 해제하는 등의 작업을 한다.

    2번째 아규먼트를 이용해서 shmid 가 가르키는 공유메모리를 제어하며, cmd 를 이용해서 원하는 제어를 할수 있다. cmd 를 이용해 내릴수 있는 명령에는 다음과 같은 것들이 있다.
    • IPC_STAT
      공유메모리 공간에 관한 정보를 가져오기 위해서 사용된다. 정보는 buf 에 저장된다. 
    • IPC_SET
      공유메모리 공간에 대한 사용자권한 변경을 위해서 사용된다. 사용자 권한 변경을 위해서는 슈퍼유저 혹은 사용자권한을 가지고 있어야 한다. 
    • IPC_RMID
      공유메모리 공간을 삭제하기 위해서 사용된다. 이 명령을 사용한다고 해서 곧바로 사용되는건 아니며, 더이상 공유메모리 공간을 사용하는 프로세스가 없을때, 즉 shm_nattch 가 0일때 까지 기다렸다가 삭제된다. 즉 해당 공유메모리 공간에 대해서 삭제표시를 하는거라고 생각하면 된다. 

    다음은 실제로 공유메모리를 사용하는 방법에 대한 가장간단한 예제이다. 자식과 부모프로세스간에 어떻게 메모리가 공유되는지 보여준다.

    예제 : shm.c
    #include <sys/ipc.h>  
    #include <sys/shm.h>  
    #include <string.h>  
    #include <unistd.h>  
     
     
    int main() 
    { 
        int shmid; 
        int pid; 
     
        int *cal_num; 
        void *shared_memory = (void *)0; 
     
     
        // 공유메모리 공간을 만든다. 
        shmid = shmget((key_t)1234, sizeof(int), 0666|IPC_CREAT); 
     
        if (shmid == -1) 
        { 
            perror("shmget failed : "); 
            exit(0); 
        } 
     
        // 공유메모리를 사용하기 위해 프로세스메모리에 붙인다.  
        shared_memory = shmat(shmid, (void *)0, 0); 
        if (shared_memory == (void *)-1) 
        { 
            perror("shmat failed : "); 
            exit(0); 
        } 
     
        cal_num = (int *)shared_memory; 
        pid = fork(); 
        if (pid == 0) 
        { 
            shmid = shmget((key_t)1234, sizeof(int), 0); 
            if (shmid == -1) 
            { 
                perror("shmget failed : "); 
                exit(0); 
            } 
            shared_memory = shmat(shmid, (void *)0, 0666|IPC_CREAT); 
            if (shared_memory == (void *)-1) 
            { 
                perror("shmat failed : "); 
                exit(0); 
            } 
            cal_num = (int *)shared_memory; 
            *cal_num = 1; 
     
            while(1) 
            { 
                *cal_num = *cal_num + 1; 
                printf("child %d\n", *cal_num);  
                sleep(1); 
            } 
        } 
     
        // 부모 프로세스로 공유메모리의 내용을 보여준다.  
        else if(pid > 0) 
        { 
            while(1) 
            { 
                sleep(1); 
                printf("%d\n", *cal_num); 
            } 
        } 
    } 
     
    예제 프로그램이 하는 일은 간단하다. int 형의 공유메모리 공간을 할당한다음. 자식프로세스에서 여기에 1씩을 더하고 부모프로세스에서는 공유메모리 내용을 출력하는 일을한다. 

    2 공유메모리 제어하기

    쉘에서 공유메모리의 상황을 보여주기 위해서 ipcs(1)란 도구를 제공한다. ipcs 를 사용하면 공유메모리 뿐만 아닌, Semaphore, Message Queue 등 소위 sytem V IPC 설비에 대한 내용을 보여준다. 그리고 ipcrm 도구를 이용해서 필요없는 공유메모리, Message Queue, Semaphore 등을 지워줄수 있다.

    위의 예제코드를 컴파일 시켜서 실행시킨다음 ipcs 를 이용해서 확인을 해보면 공유메모리 자원이 어떤식으로 관리되는지 좀더 이해를 쉽게 할수 있을것이다.

    2.1 공유 메모리 정보 확인

    -l 옵션과 함께 ipcs를 실행하면 ipc자원 제한 정보를 확인할 수 있다.
    $ ipcs -l 
    ------ Shared Memory Limits -------- 
    max number of segments = 4096 
    max seg size (kbytes) = 32768 
    max total shared memory (kbytes) = 8388608 
    min seg size (bytes) = 1 
     
    ------ Semaphore Limits -------- 
    max number of arrays = 128 
    max semaphores per array = 250 
    max semaphores system wide = 32000 
    max ops per semop call = 32 
    semaphore max value = 32767 
     
    ------ Messages: Limits -------- 
    max queues system wide = 1706 
    max size of message (bytes) = 8192 
    default max size of queue (bytes) = 16384 
     
    -m 옵션으로 실행하면 현재 사용중인 ipc 자원 정보를 확인할 수 있다.
    $ ipcs -m 
     
    ------ Shared Memory Segments -------- 
    key        shmid      owner      perms      bytes      nattch     status       
    0x00000000 0          root       777        135168     2                        
    0x00000000 819201     yundream   600        393216     2          dest          
    0x00000000 950274     yundream   600        393216     2          dest          
    0x00000000 983043     yundream   600        393216     2          dest          
    0x00000000 917508     yundream   600        393216     2          dest          
    0x00000000 1015813    yundream   600        393216     2          dest          
    0x00000000 1048582    yundream   600        393216     2          dest          
    0x00000000 27590663   yundream   600        393216     2          dest          
    0x00000000 35684360   yundream   666        4343780    2          dest          
    0x00000000 35717129   yundream   666        282808     2          dest   
     

    2.2 /proc 파일 시스템으로 제어하기

    리눅스 운영체제는 /proc 파일 시스템으로 공유 메모리 자원 값을 변경할 수 있다. 
    • /proc/sys/kernel/shmmax : 프로세스가 생성할 수 있는 공유 메모리의 최대 크기
    • /proc/sys/kernel/shmall : 현재 사용중인 공유 메모리 크기
    프로세스가 생성할 수 있는 공유 메모리 크기는 다음과 같이 변경하면 된다.
    // 500M로 
    # echo 536870912 > /proc/sys/kernel/shmmax 
     

    출처 -

    http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/IPC/SharedMemory

    'OS > 리눅스 & 유닉스' 카테고리의 다른 글

    Speaking UNIX: 공유 메모리를 이용한 프로세스 간 통신(IPC)  (0) 2012.06.01
    ibm aix polling  (0) 2012.05.31
    poll을 이용한 채팅서버  (0) 2012.05.24
    AIX 32bit/64bit 컴파일  (0) 2012.05.23
    Compiler 옵션  (0) 2012.05.23
    :

    poll을 이용한 채팅서버

    OS/리눅스 & 유닉스 2012. 5. 24. 18:38

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/poll.h>  //poll
    #include <errno.h>     //errno
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <sys/socket.h>  //socket
    #include <netinet/in.h>    //sockaddr
    #include <netdb.h>        //hostent

    #define PORT 8001
    #define BUFFSIZE 1024  // 송수신을 위한 버퍼크기
    #define MAXPOLL 128   // pollfd의 최대 크기


    // tcp server 
    int main() {
       struct pollfd polls[MAXPOLL];     // pollfd 배열
       int count=0;
       char buff[BUFFSIZE];
       int yes=1;
       int i, poll_result;
       int server_socket, client_socket;
       struct sockaddr_in server_addr, client_addr;
       int server_addrlen=sizeof(server_addr);  //서버소켓주소의 길이
       int client_addrlen=sizeof(client_addr);   //클라이언트 소켓주소의 길이


       // 인터넷 도메인 TCP 서버 소켓 생성
       if ( (server_socket=socket(AF_INET,SOCK_STREAM,0)) < 0 ) {
          perror("socket");
          exit(1);
       }
       
      // 서버 포트번호를 재사용 할 수 있도록 소켓 옵션 설정
       if ( setsockopt(server_socket,  //socket
                     SOL_SOCKET,  // level
                     SO_REUSEADDR,  // option
                     &yes,  //option value
                     sizeof(int)) < 0 ) {  // option length
           perror("setsockopt");
           exit(9);              
       }
       
       //서버의 소켓주소를 생성
       bzero((void *)&server_addr,sizeof(server_addr));
       server_addr.sin_family=AF_INET;
       server_addr.sin_addr.s_addr=INADDR_ANY;
       server_addr.sin_port=htons(PORT);
       
       //소켓과 서버주소 연결 bind()
       
       if ( bind(server_socket,(struct sockaddr *)&server_addr,
               sizeof(server_addr)) < 0) {
            perror("bind");
            exit(2);
       }
       
       //커널에 포트 알림 listen()
       if ( listen(server_socket,5) < 0 ) {
           perror("listen");
           exit(3);
       }
       
       printf("서버가 시작됨: %d\n",PORT);

      // 서버 소켓에 대한 이벤트 등록
       polls[0].fd=server_socket;
       polls[0].events=POLLIN;
       count++;    // 등록된 소켓의 수 
       
       // 서버소켓을 제외한 나머지 pollfd의 디스크립트 값을 -1 로 설정하여 할당되지 않음을 표시
       for(i=1;i<MAXPOLL;i++) polls[i].fd=-1;
       
       // 클라이언트 연결을 처리하기 위해 대기중
        
       while (1) {  
       poll_result=poll(polls,count,1000);
       
      
       if (poll_result == 0 ) continue;  // 타임아웃에 걸린 경우 다시 while loop를 테스트하도록 한다.
       if (polls[0].revents & POLLIN) {  //서버소켓에 이벤트가 발생한 경우
          bzero((void *)&client_addr,sizeof(client_addr));
          if ( (client_socket= accept(server_socket,
                 (struct sockaddr *)&client_addr,&client_addrlen)) < 0) {
              perror("accept");
              exit(4);
          }
          printf("클라이언트가 접속됨:\n");
          printf("IP: %s  PORT: %d\n",inet_ntoa(client_addr.sin_addr), 
                                    ntohs(client_addr.sin_port));
          polls[count].fd=client_socket;      // 새로 연결된 클라이언트 소켓을 pollfd에 등록하여 이벤트를 감시하도록 한다.
          polls[count].events = POLLIN;  
          count++;   
          continue;
       } else {  //클라이언트 소켓에서 이벤트가 발생한 경우              
          for(i=1;i<count;i++) {
             if ( polls[i].revents & POLLIN ) {
               //클라이언트가 전송한 메시지를 buff에 수신함
               bzero(buff,sizeof(buff));
               if ( recv(polls[i].fd, buff,sizeof(buff),0) < 0 ) {
                   perror("recv");
                   break;
               }else 
                 printf("[%d]수신 %s",polls[i].fd, buff);
               //클라이언트가 종료를 요청하면 소켓을 닫아주고
               //pollfd에서 삭제해준다.  
               if ( bcmp(buff,"exit",4) == 0 ) {
                  close(polls[i].fd);
                  polls[i].fd=-1;
                  // polls이사를 시키세요
                  break;
               }
               //클라이언트에게 수신된 메시지 echo
               if ( send(polls[i].fd,buff,strlen(buff),0) < 0) {
                  perror("send");
                  break;
               }else 
                  printf("[%d]송신 %s",polls[i].fd, buff);
             }//if
          }//for
        }//else
       }//while
       close(polls[0].fd);
       exit(0);     
    }


    출처 - http://cafe.naver.com/sunschool/5521

    'OS > 리눅스 & 유닉스' 카테고리의 다른 글

    ibm aix polling  (0) 2012.05.31
    공유 메모리 (shared memory)  (0) 2012.05.29
    AIX 32bit/64bit 컴파일  (0) 2012.05.23
    Compiler 옵션  (0) 2012.05.23
    gcc 옵션 [출처] gcc 옵션 간단 정리|작성자 열이  (0) 2012.05.23
    :

    AIX 32bit/64bit 컴파일

    OS/리눅스 & 유닉스 2012. 5. 23. 17:00

    AIX에서 사용하는 사용하는 컴파일러는 IBM에서 제공하는 xl이라는 Compiler가 쓰인다.


    이 xl은 xlc로도 쓰이며 _r 이 붙게 되면 Thread-Safe의 의미가 된다.

    정확하게 어떤 부분이 Safe한지는 잘 모르겠다.


    xl 씨리즈는 굉장히 많은 종류가 있는데 /usr/vacpp/bin에 보면 C++ 

    그리고 /usr/vac/bin에 보면 C에 해당 하는 컴파일러 종류들을 살펴 볼 수 있겠다.


    -----------------------------------------------------------------------------------------------------------------------------

    Hard Link 

    똑같은 파일이 복사되어 있는 것으로 원복을 삭제해도 복사본의 내용은 남아 있으며 복사본의 내용을 추가하면 원본의 내용에도 추가되는 구조.


    Symbolic Link

    원본의 파일에 대한 위치와 정보만을 갖고 있음.

    -----------------------------------------------------------------------------------------------------------------------------

    위 컴파일러들은 모두 기본적으로 xlc의 Symbolic Link가 걸려 있는데 각자 자신이 호출된 이름에 맞는 동작을 한다고 한다.

    따라서 xlc를 사용한 것과 xlC를 사용하는 부분은 구분해야 한다.



    -----------------------------------------------------------------------------------------------------------------------------

    출처 : http://susukang98.springnote.com/pages/377244.xhtml


    오래된 머신에서는 xlc가 깔려 있다고 한다.

    AIX에서 사용하는 옵션을 살펴보자면 


     Version [kernel 2.x, GCC 2.95.x and later] 4.3 and Later

     2a shared library creation flag -bM:SRE

     2b shared library creation (C++) cxx -shared -o

    xlC -G (or -qmkshrobj)

    obsolete:/usr/vacpp/bin/makeC++SharedLib -G -o

    (was in /usr/ibmcxx/bin or /usr/lpp/xlC/bin/)

     2c static archiver (C++) ar

     3b file extension .so (or .a)

     4 executable link options -brtl

    -bdynamic

    -Lpath -lname

     5a runtime path specification -blibpath:<path>

    (by default, it is set to the arguments of -L)

     5b  Does not build the path for shared libraries into the executable -blibpath:/usr/lib:/lib

     7 exports file/link option

    (see notes) .exp

     9 runtime library path LIBPATH

     11 runtime debugging LDR_CNTRL

     17 dynamic loading /

    dynamic symbol access loadquery/loadbind

    (AIX 4.2) dlopen / dlsym

     18 utilities dump -H

    ldd (in AIX tools)

     19 documentation man: ld, dump



    -----------------------------------------------------------------------------------------------------------------------------


    1. 32bit/64bit 컴파일


    기본적으로 AIX용 컴파일러는 프로그램을 32bit 모드로 컴파일한다. 원하는 bit 모드를 지정하려면 컴파일러, 아카이버, 링키지 에디터 등을 위해 아래와 같이 bit 모드 옵션이나 환경변수를 사용할 수 있다.


    - 컴파일러 옵션: -q32/-q64

    - OBJECT_MODE 환경변수: 32/64

    - Archive(ar) 옵션: -X32/-X64/-X32_64

    - 링키지 에디터(ld) 옵션: -b32/-b64

    아래는 64-bit 오브젝트를 생성하도록 컴파일하고 확인하는 예..

    $ xlf -c -q64 bt.f

    $ dump -ov -X32_64 bt.o

    bt.o:

    ***Object Module Header***

    # Sections Symbol Ptr # Symbols Opt Hdr Len Flags

    4 0x000026fe 81 0 0x0000

    Flags=( )

    Timestamp = "Feb 14 11:18:07 2002"

    Magic = 0x1f7 (64-bit XCOFF)

    위와 같이 dump 명령을 사용하면 실행 파일 또는 오브젝트 파일이 32-bit 모드인지 64-bit 모드인지 확인할 수 있는데, 위의 예에서는 '64-bit XCOFF'로 되어 있으므로 64-bit 오브젝트 파일임을 알 수 있다. 또한, dump 명령의 옵션인 -X32_64, -X32, -X64는 대상 파일의 bit 모드를 지정하는 것으로, 모를 때는 위와 같이 -X32_64를 사용하면 된다.

    AIX 4.3 환경에서 컴파일한 64-bit 프로그램은 AIX 5L에서는 실행되지 않으므로 AIX 5L용으로 다시 컴파일 해주어야 하지만, 32-bit 프로그램은 AIX5L 이전의 버전에서 컴파일 된 실행파일을 그대로 사용할 수 있다.

    운영체제의 32/64-bit 모드는 'ls -l /unix' 명령으로 확인할 수 있는데 /unix가 /usr/lib/boot/unix_64에 링크되어 있으면 64-bit 커널이고, /usr/lib/boot/unix_mp에 링크되어 있으면 32-bit 커널이다.

    xlC는 주석을 엄밀히 확인하고 일반적인 c style의 comment에 대해 warnning하는 경우가 있다.


    그리고 char type을 default로 unsigned값으로 보고 있다. char타입에 대해 멀티플렛폼으로 작성할 여부가 있는 프로그램의 경우 변수의 type을 char보다는 __uint8같은 명확한 type으로 명확히 정의함이 괜찮은 방법이다.


    "sn3crypt.h", line 338.18: 1540-0804 (W) The characters "/*" are detected in a comment.

    "sn3ole.h", line 189.73: 1540-0804 (W) The characters "/*" are detected in a comment.

    "sn3pcm.h", line 70.17: 1540-0848 (S) The macro name "SN3_ARCHITECTURE" is already defined with a different definition.

    "sn3pcm.h", line 54.17: 1540-0425 (I) "SN3_ARCHITECTURE" is defined on line 54 of "sn3pcm.h".

    "sn3xls.h", line 51.49: 1540-0804 (W) The characters "/*" are detected in a comment.

    make: 1254-004 The error code from the last command is


    aix5, xlC컴파일러에서 next를 실행한 오류

    컴파일을 하다가 난 warnning과 error이다. 여러 플렛폼에 있는 다양한 컴파일러로 컴파일을 할때 한번에 되지 않느다면 역시 컴파일러의 옵션을 봐야 한다. 여러종류의 컴파일러를 만났을 때 당황하지 말고 믿을 수 있는 것은 컴파일러에서 생성한 경고나 오류, 그리고 컴파일러 옵션임을 잊지 않는다.

    위의 경우는 (W)는 warnning (S)에서 에러를 냈으므로 70 line을 찾는다.


    출처 - http://jangpd007.tistory.com/37

    :

    Compiler 옵션

    OS/리눅스 & 유닉스 2012. 5. 23. 15:01

    C언어의 여러가지 컴파일러에 대해서 알아봅시다.

     

    Compiler의 종류에 따른 옵션

     

    아래 사이트에 정말 잘 정리 되어 있다.

     

    LinuxSolarisHP-UXCompaq (Digital) Tru64AIXSGIWin32MacOS XVMSOS/390
    Version[kernel 2.x, GCC 2.95.x and later][2.8][11][4.x][4.3 and later][Irix 6.5][NT 4][10.x][unmaintained][unmaintained]
    1compiler flag (position independent code)-fPIC-KPIC, -Kpic+z/+Zxxxxxxxx-KPIC (default)xxxx-fno-commonxxxx-DLL
    2ashared library creation flag-shared-G-b-shared-bM:SRE-sharedlink /DLLcc -bundle, -dynamiclib
    libtool -dynamic
    /SHARExxxx
    2bshared library creation (C++)g++ -fPIC -shared -oCC -G -oaCC -b -ocxx -shared -oxlC -G (or -qmkshrobj)
    obsolete:/usr/vacpp/bin/makeC++SharedLib -G -o
    (was in /usr/ibmcxx/bin or /usr/lpp/xlC/bin/)
    CC -shared -oCL -LD -Fexxx.DLL??
    libtool -dynamic
    ????
    2cstatic archiver (C++)arCC -xar -oarararCC -ar -oLIB (avoid link /lib that creates __impl symbols)??
    libtool -static
    ????
    3alibrary name(ld option) -soname name-h name(ld option) +h name(ld option) -soname namexxxx(ld option) -soname name/OUT:name-compatibility_version, -current_version/SHARE=xxxx
    3bfile extension.so.so.sl.so.so (or .a).so.DLL.dylib.EXE??
    4executable link options-Bdynamic
    -Lpath -lname
    -Bdynamic
    -Lpath -lname
    -a,shared
    -Lpath -name
    -no_archive
    -Lpath -lname
    -brtl
    -bdynamic
    -Lpath -lname
    -Bdynamic,
    -Lpath -lname
    Controlled by .lib files??xxxxfilename/SHAREABLE
    5aruntime path specification-rpath <path>-R <pathlist>-Wl,+b <pathlist> -Wl,+s-rpath <path>-blibpath:<path>
    (by default, it is set to the arguments of -L)
    -rpath <path>/LIBPATH:-install_name <path>xxxxxxxx
    5bDoes not build the path for shared libraries into the executabledefault-norunpathchatr +b disable <file>default-blibpath:/usr/lib:/libdefault????????
    6controlling symbolsusing a script file (see "info ld")-z defs/ nodefs/ muldefs+v[no] shlibunsats-expect [error] _unresolvedxxxx-ignore[no] _unresolved/FORCE: MULTIPLE [UNRESOLVED]??xxxxxxxx
    LinuxSolarisHP-UXCompaq (Digital) Tru64AIXSGIWin32MacOS XVMSOS/390
    7exports file/link option
    (see notes)
    using a script file (see "info ld")using a script file (see "man ld")+e,symname-exported_symbol.exp-exported_symbol
    -exports_file filename
    .def/__declspec(dllexport)-exported_symbols_list/SYMBOL_TABLE=(...).x file/
    #pragma export()
    8hiding symbolsusing a script file: VERSION command (see "Version Script" section in "info ld")using a script file (see "man ld") (see thisarticle)-h symname-hidden_symbol, -hiddenxxxx-hidden_symbol,
    -hiddens_file filename
    using a .def file-unexported_symbols_listxxxxxxxx
    9runtime library pathLD_LIBRARY_PATHLD_LIBRARY_PATH
    LD_LIBRARY_PATH_64
    SHLIB_PATH
    LD_LIBRARY_PATH (64 bits)
    LD_LIBRARY_PATHLIBPATHLD_LIBRARY_PATH
    LD_LIBRARYN32_PATH
    LD_LIBRARY64_PATH
    . and then PATHDYLD_LIBRARY_PATH
    DYLD_FALLBACK_LIBRARY_PATH
    SYS$SHARELIBPATH
    10symbol binding-Bsymbolic-Bsymbolic-Bsymbolic /immediate /deferredxxxxxxxx-Bsymbolicxxxx??xxxxxxxx
    11runtime debugging(c.f. man ld.so)
    LD_BIND_NOW
    LD_TRACE_LOADED_OBJECTS
    LD_DEBUG=help
    ltrace
    (c.f. man ld.so.1)
    LD_BIND_NOW
    LD_DEBUG
    ld -D help
    (c.f. man dld.sl)
    _HP_DLDOPTS
    (c.f. man loader)
    LD_BIND_NOW
    _RLD_ARGS
    LDR_CNTRL(c.f. man rld)
    LD_BIND_NOW
    _RLD_PATH, _RLD_ARGS
    xxxxDYLD_BIND_AT_LAUNCH
    DYLD_PRINT_LIBRARIES
    DYLD_PREBIND_DEBUG
    xxxxxxxx
    12runtime preloadLD_PRELOADLD_PRELOADLD_PRELOAD (probably HP-UX >= 11)_RLD_LIST "xxx.so:DEFAULT"xxxx_RLD_LIST "xxx.so:DEFAULT"??DYLD_INSERT_LIBRARIES????
    13loaderld.sold.sodld.slloaderxxxxrldxxxxdyldxxxxxxxx
    14runtime performancexxxxxxxxfastbind tool-quickstart_infoxxxx-quickstart_info/DELAYLOADxxxxxxxxxxxx
    LinuxSolarisHP-UXCompaq (Digital) Tru64AIXSGIWin32MacOS XVMSOS/390
    15versioningusing a script file: VERSION command (see "info ld")using a script file (see "man ld")
    -M mapfile
    xxxxxxxxxxxx-set_version/-exact_version/
    -ignore_version
    /VERSION:major.minor??/GSMATCH=xxxx
    16entry pointsxxxxxxxxxxxxxxxxxxxxxxxxDllMainxxxxxxxxxxxx
    17dynamic loading /
    dynamic symbol access
    dlopen / dlsymdlopen / dlsymshl_load / shl_findsym
    (64 bits) dlopen / dlsym
    dlopen / dlsymloadquery/loadbind
    (AIX 4.2) dlopen / dlsym
    dlopen / dlsymLoadLibrary / GetProcAddress"man 3 dyld"lib$find_image_symboldllload / dllqueryfn/ dllqueryvar
    18utilitiesldd, ldconfig, objdump, ltrace, readelfldd, elfdump, pvs, dumpstabs (compilers V.6), crle (Solaris>=8)chatr
    odump
    ldd, elfdump (HP-UX >= 11)
    odump -Dldump -H
    ldd (in AIX tools)
    elfdump -Dl
    pldd
    ldd (IRIX >= 6.5)
    DUMPBIN, depends, EXEHDR, link -dumpotoolANALYSE/IMAGExxxx
    19documentationman: ld, ld.so, ldd
    info: ld, gcc, binutil
    HOWTO: ELF-HOWTO, GCC-HOWTO
    man: ld, ld.so.1, ldd, pvsman: ld, dld.sl, chatrman: ld, loader, odumpman: ld, dumpman: dso, ld, rld, elfdump, lddMSDNman: ld, libtool, dyld, otool????
    LinuxSolarisHP-UXCompaq (Digital) Tru64AIXSGIWin32MacOS XVMSOS/390

     

    http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html

     

    GCC Simple Option

     

    -Idir 옵션

    dir은 디렉토리 이름 이고 -I와 디렉토리 이름을 붙이면 라이브러리를 찾아준다.

     

    -Dmacro

    Ex) -DMAXLEN=255 = #define MAXLEN 255

     

    -M Makefile파일을 만들 때 파일 컴파일 규칙을 stdout에 찍어준다.

     

    -Wall 모든 경고 메시지를 출력하도록 한다.

     

    -O -O2 -O3 최적화 모드 뒤로 갈수록 더 빠르게 많은 옵션으로 최적화를 한다.

     

    -p, -pg 프로파일링

    gcc -pg -o main main.cpp

    gprof hello gmon.out

     

    ar -t /usr/lib/libc.a

    ar -r libsample.a main.o

     

    GCC Compile 과정

     

    1. preprocessing

      • gcc -E main.cpp
    2. compling

      • cc1
      • cc1plus
    3. Assembling

      • gcc -S main.cpp
      • gcc -c main.cpp 링크를 하지말고 object 코드까지만 만들어라 하는 옵션
    4. Linking

      • -static dynamic linking을 지원하고 있는 시스템에서 static linking을 하라는 옵션

     

    우선 내가 요세 쓰고 있는 컴파일러 종류

     

    • visual studio 6,7,8의 컴파일러 CL.exe
    • gnu compiler gcc, g++
    • xlC compiler
    • aCC compiler

     

    새로 알게 된 xlC에 대해서 스크랩

     

    1. 32bit/64bit 컴파일

     

    기본적으로 AIX용 컴파일러는 프로그램을 32bit 모드로 컴파일한다. 원하는 bit 모드를 지정하려면 컴파일러, 아카이버, 링키지 에디터 등을 위해 아래와 같이 bit 모드 옵션이나 환경변수를 사용할 수 있다.

    - 컴파일러 옵션: -q32/-q64

    - OBJECT_MODE 환경변수: 32/64

    - Archive(ar) 옵션: -X32/-X64/-X32_64

       - 링키지 에디터(ld) 옵션: -b32/-b64

    아래는 64-bit 오브젝트를 생성하도록 컴파일하고 확인하는 예..

     

    1. $ xlf -c -q64 bt.f
    2. $ dump -ov -X32_64 bt.o
    3. bt.o:
    4.                         ***Object Module Header***
    5. # Sections      Symbol Ptr      # Symbols       Opt Hdr Len     Flags
    6.          4      0x000026fe             81                    0     0x0000
    7. Flags=( )
    8. Timestamp = "Feb 14 11:18:07 2002"
    9. Magic = 0x1f7  (64-bit XCOFF)

     

     위와 같이 dump 명령을 사용하면 실행 파일 또는 오브젝트 파일이 32-bit 모드인지 64-bit 모드인지 확인할 수 있는데, 위의 예에서는 '64-bit XCOFF'로 되어 있으므로 64-bit 오브젝트 파일임을 알 수 있다. 또한, dump 명령의 옵션인 -X32_64, -X32, -X64는 대상 파일의 bit 모드를 지정하는 것으로, 모를 때는 위와 같이 -X32_64를 사용하면 된다.

     

     AIX 4.3 환경에서 컴파일한 64-bit 프로그램은 AIX 5L에서는 실행되지 않으므로 AIX 5L용으로 다시 컴파일 해주어야 하지만, 32-bit 프로그램은 AIX5L 이전의 버전에서 컴파일 된 실행파일을 그대로 사용할 수 있다.

    운영체제의 32/64-bit 모드는 'ls -l /unix' 명령으로 확인할 수 있는데 /unix가 /usr/lib/boot/unix_64에 링크되어 있으면 64-bit 커널이고, /usr/lib/boot/unix_mp에 링크되어 있으면 32-bit 커널이다.

     

     xlC 에 대해 알게 된것들

     

     xlC는 주석을 엄밀히 확인하고 일반적인 c style의 comment에 대해 warnning하는 경우가 있다.

    그리고 char type을 default로 unsigned값으로 보고 있다. char타입에 대해 멀티플렛폼으로 작성할 여부가 있는 프로그램의 경우 변수의 type을 char보다는 __uint8같은 명확한 type으로 명확히 정의함이 괜찮은 방법이다.

     

     

    1. "sn3crypt.h", line 338.18: 1540-0804 (W) The characters "/*" are detected in a comment.
      "sn3ole.h", line 189.73: 1540-0804 (W) The characters "/*" are detected in a comment.
      "sn3pcm.h", line 70.17: 1540-0848 (S) The macro name "SN3_ARCHITECTURE" is already defined with a different definition.
      "sn3pcm.h", line 54.17: 1540-0425 (I) "SN3_ARCHITECTURE" is defined on line 54 of "sn3pcm.h".
      "sn3xls.h", line 51.49: 1540-0804 (W) The characters "/*" are detected in a comment.
      make: 1254-004 The error code from the last command is

     

    aix5, xlC컴파일러에서 next를 실행한 오류

     

     컴파일을 하다가 난 warnning과 error이다. 여러 플렛폼에 있는 다양한 컴파일러로 컴파일을 할때 한번에 되지 않느다면 역시 컴파일러의 옵션을 봐야 한다. 여러종류의 컴파일러를 만났을 때 당황하지 말고 믿을 수 있는 것은 컴파일러에서 생성한 경고나 오류, 그리고 컴파일러 옵션임을 잊지 않는다.

     

    위의 경우는 (W)는 warnning (S)에서 에러를 냈으므로 70 line을 찾는다.

     

    aCC compiler

     

    • case 1: using namespace std

     

    1. Error (future) 600: "sn3ole.h", line 313 # Type specifier is omitted; "int" is no longer assumed.
          typedef map<int, int> SN3OLE_DIR_LIST; /*< PPS Directory Entry L
          ^^^^^^^

     using namespace std;를 정의해야 하는데 하지 않은 경우

     

     어떤 버전의 컴파일러는 using namespace std가 있어야 하고 어떤 컴파일러는 없어야 한다.

    왜그럴까~? -_-;;; 만든사람 마음이다. 다음에 빌드할때는 man을 보고 옵션을 좀 따라가봐야 할듯

     

     

    • case 2: 시스템 아키텍쳐의 디파인

     

    아이테니엄 64비트 컴퓨터의 경우

     defined(__ia64) || defined(__ia64__) || defined(_IA64) || defined(__IA64__) || defined(_M_IA64) 의 메크로가 컴파일러 단에서 미리 정의되어 있다. 혹시 시스템이 아이테니엄 64비트 이지만 아키텍쳐의 정의를 타지 않는 경우 컴파일러에서 제공하는 메크로의 명칭이 무엇인지 확인해봐야 할 경우가 있다. 이런 경우는 해당 컴파일러의 헬프를 보면서 찾아보자.

     

    1. man aCC

     컴파일러의 헬프를 알아보자

     

     

     

     

    gcc compiler option

     

    g++ compiler 옵션에 대해서 알아봅시다.

     

    make (Makefile)

    g++ -g debugging mode로 빌드

     

    gdb linux debugger

    gdb - 링크를 따라가자

     

    CL.exe compiler

     

    Visual Studio에서 빌드자동배치파일을 만들려면..

    • Visual Studio 6.0을 열어서 project - export makefile을 선택하여 makefile을 export한다.
    • 다른 환경으로 옮길때는 CL.exe Path를 잡아준다.
    • Clean에 몇몇 파일을 추가한다.
    • 아래 링크에 필요한 Library를 추가한다.

     

    Debug Mode vs Release Mode

     

    • Pointer - 초기화 되지 않은 포인터의 경우 디버그모드에서는 임의값 0xCD로 초기화를 수행하지만 릴리즈에서는 초기화를 수행하지 않는다. 디버그 모드에서 컴파일러 옵션을 조정하여 초기화 하지 않은 포인터 변수를 사용하는것을 예방 할 수 있다. /GZ 컴파일러 옵션은 기본적으로 VC++ 프로젝트 셋팅에서 기본값이 아니므로 필요하다면 추가해서 초기화 되지 않는 포인터의 값을 0xCC로 채우도록 해줘야 한다. /GZ 컴파일러 옵션의 가장 큰 목적은 초기화 하지 않은 메모리 변수의 값을 0xCCCCCCCC로 채워서 디버깅중에 개발자가 초기화 하지 않은 값임을 알 수 있도록 하는것이다.
      디버그모드에서 deallocator에 의해 해제된 메모리에 채워지는 값은 0xDD 이다.

     

    • Heap - 디버그 모드에서 힙영역에 메모리를 할당하게 되면 guard byte( 0xFD로 초기화 )를 추가적으로 할당하여 가장 흔하게 범하는 zero-base의 arrary 인덱스를 잘못 계산하여 경계를 벗어난 영역을 접근하거나 지우려고 하는 코드를 작성했다고 하더라도 디버그 모드에서는 크래쉬가 발생하지 않을 수 있다. 이런 잠재적인 버그를 가진 프로그램을 릴리즈 모드에서 실행시키게 되면 크래쉬가 발생하게 된다.

     

    • ASSERT - ASSERT 구문은 디버그 모드에서는 공백으로 대체되어지는 점을 잊고 아래와 같이 쓰게 되는 경우 이 코드는 릴리즈에서는 공백으로 대체되어 실제로 체크를 할 수 없게 된다. ASSERT (OpenMyWindow () != NULL);
      위의 코드를 다음과 같이 바꾸게 되면 디버그와 릴리즈 모드 모두 정상적으로 동작하게 된다. hWND = OpenMyWindow();
      ASSERT (hWND != NULL);
      이런 체크를 릴리즈에서도 하고 싶다면 VERITY 매크로를 사용하도록 하자.
      Prototypes 사용자 정의 메시지 처리를 위해 ON_MESSAGE 매크로를 사용중이라면 메시지 핸들러의 원형을 요구하는 타입에 정확하게 맞게 선언해줘야 한다. 디버그 모드에서는 원형에 일치하지 않더라도 컴파일러가 수정하여 동작하도록 만들어줘 버그를 발견하기 힘들게 만든다. afx_msg LRESULT <class>::OnMyMessage (WPARAM wParam, LPARAM lParam);
      Optimization Max Speed 옵션은 속도 최적화에 촛점을 맞추고 있기 때문에 최적화 과정에서 안전하지 않을수 있다. 기본적으로 릴리즈 모드에서는 Maximize Speed 옵션이 기본이지만 안전하게 속도 최적화를 보장하는 Minimize Size를 추천한다. 그리고, #pragma 지시자를 사용하여 특정 영역의 옵션을 설정 할 수 있다는것을 기억하자. #pragma optimize("", off)
      // some code here #pragma optimize("", on)

    :

    gcc 옵션 [출처] gcc 옵션 간단 정리|작성자 열이

    OS/리눅스 & 유닉스 2012. 5. 23. 14:51

    옵 션

     기 능

     -E

     전처리만 실행하며 컴파일이나 어셈블 하지 않음. Macro 함수가 잘 이해가 안갈 경우 유용하다.

     -S

     컴파일만 실행하며 어셈블이나 링크 하지 않음. 어셈블리 코드를 확인 할 수 있다.

    -c

     컴파일과 어셈블하며 링크하지 않음. 링킹 에러 또는 컴파일 에러를 확인 할 수 있다.

     -g

     운영체제 고유의 형식으로 디버깅 정보를 만듬

     -o <파일명>

     출력을 <파일명>에 함.

     -I(대문자아이)<디렉토리>

     헤더 파일을 검색할 디렉터리를 추가함.

     -l(소문자엘)<라이브러리명>

     <라이브러리명>으로 지정한 라이브러리를 링크 시 추가함.

     -L<디렉토리>

     -l에 의해 지정된 라이브러리를 검색할 디렉터리를 추가함.

     -D

     매크로를 지정함.

     -O

     최적화 수준을 지정함. (0 <= level <= 3)

     -Wall

     gcc가 제공할 수 있고, 일반적으로 유용한 모든 경고 메시지 출력

     -v

     실행 명령어들과 버전을 출력.

     

    출처 - http://nix102guri.blog.me/90101605464

    [출처] gcc 옵션 간단 정리|작성자 열이

    :

    pthread 함수 간단한 요약 정리

    OS/리눅스 & 유닉스 2012. 5. 21. 20:52

    • pthread_create : 쓰레드 생성(생성과 동시에 실행)
    • pthread_join : 해당 쓰레드가 종료 할때까지 대기함( 자식 프로세스를 기다리는 wait와 비슷), pthread_detach 가 호출 되지 않았다면 쓰레드는 종료후 pthread_join이 명시적으로 불릴때까지 자신의 자원을 해제하지 않는다.
    • pthread_detach : pthread_create 를 통해 생성된 쓰레드를 떼어낸다. pthread_detach 가 호출되면 더이상 pthread_join은 사용될수 없다. 사용할시 오류반환한다. detach 된 쓰레드는 종료시 바로 자원을 해제한다.
    • pthread_exit : 현재 실행중인 쓰레드를 종료시키고자 할때 사용한다. 만약 pthread_cleanup_push 가 정의 되었다면 pthread_exit 가 호출될 경우 cleanup handler 가 호출된다. (우아한 종료를 위해서) 허나 쓰레드의 강제 종료는 자원이 공유되는 쓰레드의 성질상 매우 위험한 행위이다.
    • pthread_cleanp_push : cleanup hanlders 를 인스톨하기 위해서 사용된다 . pthread_exit 가 호출되어서 쓰레드가 종료될때 pthread_cleanup_push 로 인스톨된 함수가 자동 호출된다.쓰레드의 강제 종료시 문제가 될수 있는 사항(할당된 자원을 해제)을 이를 이용해 처리해줄 수 있다.
    • pthread_cleanup_pop : 인스톨된 cleanup handler 를 제거한다.
    • pthread_self : 현재 쓰래드의 쓰레드 식별자를 되돌려준다. (자바의 Thread.currentThread() 와 동일)

     

    • pthread_mutex_init : 뮤텍스 초기화
    • pthread_mutex_destory : 뮤텍스 제거
    • pthread_mutex_lock : 뮤텍스 잠금 요청(락되어있다면 락이 풀릴때까지 대기, 락이 않되었다면 락을하고 임계영역 진입)
    • pthread_mutex_unlock : 뮤텍스 잠금을 해제(락을 푼다.)
    • pthread_cond_init : 조건변수/상태변수를 초기화 (조건변수/상태변수는 윈도우의 이벤트 오브젝트와 같은것이다.)
    • pthread_cond_signal : 조건변수/상태변수에 시그날을 보낸다. 시그날을 보낼 경우 cond르 기다리는 쓰레드가 있다면 쓰레드를 깨운다. (java의 notify() 함수 같은것) 여러개의 쓰레드가 존재한다면 임의의 쓰레드를 깨운다.
    • pthread_cond_boradcast : 조건변수/상태변수를 기다리는 모든 쓰레드에게 신호를 보낸다. (java의 notifyAll() 함수와 같다.)
    • pthread_cond_wait : 조건변수/상태변수 에 시그날이 도착할때까지 대기한다 시그날이 도착하면 블락에서 풀린다. pthread_cond_wait는 블럭 되기 전에 mutex잠금을 자동으로 되돌려주고 시그날에 의해 블럭이 풀리면 mutex를 잠근다.
    • pthread_cond_timedwait : 조건변수/상태변수 에 시그날이 도착할때까지 abstime(절대시간) 동안 대기한다. 시그날이 전달될때까지 블럭되며 자동으로 mutex를 돌려준다.
      중요!! abstime 이란 절대시간이다. 예를 들어 10분만 기달려가 아니라 9시10분까지 기달려란 의미다.
    •  pthread_cond_destroy : pthread_cond_destory 를 통해 생성한 조건변수/상태변수 에 대한 자원을 해제한다.

    중요 : 쓰레드 조건/상태변수는 원자성을 보장하지 않으므로 뮤텍스와 같이 임계영역내에서 사용되어야 한다.

    • pthread_attr_init : thread attribute 객체를 디폴트 값으로 초기화
    • pthread_attr_destory : thread attribute 객체를 제거
    • pthread_attr_getscope : 쓰레드가 어떤영역(커널/유저) 에서 다루어지고 있는지를 얻어오기 위해서 사용된다. PTHREAD_SCOPE_SYSTEM 은 유저모드 쓰레드이고 PTHREAD_SCOPE_PROCESS 는 커널모드 쓰레드이다. 리눅스의 경우는 유저모드 쓰레드만 있다.
    • pthread_attr_setscope : 쓰레드가 어떤 영역(커널/유저)에서 작동하게 할것인지 결정하기 위해서 사용한다. 리눅스의 경우 커널 모드 쓰레드가 지원하지 않는다.
    • pthread_attr_getdetachstate : 쓰레드가 join 가능한 상태인지 아닌지를 알아보는 함수이다.
    • pthread_attr_setdetachstate : 쓰레드가 join 가능하게 아니면 detach 상태로 만들기 위한 함수이다.

     

    • pthread_sigmask : 쓰레드에서 시그널은 서로 공유된다. 그런 이유로 만약 프로세스에 시그널이 전달되면 프로세스가 생성한 모든 쓰레드로 시그널이 전달된다. 그러나 특정 쓰레드만 시그널을 받도록 하고 싶을때 이 함수를 이용한다.
    • pthread_kill : 쓰레드 식별번호로 signal 를 전달한다. (kill 과 같다.)
    • sigwait : 시그날 전달을 동기적으로 기다린다.

     

    • pthread_cancel : 쓰레드 취소(종료)
    • pthread_setcancelstate : 쓰레드를 취소 가능하게 할지 안할지 결정
    • pthread_getcancelstate : 쓰레드가 취소 가능한지 안 한지 반환

     

    • pthread_key_create : 쓰레드 개별 공간(thread specific data) 영역 만들기.
    • pthread_setspecific : TSD(thread specific data) 영역에 값쓰기
    • pthread_getspecific : TSD 영역에 값 읽기
    • pthread_delete : TSD 영역 없애기 (키 없애기)


    쓰레드는 부모 프로세스가 시그널을 받게 되면 그 시그널은 쓰레드전체에게 영향을 준다. 그리고 pthread 함수는 대부분 시그널에 안전하지 않다. 따라서 쓰레드가 시그널을 처리하지않도록 쓰레드 생성시 pthread_sigmask 함수를 이용하여 시그널을 블락시키는 것이 좋다. 


    출처 - http://onecellboy.tistory.com/179



    pthread API (IBM 문서) : http://publib.boulder.ibm.com/iseries/v5r2/ic2924/index.htm?info/apis/users_35.htm


    자세한 pthread api 관련 : www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Thread
       
    pthread_cond_timedwait 예제 : http://blog.daum.net/_blog/BlogTypeView.do?blogid=0Idq4&articleno=6301190#ajax_history_home
      
    pthread 기본 함수들 예제 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Thread/Beginning/PthreadApiReference

    Thread safety (쓰레드에 안전한 함수)
    http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/concurrency_parallel

    조건변수 와 TSD (thread specific data)http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Thread/Advanced/DeepThread#AEN23

    조건변수 예제
    http://www.joinc.co.kr/modules/moniwiki/wiki.php/man/3/pthread_cond_wait?cx=002661009167463862046%3A8oq6cxlfibu&cof=FORID%3A9&q=pthread_cond_wait&sa=Search&ie=EUC-KR#1257

    :

    스레드 조건 변수

    OS/리눅스 & 유닉스 2012. 5. 21. 20:50

    스레드 조건 변수

    - 뮤텍스의 경우, 내부에서 조건에 따른 lock과 unlock을 수행하기 어려움. 다른 스레드가 언제 뮤텍스를 해제했는지 시점 확인도 어려움.-> 이를 조건에 따라 뮤텍스를 잠그고 해제 하고 시그널을 이용해서 뮤텍스가 해제되었음을 다른 스레드에게 알릴 수 있다면?

    - pthread_cond: 시그널을 발생시키거나 시그널을 기다리는 기능 제공. (주의: 여기서 언급하는 시그널은 프로세스가 사용하는 일반적인 의미와는 다름)


    - 초기화

    pthread_cond_t condT;

    pthread_cond_t condT = PTHREAD_COND_INITIALIZER;

    pthread_cond_init(&condT);


    - (해당 스레드 조건변수에게)시그널을 보내는 함수

    int ptrhead_cond_signal(pthread_cond_t* condT); // 해당되는 스레드에게만 시그널 전송

    int pthread_cond_broadcast(pthread_cond_t* condT); // 모든 스레드에게 시그널을 전송

    : 수신 대기중인 스레드가 없을 경우, 시그널은 무시됨


    - 전송될 시그널을 대기하는 함수

    pthread_cond_wait(pthread_cond_t* , pthread_mutex_t* );

    pthread_cond_timedwait(pthread_cond_t*, pthread_mutex_t*, const struct timespec*);

    :조건 변수와 뮤텍스 모두를 인수로 사용. 즉, 뮤텍스가 초기화되어 있고 lock이 되어있어야 함. wait()함수가 실행되면 함수는 내부에 사용된 뮤텍스의 lock을 해제한채로 대기작업에 들어감.그리고 조건 변수에 대한 시그널이 검출되면 작업을 재개.(즉, 해당조건변수에 대한 시그널이 발생하면 내부의 인수로 지정된 뮤텍스를 잠근 후,  wait()함수의 다음라인을 실행)


    - 조건 변수 제거

    int prhead_cond_destroy(pthread_cond_t*);


    소스 : http://pds18.egloos.com/pds/201103/05/62/e0036962_4d721a5839bc2.png



    출처 - http://avata007.egloos.com/2724595

    :

    pthread_cond_timedwait()를 사용한 이벤트 동기화 사용 예제

    OS/리눅스 & 유닉스 2012. 5. 21. 20:49

    다음 예제는 유닉스환경에서 서로 다른 쓰레드 사이에서 이벤트를 동기화 시킬 때 유용하게 사용되어질 수 있는

    pthread_cond_timedwait()에 대한 사용 예제입니다.

    #include <pthread.h>
    #include <stdio.h>
    #include <time.h>
    #include <sys/time.h>
    
    pthread_cond_t g_condition;  // 조건변수는 단지 상태정보만을 알리기 위해서 사용되며, (※ 조건변수는 Lock 기능 없음.)
    pthread_mutex_t g_mutex;     // 상태정보를 원자적(atomic)으로 주고받기 위해서는 뮤텍스와 함께 사용해야 한다.
    
    void *ThWaitSig()
    {
        struct timeval! now;
        struct timespec ts;
    
        printf("start ThWaitSig..\n");
        gettimeofday(&now, NULL);
        ts.tv_sec = now.tv_sec + 5;
        ts.tv_nsec = now.tv_usec * 1000;
    
        pthread_mutex_lock(&g_mutex);
        printf("now waiting wakeup signal about 5 sec..\n");
        pthread_cond_timedwait(&g_condition, &g_mutex, &ts);
        printf("ok, i'm wakeup..\n");
        pthread_mutex_unlock(&g_mutex);
        return NULL;
    }
    
    int main(int argc, char **argv)
    {
        pthread_t thread;
    
        pthread_mutex_init(&g_mutex, NULL);
        pthread_cond_init(&g_condition, NULL);
    
        pthread_create(&thread, NULL, ThWaitSig, NULL);
    
        pthread_mutex_lock(&g_mutex);
        sleep(1);
        printf("now send wakeup signal.. \n");
        pthread_cond_signal(&g_condition);
        pthread_mutex_unlock(&g_mutex);
    
        pthread_join(thread, NULL);
        printf("thread's working is done.\n");
    
        pthread_cond_destroy(&g_condition);
        pthread_mutex_destroy(&g_mutex);
        return 0;
    }
    결과 : http://cfs8.blog.daum.net/image/33/blog/2008/08/26/14/53/48b39a4e7bf98
    출처 - http://blog.daum.net/aswip/6301190

    'OS > 리눅스 & 유닉스' 카테고리의 다른 글

    pthread 함수 간단한 요약 정리  (0) 2012.05.21
    스레드 조건 변수  (0) 2012.05.21
    pthread  (0) 2012.05.21
    정적 라이브러리, 공유 라이브러리 (3)  (0) 2012.05.18
    정적 라이브러리, 공유 라이브러리 (2)  (0) 2012.05.18
    :

    pthread

    OS/리눅스 & 유닉스 2012. 5. 21. 20:07

    스레드는 컴퓨터 프로그래밍에서 프로세스 내부의 작업 수행 단위를 말한다. 실제로 스레드란 개념을 설명하는 것은 쉽지 않지만 개념적으로 간단히 살펴보자. 프로그래밍에서 작업 단위는 보통 프로세스를 기본으로 삼는다. 각각의 프로세스는 보통 서로 다른 프로세스에 관여하지 않는다. 이는 일반적으로 별로 문제가 되지 않는 부분이며 어떻게 보면 상당히 잘 설계되어진 모델이다. 실제로 사용자가 스케줄링을 하기보다는 커널 레벨에서 스케줄링 되어 쓰인다. 하지만 멀티 프로세스 작업 중에는 프로세스 사이의 통신 문제와 프로세스가 스케줄링 될 때 사용중인 메모리의 간섭, 문맥교환시의 시간 지연 등 여러 문제가 생긴다.
    쉽게 말해 스레드는 프로세스 안에서 작업을 더 세분화하여, 스케줄링 된 프로세스 시간을 좀 더 효율적으로 사용하도록 한다. 물론 같은 프로세스 안에서의 작업이므로 문맥 교환이나 스레드 사이의 통신문제는 보다 쉽게 이루어 질 수 있다.
    스레드라는 것은 앞서 언급했듯 다중 환경에서 보다 효율적인 서비스를 제공하기 위한 메커니즘으로 커널 레벨에서 수행되고 있다. 하지만 스레드를 쓰려면 동기화 작업이 필요하다. 동기화가 이루어지지 않은 스레드화는 데이터 유지의 보장성을 책임지지 않기 때문에 잘못된 데이터로 인한 시스템의 치명적인 오류가 발생할 수 있다.

    스레드의 구성 요소
    스레드의 구성 요소로 스레드 함수를 수행하기 위한 생성자와 소멸자, 스레드 사이에 공유되는 데이터의 동기화를 위해 Mutex라는 락(Lock)을 걸기 위한 전역 변수가 있다. 그리고 데이터의 변경을 위해 선행되어야만 하는 조건으로 조건 변수(Condition Variable)가 존재한다.
    스레드를 쓰려면 먼저 스레드를 생성시키고 스레드간에 공유되는 데이터가 있는 경우에는 반드시 스레드로 수행되는 함수의 선행부에 Mutex를 초기화해야 한다. 값의 앞부분에는 락을 걸어야 하고 데이터의 값을 변경한 후 다른 스레드가 쓸 수 있도록 락을 풀어야 한다.
    마지막으로 스레드간에 공유되는 데이터 중 선행 조건이 만족된 이후에만 데이터의 변경이 이루어지도록 하기 위해 조건 변수를 이용한다. 조건 변수의 사용법은 Mutex와 조건 변수를 함께 초기화해야 하고 데이터를 변경하기전에 Mutex에 락을 건 후 조건 변수가 완료되기를 기다린다. 조건이 완료되면 데이터를 변경하고 Mutex의 락을 풀어 다른 스레드의 사용을 허가한다.

    스레드의 생성과 소멸
    모든 프로세스는 기본적으로 하나의 스레드와 같다. 예를 들어 main으로 시작되는 함수는 그 자체로 스레드가 되어 종료와 함께 스레드의 소멸로 이어진다. 하지만 특별히 기본 스레드에서 다른 스레드를 생성하려면 스레드 생성자를 호출해야 하고 이것들의 사용이 끝났으면 소멸자를 호출하여 메모리에서 제거해야 한다.


    스레드 생성
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start)(void *), void *arg)
    ① 위의 함수는 커널 레벨에서 수행되는 스레드를 생성한다.
    ② 함수의 호출 시 성공이면 리턴 값으로 0 이 아닌 값을, 실패했다면 0을 되돌린다.
    ③ 첫번째 인수인 스레드는 스레드 생성이 성공한 이후에 되돌아오는 스레드의 인식자이며 이 인식자의 형은 pthread형으로 unsigned int형과 동일한 유사한 형태로 제공된다.
    ④ 두 번째 인수인 attr은 생성할 스레드의 속성을 결정하는 인수로 보통 NULL값을 사용한다.
    ⑤ 세 번째 인수인 start는 스레드로 수행될 함수의 이름을 기술하는 곳으로 반드시 void형이며 인수 역시 void형 1개만을 가질 수 있다.
    ⑥ 마지막 인수는 함수에 들어가게 될 void형 인수를 기술하는 곳이다.

    스레드 소멸
    int pthread_exit(void *value_ptr)
    int pthread_detach(pthread_t thread)
    int pthread_join(pthread_t thread, void **value_ptr)
    ① 프로세스가 종료할 때 스레드는 그것의 상태와 모든 것을 완전히 종료하게 한다(이 경우 종료 이유를 기대할 수 없다).
    ② pthread_detach는 스레드 종료 후 메모리에 남아 있는 정보를 제거하는 역할을 수행한다.
    ③ pthread_join은 자식 스레드의 끝나는 상태를 기다라는 역할을 수행함으로 부모 스레드는 자식 스레드가 종료될 때까지 블러킹된다.

    스레드의 주기
    스레드는 Ready, Blocked, Running, Terminated의 4가지 상태를 그림 1과 같이 순회하며 그 주기를 마치게 된다.
    그림 1에 나타난 것과 같이 스레드가 처음 만들어졌을 경우(create) 스레드는 Ready 상태가 된다. 그리고 커널에 의해 스케줄링 되었을 경우 스레드의 상태는 Running 상태에 들어갈 수 있다. 그리고 이 스레드에 할당된 시간이 모두 끝났다면 스레드의 상태는 다시 Ready로 돌아가고 이 스레드가 임의의 자원을 사용하려고 할 때는 그 자원을 사용할 수 있기 전까지 Blocked된다. 그리고 자원을 사용할 수 있게 되면 다시 Ready 상태로 들어가게 된다. 만일 수행중인 작업이 모두 끝났거나 사용자에 의해 강제로 취소되었을 경우 스레드는 Termina ted상태가 되어 자신의 수행중인 과정을 모두 끝내게 된다. 이때 이미 위에서 언급했지만 종료 시에 모든 자원을 소멸시키지 않으므로 사용자가 자원의 소멸에 신경 써야 한다.

    스레드의 동기화
    스레드를 사용함에 있어 가장 주의해야 할 조건을 동기화다. 이것은 모든 스레드가 동시에 수행되므로 임의의 데이터를 동시에 접근하여 변경시킬 수 있음을 의미하며 전역 변수나 메모리의 할당 등의 경우 동기화를 반드시 고려해야 한다.

    Mutex의 생성과 파괴
    Mutex는 앞서 언급했듯 스레드간의 공유되는 데이터의 변경에 앞서서 수행 도중 다른 스레드로부터 이 데이터가 변경되지 않는다는 것을 보장하기 위한 것으로 사용하기 전에 초기화(create)해야 하고 모든 사용이 끝난 후 반드시 메모리에서 제거해야 한다.

    ① int pthread_mutex_init(pthread_mutex_t *mutex, pthread _mutexattr_t *attr)
      Mutex를 사용함에 있어 처음 생성시키는 역할을 담당하며 첫번째 인수로 사용할 Mutex와 두 번째 인수로 사용하게 될 Mutex의 속성을 결정한다.
    ② int pthread_mutex_destroy(pthread_mutex_t *mutex)
      Mutex사용이 모두 끝났을 경우 Mutex의 자료구조를 다시 커널로 반환 시켜 더 이상 사용하지 않음을 명문화 시키는 역할을 한다.

    Locking과 Unlocking.
    스레드 사이의 공유되는 데이터를 변경하고자 할 경우 변경하는 부분의 선두에 Mutex에 락을 걸고 변경이 완료되면 다시 언락 해야 한다.

    ① int pthread_mutex_lock(pthread_mutex_t *mutex)
      데이터를 변경하기 전 다른 스레드에게 이 데이터를 변경하지 못하도록 Mutex에 락을 건다. 만일 다른 스레드가 이미 그 데이터를 사용하려고 Mutex에 락을 걸었을 경우 스레드가 호출한 이 함수는 실패하게 된다.
    ② int pthread_mutex_trylock(pthread_mutex_t *mutex)
      pthread_mutex_lock 함수와는 달리 이미 다른 스레드에서 이 Mutex에 락을 걸어 데이터를 쓰려고 하는지 조사한다. 즉 다른 스레드에서 이 Mutex에 락을 걸었다면 그에 대한 에러 코드를 돌려준다.
    ③ int pthread_mutex_unlock(pthead_mutex_t *mutex)
      변경하려는 데이터의 사용을 마쳤을 경우 다른 스레드에서 그 데이터의 변경을 허가하는 역할을 한다. 변경하려는 데이터의 이후에 기술되어야 한다. 만일 이미 Mutex의 언락이 이루어진 후 다시 언락하려는 함수는 실패하게 된다.

    조건 변수(Condition variable)
    조건 변수는 스레드간의 공유하려는 데이터의 통신 정보를 위해 사용한다. 데이터의 변경을 하기 전에 Mutex를 락 해야하고 선행 조건을 기다리게 되며 선행 조건이 완료된 이후에 데이터의 변경을 해야 한다. 선행 조건의 완료는 단일 대기 스레드에게만 신호를 주는 Signal이라는 방법과 전체 대기 스레드에게 모두 신호를 주는 brocasting 방법이 있다.
    그림 2는 3개의 스레드가 하나의 데이터를 사용함에 있어 락과 언락, 그리고 조건 변수를 사용하여 데이터의 신용도를 보장하는 것을 보여주고 있다.

    ① 스레드 1이 데이터를 사용하고자 하여 처음 Mutex에 락을 걸고 Mutex3의 조건이 만족되기를 기다린다.
    ② 스레드 2가 데이터를 사용하려고 Mutex를 조사할 경우 이미 스레드 1이 사용하고 있으므로 스레드 1의 사용이 끝날 때까지 기다린다.
    ③ 스레드 3의 조건 변수는 스레드 1에 의해 참조되는 것으로 스레드 3의 조건이 만족된 이후에만 스레드 1의 데이터 변경이 허가된다.
    ④ 스레드 3의 조건 변수가 만족되고 스레드 1의 데이터의 변경이 완료된 이후 스레드 1은 이 데이터가 다른 스레드들에 의해 사용될 수 있도록 하기 위해 Mutex를 언락시킨다.

    조건 변수의 초기화와 소멸
    조건 변수는 사용하기 전에 반드시 초기화해야 하고 사용이 끝난 후에는 메모리에서 제거해야 한다.

    ① int pthread_cond_init(pthread_cond_t *cond, pthread_ condattr *condattr)
      조건 변수의 초기화를 위한 함수로 첫째 인수인 cond는 조건으로 사용되는 변수를, 두 번째 인수인 condattr은 조건으로 사용되는 변수의 속성을 나타낸다.
    ② int pthread_cond_destroy(pthread_cond_t *cond)
      조건 변수의 사용이 모두 끝났을 경우 커널에 자원을 되돌려 주기 위한 함수로 인수인 cond 변수의 해방을 뜻한다.

    조건 변수의 waiting과 waking
    데이터를 변경하기 전 반드시 다른 스레드에서 선행 조건이 완료되어야만 하는 경우 선행 조건의 pthread_ cond_wait를 사용하여 선행 조건의 완료를 기다리며 다른 스레드에서 선행 조건이 완료되었다는 것을 알려주는 방법으로 단일 스레드에게 알리는 signal과 모든 사용자들에게 알리는 brocast가 있다.

    ① int pthread_cond_wait(pthread_cond_t *cond, pthread_ mutex_t *mutex)
      데이터를 사용함에 있어 Mutex의 락을 건 스레드가 상황 변수의 조건을 기다리기 위한 함수다. 이 함수를 호출한 스레드는 조건이 만족되기 전까지 블러킹된다.
    ② int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *expiration)
      pthread_cond_wait 함수와 같은 역할을 하지만 일정 시간이 지난 후 자동으로 블러킹에서 벗어나게 되므로 조건이 만족되지 않아 무한히 기다라는 것을 피할 수 있다.
    ③ int pthread_cond_signal(pthread_cond_t *cond)
      cond의 조건을 기다리기 위해 pthread_cond_wait을 호출한 스레드에게 조건이 만족되었음을 알려준다.
    ④ int pthread_cond_broadcast(pthread_cond_t *cond)
      pthread_cond_signal과 같은 역할이지만 이 함수는 cond를 기다리는 모든 스레드에게 조건이 만족되었음을 알린다.


    [출처] pthread|작성자 Sally


    :

    정적 라이브러리, 공유 라이브러리 (3)

    OS/리눅스 & 유닉스 2012. 5. 18. 18:06

    3. 공유 라이브러리

    공유 라이브러리는 프로그램이 시작할때 적재되는 라이브러리이다. 공유 라이브러리가 제대로 설치된다면, 그다음에 시작하는 모든 프로그램은 자동적으로 새 공유 라이브러리를 사용한다. 이것은 훨씬 더 유연성 있고, 발전된것이다. 왜냐하면, 리눅스에서 사용하는 방법은 당신에게 다음과 같은 것들을 허용하기 때문이다:

    • 라이브러리를 업데이트해도 프로그램이 예전 버전의 라이브러리를 사용할수 있도록 지원한다.

    • 어떤 특정한 프로그램을 실행시킬때, 라이브러리 내의 특정한 라이브러리나 함수를 오버라이드 할 수 있다.

    • 이 모든것들을 존재하고있는 라이브러리에서 프로그램이 실행되는 동안에 가능하다.

    3.1. 관례들(Conventions)

    이 원하는 모든 기능을 지원하는 공유 라이브러리에 대해, 많은 관례와 지침이 따라주어야 한다. 당신은 라이브러리의 이름의 차이를 알아야 한다. 특별히, 불리는 이름``soname''과 실제이름``real name''에 대해서 알아야 한다(그리고 이것들이 어떻게 상호작용하는지를). 당신은 또한 이것들이 파일시스템의 어느부분에 위치하는지 알아야 한다.

    3.1.1. 공유 라이브러리 이름들

    모든 공유 라이브러리들은 ``불리는 이름''이라 불리는 특별한 이름을 가지고 있다. 그 불리는 이름은 접두사 ``lib'', 그 라이브러리 이름, ``.so''와 인터페이스가 바뀜에 따라 증가되는 기간과 버전넘버로 이루어진다(특별 케이스로, 저 수준의 C라이브러리는 ``lib''으로 이름이 시작하지 않는다). 충분히 검증받은(fully-qualified) 불리는 이름은 그 라이브러리가 속해있는 디렉토리를 접두사로 한다; 실제 시스템에서 충분히 검증받은 불리는 이름은 ``실제이름''의 심볼릭 링크가 된다.

    모든 공유 라이브러리들은 ``실제이름''이라 불리는 실제 라이브러리 코드를 포함하는 파일이름을 가지고 있다. 실제 이름은 불리는 이름에다가 기간, 마이너 숫자, 또다른 기간, 출시 숫자를 포함한다. 마지막의 기간과 출시 숫자는 옵션이다. 마이너 숫자와 출시 숫자는 당신이 라이브러리의 어떤 버전을 쓰고 있는지 정확하게 알게 함으로서 형상관리를 할 수 있도록 도와준다. 이 숫자들이 사용을 쉽게 한다 하더라도, 라이브러리 문서에 쓰인것과 같은 숫자가 아닐 수 있다는 점에 유의하라.

    게다가, 컴파일러가 라이브러리를 요구할 때 사용하는 다른 이름이 있다(나는 그것을 ``링크 이름(linker name)''이라 명칭할 것이다). 그것은 단지 불리는 이름에서 숫자를 없앤 이름이다.

    공유 라이브러리를 다루는 방법은 이 이름들의 구분을 하는 것이다. 프로그램이 그들이 원하는 공유 라이브러리를 내부적으로 나열한다면 그것들은 그 라이브러리들의 불리는 이름만 나열해야 한다. 반대로, 당신이 공유 라이브러리를 만든다면, 당신은 그 라이브러리를 특정한 파일이름으로 만들어야 한다(더 자세한 버전 정보들과 함께). 당신이 새로운 버전의 라이브러리를 설치한다면, 당신은 그것을 새로운 특별한 디렉토리에다 설치하고 ldconfig(8)을 사용해서 프로그램을 돌린다. ldconfig은 존재하는 파일을 조사하고, /etc/ld.so.cache의 캐시 파일을 설정하면서(잠시후에 언급 될 것이다) 실제 이름에다가 불리는 이름으로 심볼릭 링크를 만들어준다.

    ldconfig는 링커 이름을 만들지는 않는다; 보통 이것은 라이브러리 설치할때 만들어지고, 링커 이름은 ``가장최근의'' 불리는 이름이나 실제 이름으로 만들어진다. 나는 링커 이름을 불리는 이름의 심볼릭 링크로 사용할 것을 추천한다. 왜냐하면, 대부분의 경우 당신이 라이브러리를 업데이트 한다면, 당신은 링크 시킬때에 자동적으로 사용하고 싶어할 것이기 때문이다. 나는 H. J. Lu에게 왜 ldconfig가 자동적으로 링커이름을 만들어주지 않냐고 물어보았다. 그의 설명은 이렇다. 아마 당신을 최신버전의 라이브러리 코드를 돌리고 싶어할지 모른다. 하지만, 그 대신에 예전의(아마도 호환되지 않는) 버전의 라이브러리로 개발하고 싶어할 수 도 있다. 따라서, ldconfig는 프로그램이 어떤 라이브러리를 사용할 것인지 어떤 가정도 하고 있지 않다. 따라서, 설치자는 링커가 어떤 라이브러리를 사용할것이지에 대한 심볼릭 링크를 특별히 수정해 주어야 한다.

    따라서, /usr/lib/libreadline.so.3는 /usr/lib/libreadline.so.3.0과 같은 실제 이름에 ldconfig에 의해 심볼릭 링크가 된 충분히 검증받은 불리는 이름이다./usr/lib/libreadline.so.3을 심볼릭 링크하는 /usr/lib/libreadline.so 같은 링커 이름이 있을 수 있다.

    3.1.2. 파일 시스템 배치

    공유 라이브러리는 파일시스템의 어딘가에 위치해야만 한다. 대부분의 오픈소스 소프트웨어는 GNU표준을 따른다; 더 많은 정보를 위해서는info:standards#Directory_Variables를 찾아보아라. GNU표준은 소스코드를 배포할때 표준으로 모든 라이브러리를 /usr/local/lib에 올리기를 추천한다(그리고 모든 명령어는 /usr/local/bin에 위치하기를 추천한다). 그들은 또한 이 표준을 오버라이드하고, 인스톨 순서를 정해주기위한 관례를 정의한다.

    파일시스템 계층 표준(FHS = Filesystem Hierarchy Standard)는 배포판의 어디에서 무엇을 해야하는지 논의한다(http://www.pathname.com/fhs을 참조하라). FHS에 따르면, 대부분의 라이브러리는 /usr/lib에 인스톨 되어있어야만 한다. 하지만, 시작시에 요구되는 라이브러리는 /lib에 있어야 하고, 시스템의 일부가 아닌 라이브러리는 /usr/local/lib에 있어야 한다.

    위의 두 문서사이에 정말 충돌이 있지는 않다; GNU표준은 소스코드의 개발자에게 기본적것들을 추천한다. 반면에, FHS는 배포자(시스템 패키지 관리 시스템을 통해 소스코드의 기본적인 것을 오버라이드하는 사람)에게 기본적인것을 추천한다. 일반적으로 이것은 잘 돌아간다: ``최근의''(아마도 버그가 있을지도 모르는!) 소스코드는 당신이 다운로드해서 설치를 하면 ``local''디렉토리(/usr/local)에 설치될 것이고, 그 코드가 패키지를 발전시키면, 관리자는 배포판의 표준 위치로 그 코드를 표준으로 위치시킬 수 있다. 만약 당신의 라이브러리가 라이브러리를 통해 호출되는 프로그램을 호출한다면, 당신을 그런 프로그램을 /usr/local/libexec에 놓아야 한다(배포판에서는 /usr/libexec가 된다). 하나의 복잡한 점은, Red Hat종류의 시스템은 라이브러리의 탐색시에 /usr/local/lib을 기본으로 포함하지 않는다는 것이다; 아래의 /etc/ld.so.conf에 대한 논의를 보라. 다른 표준 라이브러리 위치는 X-windows를 위한 /usr/X11R6/lib을 포함한다. /lib/security는 PAM 모듈을 위한것이지만, 이것들은 DL라이브러리로 적재된다는 것을 명심하라(이것도 아래에서 논의된다).

    3.2. 라이브러리 사용 방법들

    모든 리눅스 시스템을 포함한 GNU glibc기반 시스템에서는, ELF 바이너리 실행파일의 시작은 프로그램로더가 적재되고 실행된 후에 한다. 리눅스 시스템에서는, 이 로더는 /lib/ld-linux.so.X(여기서 X는 버전 숫자)라는 이름이 붙는다. 이 로더는 프로그램에서 쓰이는 다른 모든 공유 라이브러리를 찾아주고 적재시켜준다.

    탐색될때 찾아지는 디렉토리의 리스트는 /etc/ld.so.conf에 저장된다. 많은 Red Hat기반 배포판은 기본적으로 /usr/local/lib을 /etc/ld.so.conf에 저장하지 않는다. 나는 이것이 버그라고 생각한다. 따라서, Red Hat기반 시스템에서 많은 프로그램을 돌리기 위해 /etc/ld.so.conf에 /usr/local/lib을 추가하는것은 버그 ``픽스''이다.

    라이브러리에 몇개의 함수를 추가하고 싶은데, 라이브러리의 나머지 부분을 유지하고 싶다면, 오버라이드하는 라이브러리의 이름(.o파일)을 /etc/ld.so.preload에 넣어라; 이 ``미리 적재되는'' 라이브러리는 기본 셋에 대해 우선순위를 가질것이다. 이 미리 적재되는 파일은 일반적으로 긴급패치에 사용된다; 배포판은 일반적으로 출시될때 그런파일들을 포함하지 않는다.

    프로그램 시작시에 이런 디렉토리를 다 찾는것은 매우 비효율적인 일이다. 따라서, 보통 캐싱 정렬이 사용된다. ldconfig(8)은 기본으로 /etc/ld.so.conf를 읽고 동적 링크 디렉토리들(표준 관례를따르는)에서 적절한 심볼릭 링크를 만들고, /etc/ld.so.cache에 캐시를 써 넣으면, 다른 프로그램에서 사용된다. 이것은 라이브러리 접근의 속도를 높여준다. 관련된것은 DLL이 추가되거나 삭제되거나 DLL디렉토리가 변할때도 ldconfig이 작동해야 한다는 것이다; ldconfig를 동작시키는 것은 라이브러리를 설치할때 패키지 관리자가 수행해야할 작업 중 하나이다. 그리고나서, 시작시에 동적 로더가 /etc/ld.so.cache를 사용하고 필요한 라이브러리를 로드한다.

    그런데, FreeBSD는 이 캐시를 위해 다른 파일이름을 사용한다. FreeBSD에서는, ELF 캐시는 /var/run/ld-elf.so.hints이고 a.out 캐시는 /var/run/ld.so.hints이다. 이것들은 ldconfig(8)에 의해서 업데이트된다. 따라서, 몇몇 다른 상황들에서만 이 장소의 차이가 문제가 된다.

    3.3. 환경 변수들

    여러가지 환경변수는 이 과정을 제어할 수 있다. 그리고 이 과정을 오버라이드하는 환경변수들이 존재한다.

    3.3.1. LD_LIBRARY_PATH

    이 특별한 실행을 위해 당신은 일시적으로 다른 라이브러리를 대체할 수 있다. 리눅스에서, 환경변수 LD_LIBRARY_PATH는 표준의 디렉토리들을 찾기전에 찾아보게되는 라이브러리의 디렉토리들의 콜론으로 구분되는 셋이다; 이것은 새 라이브러리나 특별히 제작한 표준이 아닌 라이브러리를 디버깅할때 유용하다. 환경변수 LD_PRELOAD는 /etc/ld.so.preload가 하는 것처럼 표준 셋을 오버라이드하는 공유 라이브러리를 함수와 함께 나열한다. 이것들은 /lib/ld-linux.so라는 로더에 의해 구현된다. LD_LIBRARY_PATH가 많은 유닉스 시스템에서 작동하는 반면 모든 시스템에서 작동하지 않는다는 것을 말하고 싶다; 예를들어, HU-UX에서는 이 기능이 환경변수 SHLIB_PATH에 의해서 가능하고, AIX에서는 LIBPATH에 의해 가능하다(같은 문법과, 콜론으로 구분되는 리스트로 가능하다).

    LD_LIBRARY_PATH는 개발과 검사를 위해 편리하다. 그러나 보통의 유저의 보통의 사용을 위해서 설치 과정에서 변경되면 안된다; 왜 그런지는http://www.visi.com/~barr/ldpath.html의 ``Why LD_LIBRARY_PATH is Bad''에서 찾아보라. 하지만, 이 기능은 여전히 개발과 검사를 위해 유용하고, 다른방식으로 해결하지 못하는 것을 해결하는데 유용하다. 만약 당신이 환경변수 LD_LIBRARY_PATH를 설정하고 싶지 않다면, 리눅스에서 당신은 프로그램 로더를 직접 불러서 인자를 넘겨줄수도 있다. 예를들어, 다음은 환경변수 LD_LIBRARY_PATH의 경로 이외의 주어진 PATH를 사용할 것이고, 실행가능 프로그램을 돌릴 것이다.

      /lib/ld-linux.so.2 --library-path PATH EXECUTABLE
    인자없이 ld-linux.so를 돌리는 것은 당신이 이렇게 사용하는데에 도움을 줄 것이다. 하지만, 이것을 보통의 용도로 사용하지 마라. 이것들은 디버깅용이다.

    3.3.2. LD_DEBUG

    GNU C에서 또다른 유용한 환경변수는 LD_DEBUG이다. 이것은 dl* 함수를 위해 만들어졌다. 따라서 그들이 하고 있는 것들에 대한 매우 장황한 정보를 준다. 예를 보자:

      export LD_DEBUG=files
      command_to_run
    라이브러리를 다룰때 파일과 라이브러리의 동작을 보여주고, 어떤 의존성이 발견되었고, 어떤 SOs(sonames)가 어떤 순서로 로드되었는지 말해준다. LD_DEBUG를 ``bindings''로 설정하는 것은 심볼제한에 대한 정보를 보여주고, ``libs''에 설정하는은 것은 라이브러리 탐색경로에 대해서 보여주고, ``version''으로 설정하는 것은 버전 의존성을 보여준다.

    LD_DEBUG를 ``help''로 설정하고 프로그램을 돌리면 여러가지 옵션을 표시할 것이다. 다시, LD_DEBUG는 보통의 사용을 위해 있는 것이 아니라, 디버깅과 검사를 위해 편리한 것이다.

    3.3.3. 다른 환경 변수들

    로딩과정을 제어할 수 있는 많은 환경변수들이 있다; 그것들의 이름은 LD_나 RTLD_로 시작한다. 대부분의 다른 환경변수들은 로더 프로세스의 저 수준의 디버깅이나 특별한 용도의 구현을 위해 존재한다. 그것들 대부분은 문서화가 잘 되어있지 않다; 당신이 그것들에 대해 알고 싶어한다면 최상의 방법은 로더의 소스코드를 읽는 것이다(gcc의 일부).

    특별한 조치가 취해지지 않는다면, 동적 연결 라이브러리에 사용자의 권한을 허락하는 것은 setuid/setgid가 걸린 프로그램에게 매우 위험하다. 따라서, GNU 로더(프로그램이 시작시에 프로그램의 나머지를 로드하는 로더)에서 setuid/setgid프로그램이라면 이 변수들(다른 비슷한 변수들)은 무시되거나 그들이 할 수 있는 역할이 매우 제한된다. 로더는 프로그램의 퍼미션을 체크해서 setuid/setgid인지 확인한다; uid/euid가 틀리거나, gid/egid가 틀리면 로더는 프로그램이 setuid/setgid라고 가정(또는 그럼 프로그램에서 파생된것)하고 따라서, 링크를 연결하는 동작에 매우 제한을 가하게 된다. 당신이 만약 GNU glibc라이브러리 소스코드를 읽었다면, 당신은 다음과 같은 것을 보았을 것이다; elf/rtld.c와 sysdeps/generic/dl-sysdep.c를 보아라. 이것은 당신이 uid/gid가 euid/egid가 같으면 프로그램을 불러서 환경변수들이 최대의 효과를 나타낼 수 있다는 것을 의미한다. 다른 유닉스같은 시스템에서는 다른 방식으로 처리하지만 같은 이유로 처리한다: setuid/setgid프로그램이 환경변수들에 의해 나쁘게 처리되면 안된는 이유이다.

    3.4. 공유 라이브러리 만들기

    공유 라이브러리를 만드는 것은 쉽다. 처음으로, gcc-fPIC나 fpic플래그를 사용해서 공유 라이브러리로 사용될 오브젝트 파일을 만들어라. -fPIC나 -fpic옵션은 ``위치에 독립적인 코드''를 만들어주고, 공유 라이브러리의 조건을 만족시킨다; 아래의 차이점을 보라. 그리고 이 형식을 따라서 공유라이브러리를 만들어라:

    gcc -shared -Wl,-soname,your_soname \
        -o library_name file_list library_list

    두개의 오브젝트 파일(a.o, b.o)를 만들고 이것들 모두를 포함하는 공유 라이브러리를 만드는 예제이다. 컴파일이 디버그 정보(-g)와 경고정보(-Wall)를 포함하는데, 이것들은 공유라이브러리를 위해 필요한것은 아니지만, 추천되는 정보라는 것을 주의하라. 컴파일은 오브젝트 파일을 만들고(-c), -fPIC옵션을 요구한다.

    gcc -fPIC -g -c -Wall a.c
    gcc -fPIC -g -c -Wall b.c
    gcc -shared -Wl,-soname,libmystuff.so.1 \
        -o libmystuff.so.1.0.1 a.o b.o -lc

    주의할 만한 가치가 있는 것들이 있다:

    • 꼭 필요한 경우가 아니라면, 결과로 생긴 라이브러리를 분해하거나, 컴파일러 옵션으로 -fomit-frame-pointer 옵션을 주지마라. 결과로 나온 라이브러리는 잘 동작할 것이고, 위의 행동은 디버거를 무용지물로 만든다

    • 코드를 생성하기 위해 -fPIC이나 -fpic을 사용하라. 코드를 생성하기 위해 -fPIC이나 -fpic을 사용하는 것은 타겟에 따라서 다르다. -fPIC을 사용하는것은 언제나 동작한다. 하지만, -fpic을 사용하는 것보다 큰 코드를 생성할 것이다(PIC은 더 큰코드를 위한것이라서 더 많은 양의코드를 만든다는 것을 기억하라). -fpic옵션은 작고 빠른 코드를 만든다. 하지만, 전역심볼이나 코드의 크기 같은 것에서 플랫폼에 독립적이다. 링커는 공유 라이브러리를 만들때 이 옵션이 맞는지 말해줄 것이다. 어느것을 써야 할지를 모를때, 나는 언제나 동작하는 -fPIC을 선택한다.

    • 몇몇의 경우에서, 오브젝트 파일을 만들기위해 gcc를 호출하는 것은 ``-Wl,-export-dynamic'' 옵션을 포함할 것이다. 보통 동적 심볼테이블은 동적 오브젝트에 의해 사용되는 심볼만 포함한다. 이 옵션은(ELF파일을 만들때) 동적 심볼테이블에 모든 심볼을 추가한다(더 많은 정보를 위해 ld(1)를 참고하라). '역 의존성'이 있을때 이 옵션을 필요로 할 것이다. 즉, DL라이브러리가 라이브러리를 로드하는데 프로그램에서 필요한 심볼이지만, 관례에 의해 정의되지 않은 심볼을 필요할 경우 사용된다. ``역 의존성''이 작동하기 위해서, 주 프로그램은 심볼이 동적으로 동작하게 해야 한다. 리눅스 시스템에서만 사용한다면, ``-Wl,export-dynamic''대신에 ``-rdynamic''을 사용할수도 있을 것이다. 하지만, ELF문서에 따르면 ``-rdynamic''플래그는 리눅스가 아닌 시스템의 gcc에서 항상 작동하는 것은 아니다.

    개발과정동안, 다른 많은 프로그램에서 사용되는 라이브러리를 수정하고 싶을때가 있을 것이다 -- 그리고 당신은 프로그램들이 ``개발상의''라이브러리를 사용하는것을 원치 않을 것이고, 어떤 특정 응용프로그램만이 그것을 사용하기를 원할것이다. ld의 ``rpath''옵션은 어떤 특정한 프로그램이 컴파일 될 때 실시간으로 라이브러리의 패스를 정해주는 역할을 한다. gcc에서 당신은 다음과 같은 방식으로 rpath를 지정해 줄 수 있다:

     -Wl,-rpath,$(DEFAULT_LIB_INSTALL_PATH)
    당신이 라이브러리 클라이언트 프로그램을 설치할때 이 옵션을 사용한다면, 그것이 충돌을 일으키지 않거나, 라이브러리를 숨기는 다른 기술을 사용하도록 하기위해 LD_LIBRARY_PATH를 사용하는 것을 걱정할 필요가 없다.

    3.5. 공유 라이브러리를 설치하고 사용하기

    당신이 공유 라이브러리를 만들었다면 당신은 그것을 설치하고 싶어 할 것이다. 간단한 방법은 표준 디렉토리(예들를어, /usr/lib)중 하나에 카피하고 ldconfig(8)을 실행시키는 것이다.

    첫째로, 당신은 공유라이브러리를 어딘가에 설치하고 싶어할 것이다. 그리고나서, 당신은 실제이름을 불리는이름으로 심볼릭링크를 걸어야만 할것이다(버전 숫자가 없는 불리는 이름이다. 즉, ``.so''로 끝나서 사용자들이 버전에 상관없이 사용하게 하는 것이다). 간단한 접근법은 다음을 실행시키는 것이다:

     ldconfig -n directory_with_shared_libraries

    마지막으로, 너의 프로그램을 컴파일할때 당신이 쓰려하는 정적, 공유 라이브러리에 대해 링커에게 말해줘야 한다. -l이나 -L옵션을 쓰면 된다.

    당신이 라이브러리를 표준 공간(예를들어, 당신은 /usr/lib을 수정해야하는 것은 아니다)에 설치하고 싶지 않을 경우, 다른 접근법이 있다. 이 경우에, 당신은 다른 어딘가에 설치하고 프로그램이 라이브러리를 찾도록 충분한 정보를 주면된다. 이 방법에는 여러가지가 있다. 간단한경우로 gcc의 -L 플래그를 줄 수 있다. ``표준이 아닌''공간에 있는 특정한 프로그램을 가지고 있다면 ``rpath''를 사용할 수 있다. 물론 환경변수를 사용해서 해결하는 방법도 있다. 특별히, 당신은 콜론으로 구분되어지는 표준공간에서 검색 전에 찾아지는 공유라이브러리들의 디렉토리들의 모임인 LD_LIBRARY_PATH를 사용할 수 있다. 만약 당신이 bash를 사용한다면, my_program은 다음과 같은 방법으로 될 수 있다:

    LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH  my_program

    몇 개의 함수를 오버라이드 하구 싶다면, 오버라이드 할 오브젝트 파일을 만들고 LD_PRELOAD를 설정하라;이 오브젝트 파일의 함수는 그 함수들을 오버라이드 할 것이다(다른 함수들은 원래 있던대로 있을 것이다).

    보통은 당신이 라이브러리를 걱정없이 업데이트 할 수 있다; 만약 API가 바뀌면 라이브러리 제작자는 불리는 이름을 바꾸어야 한다. 이런방식으로, 한 시스템에 많은 라이브러리가 있을것이고, 정확한것이 각각의 프로그램에서 선택되어서 사용되어진다. 그러나, 어떤 프로그램이 같은 불리는 이름을 가지는 라이브러리에 대해서 업데이트 한것이 잘 동작하지 않는다면, 너는 예전 버전의 라이브러리를 다른곳에 옮겨두고 그 프로그램의 이름을 바꾸고(예전이름에다가 ``.orig''를 붙인다), 라이브러리 사용을 리셋하고 실제의 새로 이름이 붙은 프로그램을 부르는 작은 ``감싸는(wrapper)'' 스크립트를 만들수 있다. 당신이 원한다면, 숫자 관례가 허용하는 한 같은 디렉토리에다가 예전의 라이브러리를 다른곳에 옮겨 둘 수 있다. 감싸는 스크립트는 다음과 같다:

      #!/bin/sh
      export LD_LIBRARY_PATH=/usr/local/my_lib:$LD_LIBRARY_PATH
      exec /usr/bin/my_program.orig $*
    당신이 프로그램을 쓸때 꼭 이것에 의존하지는 말아라; 프로그램의 라이브러리가 예전것과 호환이 되는지 확인하거나 호환되지 않는 개정을 했을때 버전넘버를 증가시켰는지 체크하라. 이것은 최악의 경우의 ``긴급'' 접근법이다.

    당신은 공유 라이브러리의 리스트를 ldd(1)을 사용해서 볼수 있다. 따라서, 예를들어 당신은 ls에 사용되는 공유 라이브러리를 볼 수 있다.

      ldd /bin/ls
    일반적으로 당신은 그 이름이 속해있는 디렉토리에 따라 관계되는 불리는 이름의 목록을 볼 것이다. 특별히 모든 경우에서 다음의 두가지의 의존성을 가질것이다:

    • /lib/ld-linux.so.N (N은 1이상인데 보통 2이다). 이것은 다른 라이브러리를 로드하는 라이브러리이다.

    • libc.so.N (N은 6이상이다). 이것은 C라이브러리이다. 다른 언어에서 C라이브러리를 사용하고자 하면(적어도 그들의 라이브러리에서 구현하려할때), 다른 대부분의 프로그램들이 이것을 포함할 것이다.

    주의 : 당신이 신뢰하지 않는 프로그램에 ldd를 실행시키지 말아라. ldd(1) 매뉴얼을 보면 확실하겠지만, ldd는 (어떤 경우들에 있어서) 환경변수를 설정하고(ELF 오브젝트나, LD_TRACE_LOADED_OBJECTS) 프로그램을 실행시킴으로서 동작한다. 이것은 신뢰하지 못하는 프로그램에 있어서 (ldd의 정보를 보여주는것 대신에) 임의의 코드를 실행시킬 수 있다. 따라서, 안전을 위해서 당신이 신뢰하지 못하는 프로그램은 ldd를 사용하지 말아라.

    3.6. 호환되지 않는 라이브러리들

    새버전의 라이브러리가 예전버전과 이진-호환이 안된다면, 불려지는 이름이 바뀌어야 한다. C에서는 이진 호환이 안되게 되는 4가지 기본 경우가 있다.

    1. 함수의 내용이 바뀌어서 본래의 스펙과 맞게 동작하지 않는 경우.

    2. 수출된(exported) 데이타 아이템이 변한경우(예외 : 데이터 구조가 계속 라이브러리 내에 존재한다면, 데이터 구조의 내부에 아이템을 추가하는 것은 괜찮다)

    3. exported 함수가 제거될 경우

    4. exported 함수의 인터페이스가 변할 경우

    이런경우를 피하려한다면, 이진-호환이 되게 라이브러리를 유지할 수 있다. 다시 말해서 그런경우를 피하고 싶다면 응용 이진 인터페이스(ABI=Application Binary Interface)를 할 수 있다. 예들들어, 예전것들을 지우지 않고 새로운 함수를 추가하고 싶을 수 있다. 당신은 구조에 새로운 아이템을 추가할 수 있다. 하지만, 예전 프로그램이 새로운 아이템을 추가한 구조를 인식하지 못한다거나 (프로그램이 아닌)라이브러리가 새로운 공간을 할당하지 못하거나, 추가의 아이템을 옵션으로 만드는것(또는 라이브러리가 그것들을 채우게 하는것)등등을 확실히 해야한다는 것을 주의하라. 주의하라 - 유저가 배열에 구조를 사용하고 있다면 당신은 배열의 구조를 늘릴수 없다.

    C++(그리고 컴파일된 템플릿이나 다른 디스패치된 메소드를 지원하는 언어)에서 상황은 교묘해진다. 위의 모든 상황이 이루어져야 하고, 더 해주어야 할 이슈들이 있다. 상황은 컴파일된 코드에서 ``보이지 않게'' 구현되기 때문에 C++이 어떻게 구현되었는지 알지 못하면 애매모호해지는 의존성을 낳는 정보들 때문에 일어난다. 엄밀하게 말하면, 이것은 ``새로운'' 이슈는 아니다. 단지, 컴파일된 C++코드가 너에게 놀라게 할 수 있을것이다. 다음에 나오는 것들은 (아마도 부족하겠지만) Troll Tech's Technical FAQ에 나오는 이진 호환을 유지하기 위해 C++에서 사용하면 안되는 것들의 목록이다.

    1. 가상함수를 재 구현하는것(그렇지 않으면 원래의 구현에서는 안전하다). 왜냐하면 컴파일러는 컴파일시에(링크시가 아니라) SuperClass::virtualFunction()를 구하기 때문이다.

    2. 가상 멤버함수를 더하거나 제거하는것. 왜냐하면 이것은 모든 서브클래스의 vtlb의 크기와 구조를 바꾸기 때문이다.

    3. 데이터 멤버의 타입을 바꾸거나 인라인 멤버함수를 통해 접근할 수 있는 데이터 멤버를 옮기는것.

    4. 새 노드를 추가하는 것 이외에 클래스 구조를 바꾸는것.

    5. private데이터 멤버를 추가하거나 삭제하는것. 왜냐하면 이것은 모든 서브클래스의 크기와 구조를 바꾸기 때문이다.

    6. 인라인이 아닌 public, protected 멤버 함수를 제거하는것.

    7. public, protected 멤버함수를 inline으로 만드는것.

    8. inline함수를 바꾸어서, 예전버전이 제대로 작동하지 않는것.

    9. 포팅할 프로그램의 멤버 함수의 접근권한(public, protected, private)을 바꾸는것. 왜냐하면, 어떤 컴파일러는 함수의 이름에 접근권한을 붙이기 때문이다.

    위의 주어진 목록대로, C++라이브러리 개발자들은 때때로 이진 호환성을 깨는 업데이트를 계획해야만한다. 다행스럽게도, 유닉스 기반 시스템은 동시에 로드되는 라이브러리의 많은 버전을 가지고 있어서, 디스크 공간을 조금 할당한다면, 유저들은 예전의 라이브러리를 요구하는 ``예전''프로그램을 돌릴수 있다.


    출처 - http://wiki.kldp.org/HOWTO/html/Program-Library-HOWTO/shared-libraries.html


    :