리눅스에선 tar 에 z 옵션을 추가하면 tar(묶기)와 gzip(압축)을 한번에 할 수가
있는데 솔라리스에선 안됩니다.
| (파이프)를 통해서 하는 방법을 소개해 드려요.

1. 압축 
  tar cvf - 압축할디렉토리 | gzip -c > aa.tar.gz

2. 해제
 gzip -dc aa.tar.gz | tar xvf -



출처 : http://dev4u.tistory.com/entry/%EC%86%94%EB%9D%BC%EB%A6%AC%EC%8A%A4-tar%EC%99%80-gzip%EB%A5%BC-%ED%95%9C%EB%B2%88%EC%97%90-%ED%95%98%EA%B8%B0

출처 : http://firekokoma.tistory.com/13


많은 개발자들이 리눅스용으로 어플리케이션을 작성하고 있었습니다만 솔라리스10의 수많은 새로운 기능과 썬의 계속되는 AMD, 인텔 프로세서 지원을 통해 개발자들이 솔라리스 플랫폼에서 어플리케이션을 작성하는 것에 대해 관심을 가지게되었습니다. 이 글은 각 운영체제에서의 개발 환경의 유사점과 차이점에 대해 살펴 봅니다. 이 글을 통해 어플리케이션을 리눅스에서 솔라리스로 포팅 하거나, 리눅스에서 개발 경험이 있는 개발자가 솔라리스에서 프로그래밍을 하는 데 도움을 얻을 수 있을 것입니다.

이 글에서 용어 "솔라리스"는 솔라리스 10 (과 오픈 솔라리스)를 나타내고 "리눅스" 는 리눅스 2.6을 나타 냅니다. 이 글에서 다루는 많은 부분들은 솔라리스와 리눅스의 이전 버젼에도 또한 적용될 것입니다. 이 글의 테스트는 SuSe 9.1 하에서 이루어 졌으나 리눅스 배포판이란 범용 리눅스를 뜻합니다. 또한 이 글은 C++ 도 동일하게 수행되겠지만 C 프로그래밍을 작성하는데 촛점을 맞추고 있습니다. 자바 기반의 어플리케이션은 리눅스 혹은 솔라리스에 특정한 함수를 호출해서는 안되는 관계로 어플리케이션은 포팅이 가능할 것입니다.

소개

이 글에서는 솔라리스와 리눅스에서 어플리케이션 프로그래머와 분석가들에게 보여질 수 있는 동일점과 차이점에 대해 생각해 봅니다. 꼭 이것이 차이점에 대한 소모적인 설명을 뜻하거나 어떠한 OS가 상대편 보다 좋다는 것을 보여주는 것은 아닙니다. 이 글은 한쪽 OS에 경험이 많은 개발자가 다른쪽 OS 또한 쉽게 다를 수 있도록 돕는데 그 목적이 있습니다.

POSIX과 호환되며 어떠한 시스템 콜 혹은 라이브러리 함수도 호출하지 않는 간단한 어플리케이션은 어떠한 수정도 없이 두 OS 상에 포팅이 가능할 것입니다. 즉 어플리케이션을 작성하고 솔라리스 혹은 리눅스에서 컴파일 한 다음 다른 OS 에서 간단히 재 컴파일하면 두 곳 모두에서 작동할 수 있습니다. 대부분의 시스템 콜 과 라이브러리 루틴은 이와 같은 것입니다.

리눅스의 많은 시스템 콜들은 솔라리스에 라이브러리 함수 형태로 존재하며, 또한 그 반대도 마찬가지입니다. 예를 들어sched_setscheduler() 는 리눅스에서 시스템 콜이고 솔라리스에서는 priocntl(2) 시스템 콜을 호출 하는 라이브러리 함수 입니다.priocntl(2) 시스템 콜은 리눅스에서는 존재 하지않습니다만 리눅스는 시간 공유, 실시간 이상의 멀티 스케쥴러를 지원하지 않습니다. 뒷부분에서 시스템콜을 기능적 섹션으로 그룹을 묶고 각 OS에서 어떠한 것이 사용 가능한지 비교해 봅니다.

리눅스 세계의 대부분의 어플리케이션과 툴킷은 어떠한 수정도 없이 컴파일이 가능할 것입니다. 여기에는 gcc, emacs, MySQL, perl 외 수많은 것들이 포함되어있습니다. 컴파일된 바이너리 패키지는 http://www.sunfreeware.com 에서 다운로드할 수 있습니다.

리눅스와 솔라리스를 비교 하는 몇몇 글이 존재 하지만 대부분은 이전 버전들을 비교하는 글이었습니다. 이런 글들은 웹에서 "리눅스 솔라리스 비교 " 로 검색해 볼 수 있습니다. Seal Rock Research White Paper (pdf) 은 솔라리스10 과 리눅스 2.6 버젼에 대해 다루고있습니다.Migrating Solaris Applications to Linux 은 솔라리스 어플리케이션을 리눅스로 포팅 하는 것에 대해 몇장에 걸쳐서 다루고 있습니다.

솔라리스와 리눅스는 많은 관리상의 차이점이 존재하며, 한 OS의 각 배포판별로도 많은 차이점이 있습니다 . 솔라리스10은 솔라리스 이전 버젼에 비해 가장 크게 변화된 "서비스 관리 프레임워크(SMF;Service Management Framework)" 라는 기능을 가지고 있습니다. 시스템 관리상의 차이점은 이 글에서 다루지 않지만 개발자에게 영향을 미치는 경우 추후 다루도록 하겠습니다.

시스템 콜과 라이브러리

리눅스에 존재하는 대부분의 시스템 콜과 라이브러리는 솔라리스에도 존재합니다. 이번 문단에서는 두 시스템 간에 각각 다른 시스템 콜과 라이브러리 루틴에 대해 다룹니다.

솔라리스는 시스템 콜의 리스트를 /usr/include/sys/syscall.h 에 보관 합니다. 리눅스는 동일한 정보를 /usr/include/asm/unistd.h에 보관 합니다. (주목할 점은 리눅스와 솔라리스 둘다 unistd.h 와 syscall.h 파일을 가지며 종종 동일한 내용을 가질때도 있다는 사실입니다.)

시스템 콜에 대한 문서는 솔라리스, 리눅스에서 모두 /usr/share/man/man2 에 존재합니다. (솔라리스는 /usr/man 에서 동일한 곳으로 심볼릭 링크가 걸려 있습니다.) 라이브러리 루틴은 메뉴얼의 다양한 섹션에 문서화되어있습니다. 리눅스, 솔라리스의 라이브러리 섹션에 대한 개관을 위해 man intro.3 를 보도록 합니다. 주목할 점은 솔라리스는 라이브러리 루틴을 리눅스보다 더 잘 분류해 놓았다는 점입니다. 예를 들어 aio_read() 는 솔라리스에서 aio_read(3RT) 에 분류되어 있지만 리눅스에서는 aio_read(3)에 분류되어 있습니다. 결과적으로 솔라리스에서 aio_read() 를 사용하는 프로그램을 컴파일할 때 개발자는 반드시 실시간 라이브러리 -lrt 를 통해 실시간 라이브러리를 컴파일/링크 할때 포함시켜야 하지만 리눅스에서는 그럴 필요가 없습니다.

리눅스와 솔라리스는 둘다 200개 이상의 서로 다른 라이브러리를 가지고 있고 라이브러리 내에 50,000 개 정도의 함수를 가지고 있습니다.

다음의 표는 리눅스와 솔라리스의 몇몇 라이브러리를 목록화한 것입니다. 주의할 점은 이것이 전체 목록은 아니라는 것이고 이러한 라이브러리들은 기본 인스톨이 된 시스템에서는 각각 따로 다운로드 및 설치를 해야 한다는 것입니다.

표 1: 리눅스와 솔라리스의 몇몇 라이브러리들
 
 
솔라리스
리눅스
설명
libc
libc
표준 C 라이브러리 (POSIX, SysV, ANSI, 등등.) 솔라리스의 libc man 참조.
libucb
libc
UCB (University California Berkeley) 호환 바이너리
libmalloc
libc
몇가지 서로 다른 malloc 라이브러리가 존재함; 기본 함수는 libc 에 존재.
libsocket
libc
소켓 라이브러리 (리눅스에서는 libc 에 존재).
libxnet
libc
X/Open 네트워킹 라이브러리
libresolv
libresolv
DNS 루틴들 (솔라리스에서는, inet_* routines)
libnsl
libnsl/libc
네트워크 서비스 라이브러리 (리눅스 - nis/nis+ 루틴들)
librpc
librpc
RPC 함수들
libslp
libslp
Service Location Protocol
libsasl
libsasl
간단한 인증과 보안 레이어
libaio
libaio
비동기 I/O 라이브러리
libdoor
 
Door 지원 (door_create()door_return(), 등등.)
librt
librt
POSIX 실시간 라이브러리
libcfgadm
 
설정 관리 라이브러리
libcontract
 
계약 관리 라이브러리 (솔라리스에서 man contract.4 참조)
libcpc
 
CPU 성능 카운터 라이브러리 (리눅스에서는 아마 커널 모듈을 설치해야 할 것임)
libdat
 
직접 접근 전송 라이브러리 (http://www.datcollaborative.org)
libelf
libelf
ELF 지원 라이브러리
libm
libm
수학 라이브러리

다음 섹션에서는 몇가지 시스템 콜과 라이브러리들에 대해 좀 더 자세히 알아 봅니다. 시스템 간의 차이점에 대해 집중하겠습니다.

소켓과 네트워킹

대부분의 소켓, 네트워킹 코드는 반드시 사용하는 OS에 따라 다시 재컴파일 되어야 하고 반드시 실행이 가능해야 합니다. 이 섹션은 솔라리스와 리눅스에서 보편적으로 사용되는 네트워크에 관련된 시스템 콜과 라이브러리를 비교합니다.

socket()

socket() 루틴, 이에 덧붙여서 AF_UNIXAF_INET, 와 AF_INET6 도메인 매개변수들은 솔라리스와 리눅스에서 추가적인 값을 각각 가지고 있습니다. 솔라리스에서 AF_NCA 도메인은 소켓에 네트워크 캐쉬 혹은 가속기 (nca(1) 참조)를 지정하여 사용 할 수 있도록 합니다. 대부분의 어드레스 패밀리(도메인들)은 리눅스, 솔라리스에 둘다 존재 합니다. 주의: 솔라리스에 /usr/include/sys/socket.h 그리고 리눅스에/usr/include/linux/socket.h 를 비교하여 가능한 어드레스 패밀리를 참고 하시기 바랍니다. 그러나 개발자는 몇몇 이러한 도메인을 지원하는 코드를 작성 혹은 다운로드 받아야 할지도 모릅니다.

리눅스는socket(2) 맨 페이지에 기술된 대로 몇가지 추가적인 도메인을 가지고 있습니다. 기술된 추가적인 도메인은 다음과 같습니다:

  • AF_IPX - Novell IPX 프로토콜(아마도 오로지 수세를 위한 것임?).
  • AF_NETLINK - 커널/유저 인터페이스 디바이스로 유저가 커널 모듈에 접근 할 수 있도록 허락해 줌. 주의: 솔라리스에서는 다른 방법으로 똑같은 일을 할 수 있는 방법이 존재 함(리눅스에서도 마찬가지).
  • AF_X25 - X25 프로토콜. 솔라리스에서 이 도메인은 Solstice X.25 제품에 포함되 있음.
  • AF_AX25 - 아마츄어 라디오 AX.25 프로토콜.
  • AF_ATMPVC - ATM 상의 영구적인 가상 서킷.
  • AF_APPLETALK - 리눅스의 man ddp 참고. 솔라리스에도 존재 하지만 문서화 되어 있지는 않음.
  • AF_PACKET - 리눅스의 man packet.7 참고. Raw 패킷 인터페이스. 솔라리스에서는 NIC 디바이스를 열고 use getmsg(2)/putmsg(2) 를 사용하여 DLPI를 통한 raw 패킷을 전송/수신 할 수 있음. (DLPI에 대한 자세한 설명은 Data Link Provider Interface (DLPI), Version 2 를 참조).
bind()

리눅스 멘 페이지 (man bind.2) 는 AF_INET 와 AF_UNIX 사이의 어드레스 패밀리 차이점에 대한 정보를 포함하고 있음. 솔라리스는 man bind.3socket 을 참조.

listen()

리눅스와 솔라리스 둘다 backlog 매개 변수는 (listen()의 두번째 매개변수) 수락되길 기다리고 있는 접속된 큐의 길이를 나타 내고 있음. 리눅스 맨 페이지에서는 이렇게 설명 하고 있고 솔라리스 멘 페이지에서는 그냥 "지연되고 있는 연결 큐"라고 설명되어 있습니다.

accept()

리눅스는 3가지의 연결 기반 소켓 타입을 지원합니다: SOCK_STREAMSOCK_SEQPACKET, 그리고 SOCK_RDM 반면 솔라리스에서는 오직SOCK_STREAM 만 기술 되어 있습니다. 리눅스 구현에서는 몇가지 소켓 플래그를 상속 하지 않습니다. 이 것이 구현의 차이점을 불러 왔을 것입니다.

connect()

리눅스 멘페이지는 (man connect.2SOCK_SEQPACKET 를 기술 하고 있지만 솔라리스에서는 기술되어 있지 않습니다. 리눅스는 struct sockaddr 내의 sa_family 를 AF_UNSPEC 으로 세팅해서 주소에 연결 함으로써 비연결 소켓과 connect() 을 구분하고 있습니다. 이러한 동작은 솔라리스에는 기술되어 있지 않습니다.

send()/recv()

다른 소켓 라이브러리 함수들과 같이, 이러한 함수들은 두 시스템에서 거의 동일하게 동작합니다. 리눅스는 멘페이지에서 몇가지 추가적인flags 매개 변수를 가지고 있습니다.

shutdown()

솔라리스와 리눅스간에 주목할만한 차이점은 없습니다.

네트워킹 예제

몇가지 차이점이 들어나는 어플리케이션을 확인 하는 것이 유용할 것입니다. tracedump 프로그램은 패킷 캡춰 라이브러리 (libpcap) 를 이용하여 유저 레벨에서의 이더넷 패킷을 읽어 들입니다. 이 코드는 raw 이더넷이 솔라리스, 리눅스 간에 상당히 다름을 알아 낼 수 있습니다. (libpcap 은 FreeBSD, HP-UX, AIX 같은 서로 다른 시스템 간의 차이점을 알아 내는데 유용할 수 있습니다.) libpcap에서 응용 가능한 코드는pcap-linux.c 와 pcap-dlpi.c에 존재합니다. DLPI 코드는 솔라리스, HP-UX, AIX, 그리고 다른 운영체제를 위해 사용 됩니다. 리눅스는 표준socket 호출을 통해 raw 소켓 패킷을 읽어 들일 수 있는 방법을 제공합니다. 솔라리스는 getmsg(2) 와 putmsg(2) 를 호출해서 DLPI 패킷을 주고 받습니다.

다음의 코드는 솔라리스의 네트워크 인터페이스에서 유저-레벨 패킷을 캡춰 하는 방법을 나타내고 있습니다. 바로 뒤에 리눅스에서 동일한 작업을 하는 코드가 따라 옵니다. 이 코드는 libpcap 라이브러리의 매우 엄청난 간략화 버젼입니다.

#include <sys/types.h>
#include <sys/dlpi.h>
#include <sys/stream.h>
#include <stdio.h>

#include <errno.h>
#include <stropts.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int
main(int argc, char *argv[])
{
    register char *cp;
    int fd;
    dl_info_ack_t *infop;
    union DL_primitives dlp;
    dl_info_req_t inforeq;
      dl_bind_req_t    bindreq;
    dl_attach_req_t attachreq;
    dl_promiscon_req_t promisconreq;
    struct    strbuf    ctl, data;
    int    flags;
    char buffer[8192];
    dl_error_ack_t *edlp;

    fd = open(argv[1], O_RDWR);  /* for instance, /dev/elxl0 */

    /* attach to a specific interface */
    attachreq.dl_primitive = DL_ATTACH_REQ;
    attachreq.dl_ppa = 0;  /* assume we want /dev/xxx0 */
    ctl.maxlen = 0;
    ctl.len = sizeof(attachreq);
    ctl.buf = (char *)&attachreq;
    flags = 0;
    /* send attach req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get ok ack, may contain error */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    memset((char *)&bindreq, 0, sizeof(bindreq));
    /* the following bind might not need to be done */
    bindreq.dl_primitive = DL_BIND_REQ;
    bindreq.dl_sap = 0; 
    bindreq.dl_max_conind = 1;
    bindreq.dl_service_mode = DL_CLDLS;
    bindreq.dl_conn_mgmt = 0;
    bindreq.dl_xidtest_flg = 0;
    ctl.maxlen = 0;
    ctl.len = sizeof(bindreq);
    ctl.buf = (char *)&bindreq;
    flags = 0;
    /* send bind req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get bind ack */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    promisconreq.dl_primitive = DL_PROMISCON_REQ;
    promisconreq.dl_level = DL_PROMISC_PHYS;
    ctl.maxlen = 0;
    ctl.len = sizeof(promisconreq);
    ctl.buf = (char *)&promisconreq;
    flags = 0;
    /* send promiscuous on req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get get ok ack */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    promisconreq.dl_primitive = DL_PROMISCON_REQ;
    promisconreq.dl_level = DL_PROMISC_SAP;
    ctl.maxlen = 0;
    ctl.len = sizeof(promisconreq);
    ctl.buf = (char *)&promisconreq;
    flags = 0;
    /* send promiscuous on req */
    putmsg(fd, &ctl, (struct strbuf *)NULL, flags);
    ctl.maxlen = sizeof(dlp);
    ctl.len = 0;
    ctl.buf = (char *)&dlp;
    /* get get ok ack */
    getmsg(fd, &ctl, (struct strbuf*)NULL, &flags);

    /* read and echo to stdout whatever comes to us */
    while (1) {
      data.buf = buffer;
      data.maxlen = sizeof(buffer);
      data.len = 0;
      ctl.buf = (char *) &dlp;
      ctl.maxlen = sizeof(dlp);
      ctl.len = 0;
      flags = 0;
      getmsg(fd, &ctl, &data, &flags);
      write(1, "\nCTL:\n", 6);
      write(1, ctl.buf, ctl.len);
      write(1, "\nDAT:\n", 6);
      write(1, data.buf, data.len);
    }
}

솔라리스 코드는 DLPI 요청을 만들고 DLPI 응답을 받아서 인터페이스에게 인터페이스에 도착하는 모든 패킷의 카피를 만들도록 요청 합니다.

리눅스의 코드는 훨씬 간단합니다. socket(2) 콜은 raw 패킷을 지정할 수 있도록 허락합니다. 리눅스는 DLPI나 STREAMS을 사용하지 않습니다.

#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>

#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/if_arp.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
    int    sock_fd = -1;
    struct sockaddr_ll    sll, from;
    struct packet_mreq    mr;
    socklen_t    fromlen;
    int        packet_len;
    char        buffer[8192];

    sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

    memset(&sll, 0, sizeof(sll));
    sll.sll_family    = AF_PACKET;
    sll.sll_ifindex    = 0;
    sll.sll_protocol    = htons(ETH_P_ALL);

    bind(sock_fd, (struct sockaddr *) &sll, sizeof(sll));

    while (1) {
      fromlen = sizeof(from);
      packet_len = recvfrom(
        sock_fd, buffer, sizeof(buffer), MSG_TRUNC,
        (struct sockaddr *) &from, &fromlen);
      write(1, buffer, packet_len);
    }
}

프로세스/프로세서 관리

솔라리스와 리눅스에서프로세스 는 프로그램의 동작하고 있는 실체 입니다. 솔라리스와 리눅스(2.6)에서 프로세스는 주소 공간을 위한 컨테이너 이자 하나 혹은 이상의 쓰레드 입니다. 시스템의 모든 프로세스들은 특수한 프로세스 ID(PID)를 가지고 있고 프로세스가 종료 된 후에도 일정 기간동안 유일성이 유지됩니다. 프로세스들은 fork(2) 와 이와 비슷한 동작을 하는 것들에 의해 만들어 집니다. 리눅스에서 프로세스(그리고 쓰레드)는 clone(2)을 사용하여 생성될 수 있습니다. 그러나 pthread_create(3) 가 좀더 포팅이 용이 합니다. 솔라리스에서 기술되지 않은 lwp_create() 시스템 콜은 어떠한 면에서 clone(2)과 비슷합니다.

vfork() 는 두 시스템에서 모두 비슷하게 동작합니다. 솔라리스는 fork1() 과 forkall() 을 가지고 있습니다. fork1() 의 경우, 자식 프로세스는 오직 fork() 콜을 실행 시키는 쓰레드를 가지도록 합니다; forkall() 의 경우, 부모 프로세스 안의 모든 쓰레드는 자식에게 복사 됩니다. 기본 fork 는 fork1() 입니다. forkall() 은 반드시 명시적으로 사용되어야 합니다. forkall() 은 리눅스에서는 존재 하지 않습니다.

ps -elfL 커맨드는 리눅스와 솔라리스 둘다에서 프로세스 내의 쓰레드를 확인 하는데 사용될 수 있습니다. 두 시스템 모두 LWP 의 숫자와 프로세스내의 각 쓰레드의 lwpid 를 보여 줍니다. 알아둘 점은 리눅스에서 lwpid 는 프로세스들 간에서도 유일한 숫자 입니다. 솔라리스에서lwpid는 프로세스 안에서만 유일합니다. 리눅스에서 멀티쓰레드 프로세스의 프로세스 ID는 사실 쓰레드 그룹의 ID입니다. 쓰레드 그룹 ID는 메인 쓰레드의 프로세스 ID와 동일 합니다. Sending a signal to any lwpid 에 시그널(kill(1)/kill(2)을 이용해서) 을 보내는 것은 프로세스에 시그널을 보내는 것과 동일 합니다. 솔라리스에서 사용자는 pid 로 시그널을 보냅니다. 두경우에서 만약 기본 동작이 취해진다면, 프로세스는 일반적으로 종료되고 쓰레드는 모두 중지 됩니다. ps(1) 맨페이지에서 자세한 사항을 참고 바랍니다.

리눅스와 솔라리스는 둘다 프로세서에 프로세스를 바인딩 하는 구문을 제공합니다. 리눅스는 각 프로세서를 상호배타적으로 사용한다는 가정하에 바인딩을 허락 합니다. 솔라리스는 배타적인 사용을 위해 프로세서의 셋에 바인딩 하는 것을 허락합니다. (CPU fencing 이라 불림) 그러나 그룹을 상호배타적인 용도로 바인딩 하는 것을 허락 하지 않습니다 (솔라리스 존은 제외). 리눅스는 CPU fencing 메카니즘을 가지고 있지 않습니다. 그러나 웹에서 관련된 구현은 찾을 수 있습니다. (예들 들어 bullopensource.org 사이트의 CPUSETS for Linux page ). 바인딩 작업을 하는 리눅스 시스템 콜은 sched_setaffinity(2) 와 sched_getaffinity(2) 입니다. 솔라리스에서는 다음과 같습니다:

  • processor_bind(2) 는 프로세서에 LWP 혹은 프로세를 바인드/언바인드
  • pset_create(2) 프로세서 셋을 셋팅
  • pbind(1) 와 psrset(1) 은 커맨드 라인 인터페이스를 제공

완벽한 설명을 위해 ps(1) 커맨드의 리눅스, 솔라리스 에서의 출력이 Threads 섹션에 기록되어 있습니다.

리눅스와 솔라리스 시스템에서 모든 형태의 exec 시스템 콜은 execve(2) 를 결과적으로 호출 하도록 되어 있습니다. 솔라리스 문서는 같은 맨 페이지에 6가지의 exec(2) 종류를 제공하고 있습니다. 리눅스의 exec(3) 맨 페이지에서는 execvexeclexecleexeclp, 그리고execvp 가 기록되어 있스빈다. 분리된 페이지로 execve(2) 를 다루고 있습니다.

/proc 파일 시스템은 리눅스와 솔라리스 상에서 약간 상이 합니다. 두개의 시스템에서 모두 /proc 는 현재 시스템에서 동작하고 있는 프로세스들의 ID들을 파일 이름으로 가지고 있는 파일들을 포함 합니다. 각 PID 로 명명된 파일들은 디렉토리가 됩니다. 리눅스의 /proc 는 다양한 다른 프로세스 이름과 더불어 다양한 다른 이름의 디렉토리들을 포함합니다. 이러한 것들의 대부분은 프로세서, 디바이스, 시스템 통계를 다루고 있습니다. 리눅스에서 사용자는/proc 을 통해 프로세스, 프로세서, 디바이스, 메인 아키텍쳐 등의 정보를 얻습니다. 솔라리스에는 이러한 종류의 정보를 보통 커맨드를 통해 얻습니다. 예를 들어 prtconf(1) 는 솔라리스의 머신 설정 정보를 얻는데 사용될 수 있습니다. 리눅스에서는 /proc 에 존재하는 파일을 조사 함으로써 정보를 얻습니다.

솔라리스에서 프로세스가 사용하는 가상 주소 공간에 대한 정보는 pmap(1) 을 사용하여 얻을 수 있고 리눅스에서는 /proc/pid/maps 파일을 봄으로써 얻을 수 있습니다. 솔라리스의 pmap(1) 과 리눅스의 proc(5) 맨페이지를 참고 하시기 바랍니다.

<-- on solaris, address space of this instance of bash -->
bash-3.00$ pmap -x $$  
1043:    /usr/bin/bash -i
 Address  Kbytes     RSS    Anon  Locked Mode   Mapped File
08045000      12      12       4       - rw---    [ stack ]
08050000     528     468       -       - r-x--  bash
080E3000      76      72       8       - rwx--  bash
080F6000     124     108      40       - rwx--    [ heap ]
FED8E000       4       4       -       - rwxs-    [ anon ]
FEDA0000       4       4       -       - rwx--    [ anon ]
FEDB0000     760     660       -       - r-x--  libc.so.1
FEE7E000      24      24       8       - rw---  libc.so.1
FEE84000       8       8       -       - rw---  libc.so.1
FEE90000      24       8       4       - rwx--    [ anon ]
FEEA0000     524     324       -       - r-x--  libnsl.so.1
FEF33000      20      20       4       - rw---  libnsl.so.1
FEF38000      32       -       -       - rw---  libnsl.so.1
FEF50000      44      40       -       - r-x--  libsocket.so.1
FEF6B000       4       4       -       - rw---  libsocket.so.1
FEF70000       4       4       4       - rwx--    [ anon ]
FEF80000     144     132       -       - r-x--  libcurses.so.1
FEFB4000      28      24       -       - rw---  libcurses.so.1
FEFBB000       8       -       -       - rw---  libcurses.so.1
FEFC0000       4       4       -       - r-x--  libdl.so.1
FEFC7000     140     140       -       - r-x--  ld.so.1
FEFFA000       4       4       4       - rwx--  ld.so.1
FEFFB000       8       8       4       - rwx--  ld.so.1
-------- ------- ------- ------- -------
total Kb    2528    2072      80       -
bash-3.00$ 

리눅스에서 동일한 정보는 아래의 그림 1 을 참고 바랍니다. 알아둘 점은 리눅스는 라이브러리의 완전한 경로 이름을 보여 준다는 것입니다.(결과는 라이브러리 이름을 보여주기 위해 약간 수정 되었음) 솔라리스에서 라이브러리의 완전한 경로 이름을 얻기 위해서는 pldd(1)를 사용합니다 .

 
사용자 삽입 이미지
그림 1: 리눅스에서 프로세스에 의해 사용된 가상 공간 주소를 검사


쓰레드

리눅스와 솔라리스는 POISIX 쓰레드를 지원 합니다. 리눅스는 The Native POSIX Thread Library for Linux 를 통해서 솔라리스는 표준 C 라이브러리의 일부로 제공하고 있습니다. Multithreaded Programming Guide 에 특별히 Chapter 5 Programming with the Solaris Software,를 참고하시기 바랍니다. Multithreading in the Solaris Operating Environment 문서도 추천할 만 합니다.

POSIC 쓰레드에 덧붙여서 솔라리스는 "솔라리스 쓰레드"를 제공합니다. threads(5) 맨 페이지는 POSIX 쓰레드 라이브러와 솔라리스 쓰레드 라이브러리의 유사점과 차이점에 대해 설명하고 있습니다. 구현은 상호 운용이 가능하고 동일한 어플리케이션에서 조금의 주의를 기울이면 같이 사용할 수 있습니다. 다음의 내용은 맨 페이지에서 바로 가져온 것입니다.

동일점

libpthread 와 libthread 라이브러리에 존재하는 대부분의 함수들은 각각의 라이브러리에 동일한 역할을 하는 함수를 가지고 있습니다. 세마포어 이름을 제외한 POSIX 함수의 이름은 "pthread" 프리픽스를 가지고 있습니다. POSIX와 솔라리스의 비슷한 함수 들은 유사한 끝부분을 가지고 있습니다. 전형적으로 POSIX 함수와 솔라리스 함수들은 같은 숫자의 인수들을 가지고 있습니다.

차이점
  • POSIX 쓰레드가 좀더 포팅이 용이 합니다.
  • POSIX 쓰레드는 설정이 가능한 속성 오브젝트에 따라 각 쓰레드에 특성을 가진 쓰레드를 만드는 것이 가능합니다.
  • POSIX 쓰레드는 쓰레드 취소를 구현하고 있습니다.
  • POSIX 쓰레드는 스케쥴링 알고리즘을 준수하고 있습니다.
  • POSIX pthreads 는 fork(2) 콜을 위한 클린-업 핸들러를 허용하고 있습니다.
  • 솔라리스 쓰레드는 중지/재개가 가능합니다.
  • 솔라리스 쓰레드는 견고한 뮤텍스 락을 가진 인터프로세스를 구현하고 있습니다.
  • 솔라리스 쓰레드는 데몬 쓰레드를 구현하여 프로세스를 종료할때 대기하지 않아도 됩니다.

아래의 프로그램은 간단한 멀티 쓰레드 프로그램입니다. 각 OS 에서 멀티 쓰레드 어플리케이션이 동작하는데에는 큰 차이가 없음을 알 수 있습니다. 물론 바닥에 있는 구현방법에는 몇가지 차이점이 있을 것입니다.

#include <pthread.h>
#include <stdio.h>

void *fcn(void *);

int
main(int argc, char *argv[])
{
    pthread_t tid;

    pthread_create(&tid, NULL, fcn, NULL);
    (void) printf("main thread id = %x\n", pthread_self());
    pthread_join(tid, NULL);
}

void *
fcn(void *arg)
{
    printf("new thread id = %x\n", pthread_self());
}

다음을 이용해서 솔라리스 플랫폼에서 프로그램을 컴파일 하고 수행 시킵니다:

bash-3.00$ cc simplepthread.c -o simplepthread
bash-3.00$ ./simplepthread
main thread id = 1
new thread id = 2
bash-3.00$ 

솔라리스에서 gcc 를 사용 하면 똑같은 결과를 얻을 수 있을 것입니다. 리눅스에서는 다음과 같이 나타 납니다:

max@linux:~/source> cc simplepthread.c
/tmp/cc8u7kZs.o(.text+0x1e): In function `main':
simplepthread.c: undefined reference to `pthread_create'
/tmp/cc8u7kZs.o(.text+0x4a):simplepthread.c: undefined reference
       to `pthread_join'
collect2: ld returned 1 exit status
max@linux:~/source> cc simplepthread.c -lpthread -o simplepthread
max@linux:~/source> ./simplepthread
main thread id = 4015c6c0
new thread id = 4035cbb0
max@linux:~/source> 

리눅스에서 POSIX 쓰레드 라이브러니는 반드시 명시적으로 링크되어야 합니다. 알아둘 점은 솔라리스 9 혹은 그 전에 버젼 역시 명시적으로 링크되어야 한다는 것입니다. 솔라리스10에서 POSIX 쓰레드는 표준 C 라이브러리에 존재 합니다. (libc.so). 또 한가지 알아 둘 점은 솔라리스는 1부터 꾸준히 증가하는 숫자를 쓰레드 ID로 지정 한다는 것입니다. 리눅스는 pthread 구조체의 사용자영역 가상 주소를 사용 합니다(구조체는 내부적으로 쓰레드 라이브러리에서 사용됨).

쓰레드는 두 시스템 모두에서 ps(1) 커맨드 혹은 /proc 파일 시스템을 통해 보여질 수 있습니다. 그림 2는 솔라리스의 ps(1) 커맨드 출력을 나타 내고 그림 3은 리눅스에서의 출력을 나타 냅니다. 여기서 알 수 있는 것은 똑같은 옵션을 주었을때 두 머신에서의 출력은 유사 하다는 것입니다.

사용자 삽입 이미지
그림 2: 솔라리스에서의 ps(1) 커맨드 출력


 
사용자 삽입 이미지
그림 3: 리눅스에서의 ps(1) 커맨드 출력


이 커맨드는 상태, 유저, PID, 부모 PID, LWP ID, LWP의 숫자 (유저 프로세스를 위한 숫자. 이것은 쓰레드의 숫자를 의미함), 스케줄링 클래스, 스케줄링 우선순위, 유저영역 가상 사이즈, 대기 채널, 시작 시간, tty, 수행 시간, 그리고 커맨드등을 보여 줍니다. 리눅스 ADDR 를 리포트 하지 않습니다. 그리고 솔라리스는 커널이 프로세스를 유지하는데 사용하는 proc_t 자료 구조의 (커널) 가상 어드레스를 보여 줍니다. 리눅스는WCHAN 을 심볼로 보여 주고 솔라리스는 그것을 주소로 보여 줍니다. 솔라리스에서 WCHAN 컬럼은 쓰레드가 블록됐을때 동기화 변수의 주소를 나타 냅니다. 리눅스에서 WCHAN 은 쓰레드가 sleep 상태 일때의 루틴을 나타냅니다. 솔라리스에서 똑같은 정보를 얻기 위해서는 mdb -k에서::threadlist -v 를 입력 합니다.

알아둘점은 64-비트 커널이 돌아가는 머신(SPARC 혹은 AMD64 아키텍쳐) 에서 ADDR 과 WCHAN 필드는 물음표를 나타낼 것입니다. 이 두 변수의 값을 보기 위해서는 ps -e -o addr,wchan,comm 을 입력합니다.

보통 사용자는 어플리케이션 쓰레드가 무엇을 하는지에 대해 관심이 많을 것입니다. 이럴때에는 pstack(1) 을 이용 합니다. 리눅스에서는pstack 을 이용합니다. 그러나 반드시 이것은 다운로드 받아져야 합니다. http://rpmfind.net/linux/RPM/ 를 참조 바랍니다. 주의할 점은 이것은 오직 하나의 쓰레드의 백트레이스 스택 많을 제공 한다는 것입니다(쓰레드 ID가 인수의 하나로 넘겨짐) 만약 모든 쓰레드의 백트레이스를 원한다면 쓰레드 ID를 하나의 분리된 인수로 넘겨 주어야 합니다.

 <-- get user-level stack(s) of a process on Solaris -->
bash-3.00$ pstack `pgrep mozilla-bin` 
21528: /usr/sfw/bin/../lib/mozilla/mozilla-bin -UILocale en-US
-----------------  lwp# 1 / thread# 1  --------------------
 fef68967 pollsys  (896dac8, 9, 0, 0)
 fef2b2aa poll     (896dac8, 9, ffffffff) + 52
 fe793242 g_main_context_iterate () + 39d
-----------------  lwp# 2 / thread# 2  --------------------
 fef68967 pollsys  (fbf5bd04, 1, 0, 0)
 fef2b2aa poll     (fbf5bd04, 1, ffffffff) + 52
 fede047d _pr_poll_with_poll (816fa0c, 1, ffffffff, fbf5bf64,
                                 fc0558aa, 816fa0c) + 2d5
 fede05f1 PR_Poll  (816fa0c, 1, ffffffff) + 11
 fc0558aa __1cYnsSocketTransportServiceEPoll6M_i_ (816f6b8) + 58
 fc055f7d __1cYnsSocketTransportServiceDRun6M_I_ (816f6b8) + 18f
 fc3d1262 __1cInsThreadEMain6Fpv_v_ (816eb60) + 32
 fede1693 _pt_root (816fcc0) + 9e
 fef67b30 _thr_setup (feec2400) + 51
 fef67f40 _lwp_start (feec2400, 0, 0, 0, 0, 0)
-----------------  lwp# 4 / thread# 4  --------------------
 fef67f7b lwp_park (0, fa87deb8, 0)
 fef620bb cond_wait_queue (825cfec, 816b8d0, fa87deb8, 0) + 3e
 fef62462 cond_wait_common (825cfec, 816b8d0, fa87deb8) + 1e9
 fef62691 _cond_timedwait (825cfec, 816b8d0, fa87df38) + 4a
 fef62722 cond_timedwait (825cfec, 816b8d0, fa87df38) + 27
 fef62761 pthread_cond_timedwait (825cfec, 816b8d0,
                                    fa87df38) + 21
 feddc598 pt_TimedWait (825cfec, 816b8d0, f1c) + b8
 feddc767 PR_WaitCondVar (825cfe8, f1c) + 64
 fc3d417e __1cLTimerThreadDRun6M_I_ (81e5108) + 16e
 fc3d1262 __1cInsThreadEMain6Fpv_v_ (820d690) + 32
 fede1693 _pt_root (820e6b0) + 9e
 fef67b30 _thr_setup (fb520400) + 51
 fef67f40 _lwp_start (fb520400, 0, 0, 0, 0, 0)
bash-3.00$ 

리눅스에서의 동일한 결과가 아래에 있습니다. 흥미로운 것은 Mozilla나 xemacs 같은 프로그램은 리눅스에서 strip 되 있고 솔라리스에서는 strip 되있지 않습니다.

max@linux:~> cd /proc/`pgrep mozilla`/task
max@linux:/proc/3991/task> pstack *

3991: /opt/mozilla/lib/mozilla-bin
(No symbols found)
0xffffe410: ???? (8803488, 8, ffffffff, 8803488, 9, 400fbea0) + 40
0x404b0a6d: ???? (8129258, 4035236c, 57f, 4011e4e6, 4048de14,
                   403513c4) + 20
0x404b0d07: ???? (814b898, 814b898, 0, 0, 415a8f64, 814b898) + 30
0x401dc11f: ???? (8106350, bfffee80, bfffede8, 807673e, 8084cf4, 0)
0x415c4006: ???? (8106350, 0)
0x414fbae4: ???? (8105ee8, 0, 8079c2c, bfffee90, 80a67b8,
                      40ad841c) + 1f0
0x08059b7c: ???? (80e7f08, bffff058, 40017068, 14, 4081ccf8,
                       1) + 90
0x08055a47: ???? (1, bffff134, bffff13c, 4081ccf8, 406eebd0,
                     400168c0) + 40
0x405f2500: ???? (8055840, 1, bffff134, 80557b0, 8055740, 
                     4000d330) + 40000ed8

4001: /opt/mozilla/lib/mozilla-bin
(No symbols found)
0xffffe410: ???? (413eb7f0, 1, ffffffff, 18, 413eb7f8, 0) + 230
0x400c7439: ???? (818911c, 1, ffffffff, 40c5a0a8, ffffffff,
                   8188dec)
0x40bc8a52: ???? (8188dc8, 8188df4, 1, 8188dec, 8188f7c, 1) + 10
0x40bc8bcb: ???? (8188dc8, 413ebbb0, 40102ce0, 400d5238,
                   8189478, 0)
0x40a8da6b: ???? (81893f8, 8189478, 4000ca40, 40102be8, 0, 0)
0x400cb7a6: ???? (8189478, 413ebac4, 0, 0, 0, 0) + 54
0x400fa9dd: ???? (413ebbb0, 0, 0, 0, 0, 0) + bec144d4

4004: /opt/mozilla/lib/mozilla-bin
(No symbols found)
0xffffe410: ???? (40656756, 400d5238, 81ed160, 81ed2d0, 41ffba08,
                   400c5721) + 170fd55
crawl: Input/output error
Error tracing through process 4004
0x1afcdbf8: ????max@linux:/proc/3991/task> 

솔라리스 쓰레드는 기본적으로 유저 스택 사이즈를 1MB로 지정 합니다. 리눅스에서는 2MB 입니다.(수세 9.1)

동기화

두 운영체제는 POSIX 동기화 메카니즘을 지원합니다(예: 뮤텍스, 조건 변수, 읽기/쓰기 락, 세마포어, 베리어등) 기본적으로 메카니즘은 뮤텍스에 바탕을 두고 있습니다. 솔라리스에서 사용자-레벨의 뮤텍스는 "adaptive" 스핀 락을 사용해서 구현 됩니다. 리눅스에서 메카니즘은 "futex" 혹은 fast user level mutex 입니다. 두 메카니즘 모두 비경합 조건에서 커널에 들어가는 것을 회피 하고 반드시 비교할수 있을 만한 성능과 동작을 제공해야 합니다.

솔라리스 사용자-레벨의 "adaptive" 스핀 뮤텍스는 Multithreading in the the Solaris Operating Environment (pdf) 에 자세한 설명이 있습니다. 리눅스의 futex는 Futexes Are Tricky (pdf) 에 자세한 설명이 있습니다.

솔라리스 메카니즘인 lwp_park() 와 lwp_unpark() 그리고 리눅스 메카니즘인 futex_up() 와 futex_down() 은 어플리케이션에서 사용될 수 있습니다. 그러나 필자는 어떠한 소스 코드 예제도 찾지 못했습니다. 아마도 POSIX API들을 사용 하는 것이 가장 좋을 것입니다. 만약 POSIX 라킹 메카니즘에 상대적인 속도 차이를 비교하고자 한다면 (혹은 다양한 다른 라이브러리 루틴, 시스템 콜의 성능 같은) 필자는 libmicro 마이크로 벤치마크의 카피를 얻을 것을 관장하고 그것을 솔라리스와 리눅스에서 사용해 보길 권장 합니다. (libmicro는 여기 에서 다운로드 받으실 수 있습니다)

메모리 관리

커널의 메모리 다루는 방법에 대한 차이를 설명하는 대신 필자는 유저 레벨에서 몇가지 다른 메모리 할당 라이브러리가 존재 하고 대부분 두 OS 에서 사용 할 수 있음 만을 밝히려고 합니다. 유저레벨 메모리 할당자의 비교는 SDN의 A Comparison of Memory Allocators in Multiprocessors 에서 찾아 보실 수 있습니다. http://gee.cs.oswego.edu/dl/html/malloc.html 에 "A Memory Allocator" 는 리눅스에서 사용되는 (조금 지난) 메모리 할당자들에 대한 설명을 포함 하고 있습니다. 또한 소스 코드에서도 관련 커멘트를 찾아 볼 수 있습니다.

타이머

어플리케이션 레벨에서 솔라리스와 리눅스는 둘다 timer_create()timer_delete(), 그리고 nanosleep() 를 포함한 POSIX 타이머 루틴을 제공합니다. 솔라리스는 추가적으로 CLOCK_HIGHRES 라는 타이머를 제공하며 이것은 최적의 하드웨어 소스를 사용하도록 시도 하고 nano 초 수준의 정밀함을 제공 할 것입니다. CLOCK_HIGH_RES 타이머는 아마 미슷한 수준의 정밀성을 리눅스에서 제공할 것입니다. 그러나 커널 패치의 일부로 설치 되어야 합니다.(http://high-res-timers.sourceforge.net/ 에서 고 정밀도 타이머 프로젝트의 홈페이지를 찾으실 수 있습니다). 다음의 예제 코드는 CLOCK_HIGHRES 타이머를 사용하여 유저가 지정한 간격, 유저가 지정한 시간 동안 타이머가 동작 하도록 합니다. 간격은 nano 초 수준으로 지정 되고 동작 시간은 초 수준으로 지정 됩니다. 프로그램이 실행을 완료 하면 타이머가 몇번 동작 했는지와 타이머가 "overrun" 했는지 숫자로 보여 줍니다. 여기서 "overrun" 값이란 타이머가 처음 동작한 시간(시그널이 생성되도록 함)과 시그널이 처리되는 시간 (timer_getoverrun(3RT) 참조) 과의 차이를 의미 합니다. 간격을 너무 짧게 줄 경우 시스템이 정지해 버릴 수도 있습니다.

#include <pthread.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>

#include <errno.h>

#define DURATION 120    /* default time to run in seconds */

  /* default .5 seconds in nanosecs */
#define INTERVAL (1000*1000*500)

void* timer_fcn(void* arg);
void* signaler_thd(void* arg);

/* Program globals */
extern int errno;
int duration = DURATION;
int interval = INTERVAL;

int
main(int argc, char *argv[]) 
{
   sigset_t mask;
   pthread_t wtid = 0;
   pthread_t stid = 0;
   int rval;
   int n;

   if (argc >=2) {
       errno = 0;
       if (argc == 2)
         duration = strtol(argv[1], NULL, 0);
       else if (argc == 3) {
         interval = strtol(argv[1], NULL, 0);
         duration = strtol(argv[2], NULL, 0);
       }
       if (errno || argc > 3 || interval <= 0
          || duration <= 0) {
           fprintf(stderr, "Usage: %s [[interval] duration]\n",
                  argv[0]);
           fprintf(stderr, "interval nsecs, duration seconds\n");
           exit(1);
       }
   }
     
   /* mask SIGALRM signals */
   sigemptyset(&mask);
   sigaddset(&mask, SIGALRM);
   sigaddset(&mask, SIGUSR1);
   rval = pthread_sigmask(SIG_BLOCK, &mask, NULL);
   if(rval != 0) {
      printf("%s: pthread_sigmask failed, errno = %d.\n",
             argv[0], rval);
      exit(1);
   }

   rval = pthread_create(&wtid, NULL, timer_fcn, NULL);
   if (rval != 0) {  /* Waiter create call create failed */
    perror ("Waiter create");
    printf ("Waiter create call failed: %d.\n", rval);
    exit (1);
    }


   /* Do signaler thread */
   rval = pthread_create(&stid, NULL, signaler_thd, &mask);
   if (rval != 0) {  /* Signaler call create failed */
    printf ("Signaler call create failed: %d.\n", rval);
    exit (1);
   }

   /* Wait for waiter and signaler to finish */    
   rval = pthread_join(stid, NULL);
   if (rval != 0) {  /* Signaler call join failed */
    printf ("Signaler call join failed: %d.\n", rval);
    exit (1);
   }

   rval = pthread_join(wtid, NULL);
   if (rval != 0) {  /* Waiter call join failed */
    printf ("Waiter call join failed: %d.\n", rval);
    exit (1);
   }

   printf("done\n");
   exit(0);
}

pthread_mutex_t mp;
pthread_cond_t cv;
int time_expired = 0;
int timerentered;
int timeroverrun;
timer_t itimerid;

void *
timer_fcn(void *arg)
{
  struct itimerspec value;
  struct sigevent event;

  value.it_interval.tv_sec = 0;
  value.it_interval.tv_nsec = interval;  /* nsec intervals  */
  value.it_value.tv_sec = 1;  /* starting in 1 second */
  value.it_value.tv_nsec = 0;  /* plus 0 nanosecs */

  event.sigev_notify = SIGEV_SIGNAL;
  event.sigev_signo = SIGALRM;
  event.sigev_value.sival_int = 0;
  

  if (timer_create(CLOCK_HIGHRES, &event,
     &itimerid) == -1) {
       perror("timer_create failed");
       exit(1);
  }

  /* the second arg can be set to TIMER_ABSTIME */
  if (timer_settime(itimerid, 0, &value, NULL) == -1) {
      /* else time value is relative to when the call is made */
    perror("timer_settime failed");
    exit(1);
  }

  pthread_mutex_lock(&mp);
  while (time_expired == 0)
    pthread_cond_wait(&cv, &mp);
  printf("timerentered = %d\n", timerentered);
  printf("timeroverrun = %d\n", timeroverrun);
  pthread_mutex_unlock(&mp);
  exit(0);
}

int timerset;

void *
signaler_thd(void *arg)
{
    int signo;
    
    while (1) {
      signo = sigwait(arg);
      if (signo == SIGALRM) {
       if (!timerset) {
        struct itimerspec value;
        struct sigevent event;

        timer_t endtimerid;

        ++timerset;
        value.it_interval.tv_sec = 0;
        value.it_interval.tv_nsec = 0;
        value.it_value.tv_sec = duration; /*wait duration secs*/
        value.it_value.tv_nsec = 0;  /* plus 0 nanosecs */

        event.sigev_notify = SIGEV_SIGNAL;
        event.sigev_signo = SIGUSR1;
        event.sigev_value.sival_int = 0;
  

        if (timer_create(CLOCK_HIGHRES, &event,
         &endtimerid) == -1) {
           perror("timer_create failed");
           exit(1);
        }

        /* the second arg can be set to TIMER_ABSTIME */
        if (timer_settime(endtimerid, 0, &value, NULL)
          == -1) {
          perror("timer_settime failed");
          exit(1);
        }
       } else {  /* if (!timerset) */
        ++timerentered;
        timeroverrun += timer_getoverrun(itimerid);
       }
      } else {  /* SIGUSR1 */

       struct itimerspec value;
       struct sigevent event;


       /* cancel the interval timer */
       value.it_interval.tv_sec = 0;
       value.it_interval.tv_nsec = 0;  /* nanosecond intervals */
       /* setting the following to 0 should stop the timer */
       value.it_value.tv_sec = 0;
       value.it_value.tv_nsec = 0;  /* plus 0 nanosecs */

       event.sigev_notify = SIGEV_SIGNAL;
       event.sigev_signo = SIGALRM;
       event.sigev_value.sival_int = 0;
  
       pthread_mutex_lock(&mp);
       if (timer_settime(itimerid, 0, &value, NULL) == -1) {
        perror("timer_settime failed");
        exit(1);
       }

       ++time_expired;
       pthread_cond_signal(&cv);
       pthread_mutex_unlock(&mp);
      }
   }
}

컴파일 된 코드의 실행 예가 있습니다.

  <-- realtime library and best optimization -->
bash-3.00$ cc timerex1.c -lrt -o timerex1 -O -fast
bash-3.00$ ./timerex1  <-- only root can use high res timer
timer_create failed: Not owner
bash-3.00$ su
Password: 
  <-- default interval is .5 seconds, duration is 120 seconds -->
# ./timerex1  
timerentered = 240  <-- timer fired every .5 seconds
timeroverrun = 0
# ./timerex1 1000000 10  <-- interval is 1 msec for 10 secs
timerentered = 9912
timeroverrun = 88
# priocntl -e -c RT ./timerex1 1000000 10  <-- run it real time
timerentered = 10000  <-- timer fired once each msec for 10 secs
timeroverrun = 0
# ./timerex1 100000 10  <-- interval is 100 usecs for 10 seconds
timerentered = 99615  <-- we missed a few
timeroverrun = 386
# priocntl -e -c RT ./timerex1 100000 10  <-- try real time 
timerentered = 99871  <-- almost 1 every 100 microseconds
timeroverrun = 129
# ./timerex1 10000 10  <-- interval is 10 microseconds
timerentered = 485905  <-- here we miss over half
timeroverrun = 514125  <-- (sig handler takes > 10 usecs?)
 <-- using RT 1 usec interval causes hang on my machine -->

# priocntl -e -c RT ./timerex1 1000 10 

IPC

솔라리스와 리눅스 둘다 시스템 V IPC (공유 메모리, 메세지 큐, 세마포어)를 지원 합니다. 두 시스템 모두 파이프와 실 시간 공유 메모리 작업(shm_open()shm_unlink(), 등등)을 지원합니다. 두 시스템 모두 tmpfs 파일 시스템을 지원합니다(파일을 위해 메모리와 스왑 공간을 사용) 솔라리스는 tmpfs 내에 /tmp/var/run, 그리고 /etc/svc/volatile 을 위치 시킵니다 리눅스는 /dev/shm 을 사용합니다. 두 시스템 모두 다른 마운트 지점을 추가 하는 것을 허용 합니다.

솔라리스에서 tmpfs 를 사용 하는 과정이 설명 되어 있습니다; 리눅스를 위한 과정은 아래에 설명 됩니다. 알아둘 점은 솔라리스의 "swap"은 메모리와 디스크를 둘다 사용 합니다. (디스크는 필요시에만) 다시 말해서 /tmp 파일에 생성되는 파일은 메모리에 저장 됩니다. 메모리가 꽉 찰 경우 pageout 데몬이 /tmp 에서 데이타를 디스크에 스왑 스페이스 옮기게 됩니다.

# mkdir /foo
<-- create a tmpfs file system using swap on /foo
# mount -F tmpfs swap /foo  
# df -h /foo
Filesystem         size   used  avail capacity  Mounted on
swap           652M     0K   652M     0%    /foo
# df -h /tmp
Filesystem         size   used  avail capacity  Mounted on
swap           652M    52K   652M     1%    /tmp
# 

아래에 리눅스상에서 비슷한 과정을 설명합니다.

linux:/home/max # mkdir /foo
 <-- tmpfs also uses swap space and memory -->
linux:/home/max # mount tmpfs /foo -t tmpfs 
linux:/home/max # df -h /foo
Filesystem        Size  Used Avail Use% Mounted on
tmpfs         248M     0  248M   0% /foo
linux:/home/max # df -h /dev/shm
Filesystem        Size  Used Avail Use% Mounted on
tmpfs         248M   16K  248M   1% /dev/shm
linux:/home/max # 

이 글의 앞부분에서 언급한대로 libmicro 벤치마크를 수행하여 두 시스템의 상대적인 성능을 비교해 보는 것은 꽤 흥미로울 것입니다.

시그널 핸들링

솔라리스와 리눅스는 시그널을 비슷하게 처리 합니다. 솔라리스에 존재하는 몇몇 시그널은 리눅스에 존재 하지 않고 그 반대도 마찬가지 ?니다. 또한 몇몇 시그널은 다른 시그널 번호를 사용 합니다. 두가지 모두 signal() 위에 sigaction(2) 을 사용하여 시그널을 캐치 하고 멀티 쓰레드 어플리케이션에서 sigwait() 를 사용 하여 비동기 시그널을 처리 합니다. 리눅스에서 sigwait(3) 메뉴얼 페이지는 BUGS 섹션을 가지고 있습니다. 리눅스 시그널 핸들링은 POSIX 표준과는 다릅니다. POSIX에서 비동기 적으로 전달 되는 시그널(프로세스외 외부적으로 전달 되는 시그널)은 현재 블럭된 시그널을 가지고 있지 않는 모든 쓰레드에 의해 처리 됩니다. 리눅스에서 비동기 시그널은 어떤 특정한 쓰레드(시그널은 kill(1)을 통해 특정 쓰레드로 전달될 수 있음)에 전달 됩니다. 솔라리스는 이 경우 POSIX 표준을 구현 합니다. 즉 프로세스의 특정한 쓰레드에 직접적으로 시그널을 전달 할 수 있는 방법은 없습니다. 하나의 방법으로는 프로세스에 특정한 쓰레드로가 아니라 kill(1) 을 통해 프로세스로 전달 하는 것이 있습니다.

http://lsbbook.gforge.freestandards.org/sig-handling.html 에 있는 "Building Applications with the Linux Standard Base"는 몇가지 이러한 차이점들을 기술 하고 있습니다. 주의할 점은 이 페이지는 완벽하게 정확하지는 않다는 것입니다. 예를 들어 이 페이지에서 리눅스에서는 "버스 에러"가 없다는 것을 나타내기 위해 SIGBUS 를 SIGUNUSED 로 지정한다고 설명되어 있습니다. 리눅스의 mmap(2) 맨 페이지에서 설명하고 있는 바로는 mmap 이 사용 되었단 파일에서의 올바른 위치와 부응하지 않는 메모리 범위를 엑세스 할때 SIGBUS 를 받게 된다고 되어 있습니다. (솔라리스도 똑같이 동작함).

솔라리스와 리눅스 두 OS 모두 시그널은 커널에서 유저 모드로 쓰레드가 돌아갈때 pending 된 시그널이 발견 되었을 시에 처리 됩니다. 두 시스템 모두 SIGKILL 과 SIGSTOP 은 다른 시그널에 비교해서 우선순위가 높습니다. 그렇지 않다면 솔라리스에서 시그널은 문서화되지 않은 순서대로 처리 됩니다(낮은 시그널 숫자가 먼저 처리 됨). 리눅스에서 시그널은 전달된 순서대로 처리 됩니다. (SIGKILL 과 SIGSTOP 은 예외).

솔라리스에서 수행중인 프로세스의 시그널 설정을 보기 위해서는 psig 를 사용합니다.

bash-3.00$ psig $$  <-- signal disp for current shell
954:	/usr/bin/bash -i
HUP	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
INT	caught	sigint_sighandler	0
QUIT	ignored
ILL	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
TRAP	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
ABRT	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
EMT	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
FPE	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
KILL	default
BUS	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
SEGV	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
SYS	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
PIPE	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
ALRM	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
TERM	ignored
USR1	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
USR2	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
CLD	blocked,caught	0x807d4d7 	0
PWR	default
WINCH	caught	0x807e182   0  <-- not all syms are present
URG	default
POLL	default
STOP	default
TSTP	ignored
CONT	default
TTIN	ignored
TTOU	ignored
VTALRM	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
PROF	default
XCPU	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
XFSZ	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
WAITING	default
LWP	default
FREEZE	default
THAW	default
CANCEL	default
LOST	caught	termination_unwind_protect	0	HUP,INT,
  ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,
  VTALRM,XCPU,XFSZ,LOST
XRES	default
JVM1	default
JVM2	default
RTMIN	default
RTMIN+1	default
RTMIN+2	default
RTMIN+3	default
RTMAX-3	default
RTMAX-2	default
RTMAX-1	default
RTMAX	default
bash-3.00$ 

필자가 얘기 할 수 있는 것은 리눅스에서 이러한 작업을 쉽게 할 수 있는 방법은 없습니다. 그러나 몇몇 개발자들이 아마 커널 패치/모듈을 구현 해서 개발자가 정보를 얻을 수 있도록 했습니다.

결론

일반적으로 리눅스 혹은 솔라리스에서 개발자가 POSIX-호환의 어플리케이션을 개발 하고 있다면 어플리케인션 반드시 간단한 재컴파일 과정을 통해 각 OS로 포팅 되어야 합니다. 물론 많은 어플리케이션은 POSIX 와 부합하지 않는 부분을 가지고 있습니다. 예를 들어 디바이스ioctl(2) 은 OS에 따라 다르게 되어 있습니다.(그리고 물론 디바이스에 따라)

솔라리스를 위한 문서를 얻는 것은 이성적으로 매우 직관적입니다. 왜냐하면 모든 문서가 http://docs.sun.com에 존재하기 때문입니다. 물론 리눅스를 위한 문서를 얻는 과정도 간단하지만 (웹검색을 통해) 종종 어려울 경우도 있습니다. 개발자는 리눅스가 전형적으로 동일한 작업을 하기 위해 많은 방법이 있음을 발견하게 될 것입니다.(예를 들어 쓰레드의 구현 차이 등) 필자의 개인적은 인상은 리눅스의 문서는 종종 소스 코드 자체가 될 수도 있습니다. 만약 소스 코드에 대한 전체 접근이 가능할 경우 이것은 괜찮은 방법입니다. 개발자는 모든 소스 코드에 대한 접근 권한을 물론 가지고 있지만 이것이 한 장소에 모여 있지는 않습니다. 사실 그것은 여러군데에 퍼져 있는 것처럼 보입니다. 썬의 소스는 현재 (http://www.opensolaris.org) 한군데에 모여 있습니다.

이 글은 두 시스템에서 사용 가능한 툴을 모두 사용하였지만 자세한 부분 까지 파고 들지는 않았습니다. 썬이 오픈솔라리스로 전환하기 전에 리눅스의 발표는 소스 코드에 대한 가시성 면에서 봤을때 항상 어떻게 작업이 돌아 가는지 알 수 있도록 소스 코드가 항상 공개 되있으므로 솔라리스에 비해 한발짝 앞서 있었습니다. 이제는 오픈솔라리스의 DTrace 같은 툴을 통해 리눅스가 솔라리스를 따라와야 하는 상황입니다. 리눅스의 변화 속도를 볼때 필자가 확신 할 수는 없지만 그 시간이 오래 걸리지는 않을 것입니다. 필자는 각 시스템의 서로의 장점과 실수를 배움으로써 상호 보완관계로 발전하기를 바랍니다.

출처 : http://wiki.kldp.org/wiki.php/ShellProgrammingTutorial

쉘 프로그래밍 강좌

  • 참고서적: 초보자용 리눅스 프로그래밍 (대림출판사, 한동훈,이만용역, NEIL MATTHEW, RICHARD STONES 저)
  • 넷츠고 리눅스 동호회 7월 제 5회 정기 공개강좌 자료
  • 글쓴이: 위경섭 <powerhack@netsgo.com>
  • 위키문서 변환: 윤현호 <hhyoon@kldp.org> 2005년 2월 23일. 원본: http://wiki.kldp.org/KoreanDoc/Shell_Programming-KLDP


1 변수

  • 쉘변수는 처음 사용될 때 만들어진다. 즉 미리 선언할 필요가 없다.
  • 쉘변수는 유닉스 명령과 마찬가지로 대소문자에 구별이 있다.
  • 쉘변수는 기본적으로 데이터를 문자열로 저장한다. 수치를 대입해도 실제 수치가 아닌 문자열이 저장된다. 계산이 필요할 경우는 자동으로 수치로 변환하여 계산후 다시 문자열로 저장된다.
  • 쉘변수의 값을 사용할 때는 변수명앞에 "$" 를 붙여서 사용한다.
  • 쉘변수에 값을 대입할때는 "$"를 사용하지 않는다.
  • 쉘변수는 타입이 없다. 즉 아무 값이나 다 넣을 수 있다.

1.1 환경변수

쉘을 기동하고나면 기본적으로 셋팅되어있는 변수들이다. 유닉스/리눅스에는 많은 환경변수들이 있고 필요한경우 이 변수들을 마치 일반변수처럼 값을 얻어오거나 셋팅할 수 있다. 여기서는 쉘과 직접적인 관련이 있는것만 설명한다.
  • $0 - 실행된 쉘 스크립트 이름
  • $# - 스크립트에 넘겨진 인자의 갯수
  • $$ - 쉘 스크립트의 프로세스 ID

1.2 인자 변수

쉘스크립트에 인자를 넘겨줄때 그 인자들에 대한 정보를 가지고 있는 변수들.
  • $1~ $nnn - 넘겨진 인자들
  • $* - 스크립트에 전달된 인자들을 모아놓은 문자열. 하나의 변수에 저장되며 IFS 환경변수의 첫번째 문자로 구분된다.
  • $@ - $*과 같다. 다만 구분자가 IFS변수의 영향을 받지 않는다.

2 일반변수

일반변수에 특별한 제약은 없다. 단 대소문자 구분만 정확하게 해주면 된다.

예제
#!/bin/sh
echo "This Script Executable File : $0"
echo "Argument Count : $#"
echo "Process ID : $$"
echo "Argument List \$* : $*"
echo "Argument List \$@ : $@"
echo "Argument 1 : $1"
echo "Argument 2 : $2"
echo "Argument 3 : $3"
echo "Argument 4 : $4"  
실행
$chmod 755 test1
$./test1 a1 a2 a3 a4
This Script Executable File : ./test1
Argument Count : 4
Process ID : 905
Argument List $* : a1 a2 a3 a4
Argument List $@ : a1 a2 a3 a4
Argument 1 : a1
Argument 2 : a2
Argument 3 : a3
Argument 4 : a4

2.1 연산

변수의 산술 연산은 생각하는 것처럼 쉽지 않다. 위에서 언급했듯이 변수에는 모든 것이 문자열로 저장되기 때문에 연산이 불가능하다. 연산을 위해서는 좀 복잡한 절차를 거쳐야 한다.
변수 = $((산술식))
이것이 가장 단순한 연산 규칙이다. 산술식내에는 변수($1, $a 와 같은) 도 들어갈 수 있다. 산술식 내에 숫자가 아닌 문자열, 또는 문자열이 담겨있는 변수가 들어가면 그것들은 계산에서 제외된다. (정확히 말하면 0 으로 간주되어 연산이 이루어 지지 않는다.)

2.2 매개변수 확장

매개변수 확장이란 변수의 값을 문자열 등으로 대체하는 것을 말한다. 단순한 대체뿐 아니라 변수내의 문자열을 조작하여 원하는 문자열만을 추출할 수도 있다.

형식:
  • ${parm:-default} - parm이 존재하지 않으면 default로 대체된다.
  • ${#parm} - parm의 길이를 참조한다.(가져온다)
  • ${parm%word} - 끝에서부터 word와 일치하는 parm의 최소부분(첫번째 일치)을 제거하고 나머지를 반환한다.
  • ${parm%%word} - 끝에서부터 word와 일치하는 parm의 최대부분(마지막 일치)을 제거하고 나머지를 반환한다.
  • ${parm#word} - 처음부터 word와 맞는 parm의 최소부분(첫번째 일치)을 제거하고 나머지 부분을 반환한다.
  • ${parm##word} - 처음부터 word와 맞는 parm의 최대부분(마지막 일치)을 제거하고 나머지를 반환한다.
word에는 와일드 카드를 사용할 수 있다.예를 보자.
1 #!/bin/sh
2
3 p="/usr/X11R6/bin/startx"
4
5 unset p
6 a=${p:-"Variable p Not found"}
7 echo $a
8
9 p="/usr/X11R6/bin/startx"
10 a=${p:-"Variable parm Not found"}
11 echo $a
12
13 a=${#p}
14 echo $a
15
16 a=${p%/*}
17 echo $a
18
19 a=${p%%/*}
20 echo $a
21
22 a=${p#*/}
23 echo $a
24
25 a=${p##*/}
26 echo $a
27                    
위 스크립트의 결과는 다음과 같다.
Variable p Not found
/usr/X11R6/bin/startx
21
/usr/X11R6/bin

usr/X11R6/bin/startx
startx
  • 6행 : 변수 p 가 제거 되었으므로 "Variable p Not found" 가 a에 들어간다.
  • 10행 : 변수 p 가 있으므로 그대로 a에 들어간다.
  • 13행 : a에는 변수 p의 길이가 들어간다.
  • 16행 : p 에서 가장 오른쪽의 "/"부터 끝까지 지우고 나머지를 a에 넣는다.
  • 19행 : p 에서 가장 왼쪽의 "/" 부터 끝까지 지우고 나머지를 a에 넣는다. (아무것도 없다)
  • 22행 : p 의 처음부터 가장왼쪽의 "/" 까지 지우고 나머지를 a에 넣는다.
  • 25행 : p 의 처음부터 가장 오른쪽의 "/"까지 지우고 나머지를 a에 넣는다.

3 조건 판단

쉘 스크립트에서 조건판단은 if 와 test 명령을 혼합하여 사용한다. 일반적인 예는 다음과 같다.
if test -f test1
then
...
fi

-f 는 주어진 인자가 일반 파일일 때 참이 된다.

test 명령은 [] 로 대체될 수 있다.
if [ -f test1 ]
then
...
fi

if [ -f test1 ]; then
...
fi

3.1 test 명령

test 명령의 조건은 다음과 같이 세 부류로 나누어진다.

3.1.1 문자열 비교

  • [ string ] - string이 빈 문자열이 아니라면 참
  • [ string1 = string2 ] - 두 문자열이 같다면 참
  • [ string1 != string2 ] - 두 문자열이 다르면 참
  • [ -n string ] - 문자열이 null(빈 문자열) 이 아니라면 참
  • [ -z string ] - 문자열이 null(빈 문자열) 이라면 참

3.1.2 산술 비교

  • [ expr1 -eq expr2 ] - 두 표현식 값이 같다면 참 ('EQual')
  • [ expr1 -ne expr2 ] - 두 표현식 값이 같지 않다면 참 ('Not Equal')
  • [ expr1 -gt expr2 ] - expr1 > expr2 이면 참 ('Greater Than')
  • [ expr1 -ge expr2 ] - expr1 >= expr2 이면 참 ('Greater Equal')
  • [ expr1 -lt expr2 ] - expr1 < expr2 이면 참 ('Less Than')
  • [ expr1 -le expr2 ] - expr1 <= expr2 이면 참 ('Less Equal')
  • [ ! expr ] - expr 이 참이면 거짓, 거짓이면 참
  • [ expr1 -a expr2 ] - expr1 AND expr2 의 결과 (둘다 참이면 참, 'And')
  • [ expr1 -o expr2 ] - expr1 OR expr2 의 결과 (둘중 하나만 참이면 참, 'Or')

3.1.3 파일 조건

  • [ -b FILE ] - FILE 이 블럭 디바이스 이면 참
  • [ -c FILE ] - FILE 이 문자 디바이스 이면 참.
  • [ -d FILE ] - FILE 이 디렉토리이면 참
  • [ -e FILE ] - FILE 이 존재하면 참
  • [ -f FILE ] - FILE 이 존재하고 정규파일이면 참
  • [ -g FILE ] - FILE 이 set-group-id 파일이면 참
  • [ -h FILE ] - FILE 이 심볼릭 링크이면 참
  • [ -L FILE ] - FILE 이 심볼릭 링크이면 참
  • [ -k FILE ] - FILE 이 Sticky bit 가 셋팅되어 있으면 참
  • [ -p FILE ] - True if file is a named pipe.
  • [ -r FILE ] - 현재 사용자가 읽을 수 있는 파일이면 참
  • [ -s FILE ] - 파일이 비어있지 않으면 참
  • [ -S FILE ] - 소켓 디바이스이면 참
  • [ -t FD ] - FD 가 열려진 터미널이면 참
  • [ -u FILE ] - FILE 이 set-user-id 파일이면 참
  • [ -w FILE ] - 현재 사용자가 쓸 수 있는 파일(writable file) 이면 참
  • [ -x FILE ] - 현재사용자가 실행할 수 있는 파일(Executable file) 이면 참
  • [ -O FILE ] - FILE 의 소유자가 현재 사용자이면 참
  • [ -G FILE ] - FILE 의 그룹이 현재 사용자의 그룹과 같으면 참
  • [ FILE1 -nt FILE2 ] - : FILE1이 FILE2 보다 새로운 파일이면 ( 최근파일이면 ) 참
  • [ FILE1 -ot FILE2 ] - : FILE1이 FILE2 보다 오래된 파일이면 참
  • [ FILE1 -ef FILE2 ] - : FILE1 이 FILE2의 하드링크 파일이면 참

3.2 if 구문

if 문은 조건을 판단하여 주어진 문장을 수행한다.

3.2.1 형식 1 (단일 if 문)

형식:
if [ 조건 ]
then
    문장1
    문장2
fi

3.2.2 형식 2 (if-else 문)

형식:
if [ 조건 ]
then
    문장3
    문장4
fi

3.2.3 형식 3 (if-elif 문)

형식:
if [ 조건 ]
then
    문장1
    문장2
elif
    문장3
    문장4
else
    문장5
    문장6
fi

3.3 case 구문

'패턴'에는 * 문자, 즉 와일드카드를 사용할 수 있다.

형식:
case 변수 in
패턴 [ | 패턴 ] ... ) 문장 ;;
패턴 [ | 패턴 ] ... ) 문장 ;;
....
* ) 문장 ;;
esac

3.4 목록

여러 명령을 실행할때 앞의 명령의 결과에 의해서 다음행동이 결정되어야 할 경우가 있다. 이런경우에 AND나 OR조건을 사용해서 한번에 처리할 수 있다. 이것은 쉘 스크립트 뿐 아니라 명령행에서도 사용 가능하다. 물론 if 문을 이용해서 반환값을 검사하여 처리할 수 있지만 문장이 길어지고 복잡해진다.

3.4.1 AND 목록

statment1 && statment2 && statmentN && .....

위의 명령들은 각 명령이 거짓이 될 때 까지 명령을 수행해 나간다. 수행 도중 결과가 거짓이 되면 그이후의 명령은 수행되지 않는다.

3.4.2 OR 목록

statment1 || statment2 || statmentN || .....

위의 명령들은 각 명령이 거짓이 나오는 동안 계속된다. 즉 참이 나오면 실행을 멈춘다.

3.4.3 AND와 OR목록은 혼용이 가능하다.

[ 조건 ] && 문장1 || 문장2

위의 예는 조건이 참이면 문장1을 수행하고 거짓이면 문장2를 수행한다.

또한 위의 문장1이나 문장2에서 여러개의 문장을 수행하고 싶을 때는 {}를 사용하면 된다.
[조건] && {
    문장1
    문장2
    문장3
} || {
    문장4
    문장5
    문장6
}

4 제어문

4.1 for

for 문은 지정된 범위안에서 루프를 수행한다. 범위는 어떤 집합도 가능하다.

형식:
for 변수 in 값1, 값2, ...
do
    문장
done

매 루프를 돌때마다 변수의 값은 in 이후의 값으로 대체된다.

예제:
for str in "test1", "test2", "test3", "test4"
do
    echo @str
done

출력:
test1
test2
test3
test4

값에는 와일드 카드 확장을 사용할 수 있다.
for file in $(ls -a | grep "^\.")
do
    echo "$file is Hidden File"
done

위 예의 출력 결과는 현재 디렉토리에서 처음이 "." 으로시작하는 파일(히든파일)만을 출력한다.

for file in $(ls chap[345].txt); do
    echo "--- $file ---" >> Books.txt
    cat $file >> Books.txt
done

위의 예는 chap3.txt, chap4.txt, chap5.txt 파일을 Books.txt 라는 파일에 붙여 넣는다.

다음의 예를 보고 결과를 예측해보자.
echo "\$* output"

for fvar in $*
do
    echo $fvar
done

echo "\$@ output"
for fvar in $@
do
    echo $fvar
done

4.2 while

for 명령의 경우는 횟수를 지정해서 루프를 수행하는 데는 문제가 있다. while 문은 실행 횟수가 지정되지 않았을 때 편리하다.

형식:
while 조건문
do
    문장
done

예제를 보자. 패스워드를 입력받고 맞는지 확인하는 프로그램이다.
echo "Enter Password : "
read password1

echo "Retype Password : "
read password2

while [ "$password1" != "$password2" ]
do
    echo "Password mismatch Try again "

    echo "Retype Password : "
    read password2
done

echo "OK Password Match complete"

어떻게 동작하는가 ?

4.3 until

until은 while문과 동일한 효과를 내지만 조건이 반대이다. 즉, while문은 조건이 참일동안 루프를 수행하지만 until은 조건이 거짓일 동안 루프를 수행한다.

형식:
until 조건문
do
    문장
done

다음 예를 보자. 이 예는 지정한 유저가 로그인하면 알려준다.
#!/bin/sh

until who | grep "$1" > /dev/null
do
    sleep 10
done

echo "User $1 just logged in ^_^"

4.4 select

select문은 원하는 리스트를 출력하고 그 중 선택된 것을 돌려주는 구문이다. 주의할 점은 select의 루프 내에서는 자동적으로 루프를 벗어날 수 없다. 반드시 break문을 사용해서 루프를 벗어나야 한다.

예: 간단한 퀴즈
#!/bin/sh

echo "다음중 스크립트언어 프로그래밍에 속하는 것은 ?"
select var in "쉘 프로그래밍" "C 프로그래밍" "자바 프로그래밍" "Exit"
do
    if [ "$var" = "쉘 프로그래밍" ]
    then
        echo "정답입니다."
        exit 0
    elif [ "$var" = "Exit" ]
    then
        echo "종료합니다."
        exit 1
    else
        echo "$var 을 선택하셨습니다. 오답입니다."
        echo "다음중 스크립트언어 프로그래밍에 속하는 것은 ?"
    fi
done

5 함수

쉘 스크립트 내부에 또는 다른 스크립트파일에 함수를 정의해 놓고 사용할 수 있다. 함수를 사용하면 코드를 최적화 할 수 있고, 코딩이 간결해지며,재사용이 가능하다. 그러나 다른 스크립트 파일을 호출해서 함수를 실행할 경우, 가능은 하지만 스크립트의 실행시간이 길어지고, 함수의 결과를 전달하는 것이 까다롭기 때문에 가급적이면 외부파일의 함수는 안쓰는 것이 좋다.

형식:
함수명 ()
{
	문장
	return 값
}

사용
함수명 인자1, 인자2, ...

함수는 독립적으로 $#, $*, $0 등의 인자 변수를 사용한다. 즉 함수내의 $#과 본체의 $#은 다를 수 있다는 것이다.

다음의 예를 보자
#!/bin/sh
		
func()
{
    echo ------ this is func --------
    echo "This Script Executable File : $0"
    echo "Argument Count : $#"
    echo "Process ID : $$"
    echo "Argument List \$* : $*"
    echo "Argument List \$@ : $@"
    echo "Argument 1 : $1"
    echo "Argument 2 : $2"
    echo "Argument 3 : $3"
}

echo ------ this is main --------
echo "This Script Executable File : $0"
echo "Argument Count : $#"
echo "Process ID : $$"
echo "Argument List \$* : $*"
echo "Argument List \$@ : $@"
echo "Argument 1 : $1"
echo "Argument 2 : $2"
echo "Argument 3 : $3"
echo "Argument 4 : $4"
func aa bb cc 

본체와 함수에서 동일한 변수를 보여주지만 값은 틀린다는 것을 알 수 있다.

함수에서 값을 반환하기 - 함수에서 반환값은 반드시 정수값만을 반환할 수 있다. 이 값을 if 등으로 조건을 판단해서 사용할 수 있다. 반환값 중 0은 참으로 나머지 숫자는 거짓으로 판별된다.

6 명령어

쉘에서 쓸 수 있는 명령어는 두가지로 나누어진다. 명령 프롬프트 상에서 실행 시킬 수 있는 외부 명령어와 쉘 내부 명령이다. 내부 명령은 보통 쉘 내부나 쉘 구문상에서 쓰인다. 외부명령은 쉘에 관계없이 사용이 가능하다.

6.1 break

제어문이나 조건문의 루프를 빠져나갈때 사용한다.

예제
while [ $a -eq 10 ]
do
    if [ $a -eq 5 ]; then
        break
    fi
done

6.2 continue

제어문이나 조건문의 처음으로 돌아가서 다시수행한다.

예제
while [ $a -eq 10 ]
do
    if [ $a -eq 5 ]; then
        continue
    fi
done

6.3 : 명령

의미없는 명령. 논리값 true를 대신해 쓰기도 한다.

6.4 . 명령

. 명령을 사용하면 현재 쉘에서 명령을 실행시킨다 그러므로 실행된 명령의 결과를 본 프로그램에서 사용할 수 있다.

예를 들면 A 라는 스크립트에서 B라는 스크립트를 그냥 실행할 경우 B에서의 변화(환경변수 등)는 A에게 아무런 영향도 미치지 않는다. 그러나 . 명령을 사용해서 실행하면 B에서의 변화가 A에도 영향을 미친다.

6.5 echo

문장을 출력한다. 자동으로 개행문자가 삽입된다. (다음 줄로 넘어간다)

6.6 eval

인자의 실제 값을 구하는데 사용한다.
foo=10
x=foo
y='$'$x
echo $y

이 예를 실행해 보면 $foo가 출력된다
foo=10
x=foo
eval y='$'$x
echo $y

이 예에서는 $foo의 값 즉 10 이 출력된다. eval명령은 원하는 문자열들을 조합해서 변수를 액세스 할 수 있다.

6.7 exec

현재 쉘을 다른 프로그램으로 대체한다.

예제
exec csh

6.8 exit n

현재 쉘을 종료한다. 종료시 n 값을 리턴한다.

6.9 export

해당 쉘에서 파생된 자식 프로세스에서 export한 환경변수는 본래 쉘에서 관리한다.

6.10 expr

표현식의 값을 구한다.
x=`expr 1 + 2`
요즘은 expr보다는 $((계산식)) 구문을 많이 사용한다.

6.11 printf

C 언어의 printf명령과 흡사하다.

형식:
printf "Format String" arg1 arg2 arg3 ...

6.12 return

쉘 함수에서 값을 반환 할 때 쓰인다. 0은 성공을 1~125까지는 쉘 에러코드를 나타낸다.

6.13 set

쉘 내부에서 매개 인자를 설정한다. set의 인자로 쓰인 문자열은 공백에 의해 $1 부터 차례대로 대입된다.

예제
#!/bin/sh
echo $#
set $(ls)
echo $# 

결과는
0
22

이다. (22는 필자의 ls 결과의 갯수이다.) 첫번째 0는 이 스크립트에 인수가 없으므로 0이고 set $(ls) 에 의해서 인수의 갯수가 22개로 늘었다.

6.14 shift

쉘의 인자를 한자리씩 아래로(n -> 1 로) 이동시킨다.

예제
#!/bin/sh

echo $1
shift
echo $1
shift 5
echo $1

실행
#./myscript 1 2 3 4 5 6 7 8 9 0
1
2
7

6.15 trap

쉘의 실행도중 시그널을 처리하는 시그널 처리기를 만드는 역할을 한다.

형식:
trap command signal

쉘 스크립트는 위에서 아래로 실행되므로 보호하려는 부분 이전에 trap 명령을 사용해야 한다. trap 조건을 기본으로 사용하려면 명령에 - 를 넣으면 된다. 신호를 무시하려면 '' 빈 문자열을 준다.

6.16 unset

변수나 함수를 제거한다.

7 명령 실행

외부 명령의 실행 결과를 변수에 집어넣어 변수의 값으로 사용할 수 있다.

형식:
x = $(명령)

이렇게 변수에 결과를 넣은 후에는 이 변수를 일반문자열로 생각하고 원하는 가공을 해서 결과를 얻어낼 수 있다. 위에서 보았던 매개변수 확장이나 set명령을 이용해서 원하는 부분을 추출해 내면 그만이다.

8 쉘 스크립트 내부에서 명령에 입력 전달하기 (Here Documents)

이 기능은 쉘 내부에서 명령어에 입력을 전달하는 방법이다. 전달된 입력은 마치 키보드에서 눌려진 것처럼 반응한다.

형식:
명령 << 종료문자열
입력값.....
종료문자열

예제: 자동으로 메일을 보내는 스크립트
#!/bin/sh

mail $1 << myscript
This is Header
This is Body
.

myscript

9 디버깅 하기

쉘 프로그래밍 시 간단하게 디버깅하는 방법을 소개합니다.

9.1 쉘 옵션

  • sh -n 스크립트: 문법 에러만을 검사, 명령을 실행하지 않음
  • sh -v 스크립트: 명령을 실행하기 전에 에코
  • sh -x 스크립트: 명령줄에서 처리한 다음 에코

9.2 set 옵션

위의 쉘 옵션은 아래와 같이 set 옵션으로도 설정할 수 있다.
  • set -o noexec 또는 set -n : 문법 에러만을 검사, 명령을 실행하지 않음
  • set -o verbose 또는 set -v : 명령을 실행하기 전에 에코
  • set -o xtrace 또는 set -x : 명령줄에서 처리한 다음 에코
  • set -o nounset 또는 set -u : 정의되지 않은 변수가 사용되면 에러 메시지를 제공한다.

아래와 같이 set -x를 이용하여 손쉽게 실행과정을 추적할 수 있다. (참고로 set 옵션을 취소하려면 set +x를 입력하면 된다. 다른 옵션도 마찬가지)
set -x
for str in "test1" "test2" "test3" "test4"
do
    echo $str
done

결과
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str

솔라리스에서 결과 파일등을 보고 하려는데... vi나 cat으로 긁어서 윈도우 메모장에 붙여넣으면 생노가다입니다 ㅠㅠ


그래서 간단한것이 메일을 쏘는 방법입니다..


#mailx -s "메일 제목" xxx@yyy.naver.com < ./"텍스트 파일"


이런 식으로 전송하면 파일 내용이 메일 본문으로 전송되므로 굳이 putty 긁어오기 신공이 필요없고 깔끔합니다.

Solaris  Basic  Set

  

  **  네트워크  설정  **

  

/etc/hosts

  127.0.0.1              localhost

  192.168.211.20      solaris10  loghost

  

/etc/hostname.pcn0

  solaris10

  

/etc/netmasks

  192.168.211.0          255.255.255.0

  

/etc/defaultrouter

  192.168.211.2

  

        -  부팅시  GW자동인식이  안돼면
            /etc/init.d/inetsvc

            route  add  default  route_IP  1    
    

/etc/resolv.conf
nameserver  219.250.36.130

  

/etc/nsswitch.conf
hosts:        files  dns

  

**  shell  바꾸기**                  

작업의  편의를  위해  root  의  쉘과  홈디렉토리부터  변경해준다.
솔라리스에서는  기본적으로  root  의  홈디렉토리를  /  로  사용하지만  관리의  편의를  위해  /root  라는  디렉토리를  만들어서  root  의  홈디렉토리로  사용한다.

  #  mkdir  /root
  
root의  기본  쉘을  bash로  변경해주고,  홈디렉토리를  /root  로  변경해준다.

  기본  :  root:x:0:1:Super-User:/:/sbin/sh

  변경  :  root:x:0:1:Super-User:/root:/bin/bash

/etc/bashrc와  root의  홈디렉토리에  .bash_profile  을  만들어준다.
  리눅스의  파일을  참조하여  사용한다.

  +++  /etc/bashrc  ++++++++++++++++++++++++++++++++++++++++

#  /etc/bashrc

#  System  wide  functions  and  aliases
#  Environment  stuff  goes  in  /etc/profile

#  by  default,  we  want  this  to  get  set.
#  Even  for  non-interactive,  non-login  shells.
if  [  $UID  -gt  99  ]  &&  [  "`id  -gn`"  =  "`id  -un`"  ];  then
        umask  002
else
        umask  022
fi

#  are  we  an  interactive  shell?
if  [  "$PS1"  ];  then
        case  $TERM  in
        xterm*)
                if  [  -e  /etc/sysconfig/bash-prompt-xterm  ];  then
                        PROMPT_COMMAND=/etc/sysconfig/bash-prompt-xterm
                else
                        PROMPT_COMMAND='echo  -ne  "\033]0;${USER}@${HOSTNAME%%.*}:${PWD/#$HOME/~}\007"'
                fi
                ;;
        screen)
                if  [  -e  /etc/sysconfig/bash-prompt-screen  ];  then
                        PROMPT_COMMAND=/etc/sysconfig/bash-prompt-screen
                else
                PROMPT_COMMAND='echo  -ne  "\033_${USER}@${HOSTNAME%%.*}:${PWD/#$HOME/~}\033\\"'
                fi
                ;;
        *)
                [  -e  /etc/sysconfig/bash-prompt-default  ]  &&  PROMPT_COMMAND=/etc/sysconfig/bash-prompt-default
                ;;
        esac
        #  Turn  on  checkwinsize
        shopt  -s  checkwinsize
        [  "$PS1"  =  "\\s-\\v\\\$  "  ]  &&  PS1="[\u@\h  \W]\\$  "
fi

if  !  shopt  -q  login_shell  ;  then  #  We're  not  a  login  shell
                for  i  in  /etc/profile.d/*.sh;  do
                if  [  -r  "$i"  ];  then
                        .  $i
                fi
        done
        unset  i
fi
#  vim:ts=4:sw=4

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

  +++  /etc/bashrc  ++++++++++++++++++++++++++++++++++++++++

#  .bash_profile

#  Get  the  aliases  and  functions
if  [  -f  /etc/bashrc  ];  then
                .  /etc/bashrc
fi

#  User  specific  environment  and  startup  programs

PATH=$PATH:$HOME/bin

export  PATH
unset  USERNAME

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

-  다른  사용자의  shell  을  변경하고자  할때도  위와  같은  방법을  참고하여  변경해  준다.


**  솔라리스에서  한글  사용하기**

set  |  grep  LANG  으로  설정  확인
LANG=ko

변경은    /etc/profile  에


LANG=ko
export  LANG

  를  추가해  준다.


**  솔라리스에서  /home  사용하기**

  솔라리스에서는  /home을  automounter가  mount해서  사용하기  때문에  root라  할지라도  디렉토리나  파일을  만들  수  없다.  하지만  리눅스에  익숙한  관리자라면  /home  에서  모든  사용자  홈디렉토리를  관리하기를  원할  것이다.  /home  을  automount  하지  않으려면  /etc/auto_master파일에서  /home부분을  주석처리  하고  시스템을  재부팅하면  된다.

/etc/auto_master

......
+auto_master
/net                        -hosts                    -nosuid,nobrowse
#/home                      auto_home              -nobrowse
/xfn                        -xfn

reboot


**  시스템  시간  맞추기**

직접  맞추기
  
  rdate  명령어를  사용하여  필요할때마다  수정해도  좋지만,  이보다는  시스템  재부팅시에  자동으로  세팅되도록  부팅  스크립트에  포함시켜  준다.  time  서버로는  time.bora.net  또는  time.kriss.re.kr  를  사용한다.
  일단  현재  시간을  맞추기  위하여  프롬프트  상에서  한번  실행해  준다.  이때  리눅스에서는  -s  옵션을  주어  시스템  시간을  동기화  시키지만  솔라리스에서는  -s  옵션을  주지  않는  것을  주의한다.

rdate  time.bora.net

  다음  시스템  부팅시마다  자동으로  맞추기  위하여  부팅  스크립트에  포함시켜  준다.

  /etc/rc3
  …
rdate  time.bora.net

자동으로  맞추기

XNTP  라는  프로그램을  사용하면  시스템에  항상  XNTP  프로그램이  데몬으로  상주하며  자동으로  시스템  시간을  정확히  맞혀  준다  설정방법은

pkginfo  |  grep  ntp

NTP  패키지가  설치되어  있는지  확인하고,  설치되어  있으면

/etc/inet                          /  디렉토리로  이동하여,  ntp.client  파일을  ntp.conf라는  이름으로  복사한다.

ntp.conf  를  열어  맨  밑에

server  time.bora.net
server  time.nuri.net
server  gps.bora.net
server  ntp1.cs.pusan.ac.kr
server  ntp.ewha.net
server  ntp1.gngidc.net
server  ntp2.gngidc.net
server  time.kriss.re.kr

를  추가해  준다.

/etc/init.d/xntpd  start

다음  시스템  재부팅부터는  자동  실행된다

ntpq  -p

로  ntp  서버에  정상  접속하는지  확인한다.


**  DNS  설정  **

솔라리스에서  DNS  설정법은  리눅스와  약간의  차이가  있다.  먼저  리눅스처럼  resolv.conf  파일에  사용할  DNS  를  추가한다.  추가  방법은  동일하지만,  리눅스처럼  resolv.conf  파일이  생성되어  있는  것이  아니고,  시스템  관리자가  직접  생성해  줘야  한다.  위치는  리눅스와  같은  /etc/resolv.conf  가  된다.

resolv.conf

nameserver  211.115.194.2
nameserver  211.115.194.3
nameserver  203.255.112.34
search  localdomain

다음으로  nsswitch.conf  라는  파일을  열어서  hosts:  ….  줄의  맨  끝에  DNS를  추가해  준다.

nsswitch.conf

  …
hosts:            files  dns


**  패키지  다운로드  **

http://www.sunfreeware.com

  패키지  설치  확인은  아래의  명령어를  사용하여  확인한다.

pkginfo  |  grep  {Package  Name}

  패키지  설치법

  보통  솔라리스  패키지는  패키지명-패키지버전-OS버젼-cpu타입-local.gz  으로  제공된다.
  우선  gzip  을  사용하여  gz  압축을  해제후에  pkgadd  를  사용하여  설치한다.

ex)  gzip  -d  autoconf-2.59-sol9-intel-local.gz
    pkgadd  -d  autoconf-2.59-sol9-intel-local


**  컴파일  환경  만들기  **

wget  패키지를  미리  설치하여  다운로드에  사용한다
  
    ftp://ftp.sunfreeware.com/pub/freeware/intel/10/wget-1.10.2-sol10-x86-local.gz  
  
autoconf  패키지  설치

  wget  ftp://ftp.sunfreeware.com/pub/freeware/intel/10/autoconf-2.59-sol10-intel-local.gz

make  패키지  설치

  wget  ftp://ftp.sunfreeware.com/pub/freeware/intel/10/make-3.80-sol10-intel-local.gz


automake  패키지  설치

  wget  ftp://ftp.sunfreeware.com/pub/freeware/intel/10/automake-1.9-sol10-intel-local.gz

gcc  패키지  설치

  wget  ftp://ftp.sunfreeware.com/pub/freeware/intel/10/gcc-3.3.2-sol10-intel-local.gz

  

패키지는  일반적으로  /usr/local  ~  에  설치  되므로  /etc/profile  에  PATH와  LD_LIBRARY_PATH를  수정  또는  추가해  준다.

  /etc/profile

...

PATH=/usr/local/bin:$PATH
LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:/usr/openwin/lib:/usr/openwin/bin:/usr/dt/lib:/usr/ucblib:/opt/lib:/usr/sfw/lib/:.
export  PATH  LD_LIBRARY_PATH

주의사항:
LD_LIBRARY_PATH에서  디렉토리  경로의  순서를  바꾸시면  안됩니다!  역시  PATH에서  /usr/local/bin이  앞에  나와야  합니다.


libgcc  패키지  설치

  wget  ftp://ftp.sunfreeware.com/pub/freeware/intel/10/libgcc-3.3-sol10-intel-local.gz

libiconv  패키지  설치

  wget  ftp://ftp.sunfreeware.com/pub/freeware/intel/10/libiconv-1.9.2-sol10-x86-local.gz


**  개발  환경  만들기**

  --  ls  하이라이팅  세팅

  솔라리스에  기본적으로  제공되는  ls  는  하이라이팅  기능을  지원하지  않으므로  fileutils에  포함되어  있는  ls를  사용하기  위하여  fileutils를  설치한다.
  현재  intel  기반  솔라리스용  fileutils  패키지를  제공하지  않으므로  앞서  세팅한  컴파일  환경을  사용하여  설치한다.

wget  http://ftp.gnu.org/pub/gnu/fileutils/fileutils-4.1.tar.gz
  
gzip  -d  fileutils-4.1.tar.gz
tar  xvf  fileutils-4.1.tar.gz

cd  ./fileutils-4.1

./configure

make
  make  시에  컴파일  에러가  난다면
  ........
  error:  parse  error  before  "ctid_t"  
  ........
  
  -  PATH  에  /usr/ccs/bin  가  추가되어  있는지  확인
  -  cd  /usr/local/lib/gcc-lib/sparc-sun-solaris2.10/3.3.2/install-tools
      ./mkheaders

  

make  install

  

설치가  완료되었다면,  /usr/local/bin  에  ls  가  설치되었는지  확인한다.

/usr/local/bin/ls  --color=auto

하이라이팅이  정상적으로  나타난다면,  쉘  스크립트에  alias  를  주어  사용한다.

/etc/profile
...
alias  ls  =  ‘/usr/local/bin/ls  --color=auto’


  --  vi  환경  설정

솔라리스에  기본적으로  제공되는  vi  는  하이라이팅  기능을  지원하지  않으므로  vim  을  설치하여  하이라이팅과  탐색  옵션  기능,  탭  설정  기능  등을  사용한다.

vim  패키지를  다운로드하여  설치한다.
wget  ftp://ftp.sunfreeware.com/pub/freeware/intel/10/vim-6.3-sol10-intel-local.gz

vim  패키지를  사용하기  위해  ncurses  패키지를  다운로드하여  설치한다.
wget  ftp://ftp.sunfreeware.com/pub/freeware/intel/10/ncurses-5.4-sol10-intel-local.gz
  
정상적으로  설치되었는지  확인하여  본다.
vim

이때  라이브러리  에러가  난다면  /etc/profile의  LD_LIBRARY_PATH  에  /usr/sfw/lib/  가  포함되어  있는지  확인하여  본다.

정상적으로  실행되지만  vim  역시  하이라이팅  기능이  기본  설정이  아니다.  터미널을  ansi  표준으로  세팅하고,  vi  를  vim  으로  aliasing  하고,  vim  설정  파일을  각자의  홈  디렉토리에  생성하여  하이라이팅  on  한다.

/etc/profile
...
alias  vi='vim'
TERM=ansi
export  TERM

을  포함하여  준다.

  

기타  추가  aliasing

/etc/profile

....

alias  cp='cp  -i'
alias  l.='ls  -d  .[a-zA-Z]*  --color=tty'
alias  ll='ls  -l  --color=tty'
alias  ls='ls  --color=tty'
alias  mv='mv  -i'
alias  rm='rm  -i'
alias  vi='vim'
alias  which='alias  |  /usr/bin/which  --tty-only  --read-alias  --show-dot  --show-tilde'

  

그리고  각자의  홈  디렉토리에  .

/root/.vimrc

set  incsearch
set  hlsearch
set  laststatus=2
set  ts=4
set  sw=4
syn  on

[출처]  Solaris  설치  후  기본적인  설정|작성자  바람추적
[2008년  04월  01일  22:27:00  수정되었습니다.]
[본문링크] Solaris 설치 후 기본적인 설정

'리눅스 서버에 대해서 > 솔라리스(UNIX) 관련' 카테고리의 다른 글

솔라리스에서 파일 첨부  (0) 2013.01.09
UNIX 뮤텍스  (0) 2013.01.09
유닉스 Thread 시스템프로그래밍  (0) 2013.01.09
솔라리스 기본 설정 (텍스트)  (0) 2013.01.04
솔라리스 pkg 설치.  (0) 2013.01.04
솔라리스 11 설치  (0) 2012.12.08

+ Recent posts