'Linux/Programming'에 해당되는 글 16건

  1. 2007.09.19 cpu 사용율 구하기(2)
  2. 2007.09.18 ps만들기
  3. 2007.09.18 CPU사용률 구하는 공식
  4. 2007.09.18 시리얼통신에 관하여
  5. 2007.09.18 terminal 통신
  6. 2007.09.11 SO파일 만들어볼까나(공유라이브러리)

cpu 사용율 구하기(2)

참조 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/QOS/SMS/Cpu_Usage
// Standard C++ Library
#include <iostream>

// Common Library
#include <cinterface.h>

// Standard C Library
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>

#define K4 0
#define K6 1

using namespace std;

int isTotal=0;
int findCpu=0;

int GetCpuNum();
int Type = 0;

struct _CpuStat
{
    unsigned int User;
    unsigned int System;
    unsigned int Idle;
};

struct _CpuStat CpuStat[2];

char *rtvstr;

int Init()
{
    GetCpuNum();
    rtvstr = (char *)malloc(80);
    return 1;
}

int cidx = 0;

// CPU 갯수를 얻어온다.
int GetCpuNum()
{
    FILE *fp;
    fp = fopen("/proc/stat", "r");
    char buf[80];
    isTotal=0;
    findCpu=0;
    if (fp == NULL)
    {
        return 0;
    }
    while(fgets(buf, 80, fp))
    {
        if (strncmp(buf, "cpu", 3) != 0)
        {
            continue;
        }
        if (isdigit(buf[3]))
        {
            findCpu++;
        }
        else
        {
            isTotal = 1;
        }
    }
    fclose(fp);
    return 1;
}

// Cpu 갯수를 리턴한다.
int NumRows()
{
    return findCpu;
}

// proc 파일시스템을 분석해서 Cpu 사용율 정보를 얻어온다.
// /proc/stat 의 사용율 총합라인만 읽어올 것이다.
char *Read()
{
    FILE *fp;
    char buf[80];
    fp = fopen("/proc/stat", "r");
    char cpuid[8];
    int nused;
    if (fp == NULL)
    {
        return NULL;
    }
    while(fgets(buf, 80, fp))
    {
        if (!strncmp(buf, "cpu", 3))
        {
            sscanf(buf, "%s %d %d %d %d",
                    cpuid,
                    &CpuStat[cidx%2].User,
                    &nused,
                    &CpuStat[cidx%2].System,
                    &CpuStat[cidx%2].Idle
                    );
            break;
        }
    }
    // 처음실행했다면, 1초를 쉰다음 재귀호출 한다.
    if (!cidx)
    {
        sleep(1);
        cidx++;
        *Read();
    }
    cidx++;
    int diff1;
    int diff2;
    int diff3;
    int Idle, System, User;
    diff1 = CpuStat[(cidx+1)%2].User - CpuStat[(cidx)%2].User;
    diff2 = CpuStat[(cidx+1)%2].System - CpuStat[(cidx)%2].System;
    diff3 = CpuStat[(cidx+1)%2].Idle - CpuStat[(cidx)%2].Idle;

    Idle = (diff3*100)/(diff1+diff2+diff3);
    System = (diff2*100)/(diff1+diff2+diff3);
    User = (diff1*100)/(diff1+diff2+diff3);

    sprintf(rtvstr,"CPU=%d,%d,%d\n", User,System,Idle);
    return rtvstr;
}
int Clean()
{
    if (rtvstr != NULL)
        free(rtvstr);
    rtvstr = NULL;
    return 1;
}

ps만들기

참조 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/proc/MakePS

나만의 ps를 만들어보자

윤 상배

yundream@joinc.co.kr

교정 과정
교정 0.8 2003년 4월 7일 23시
최초 문서작성


1절. 소개

대부분의 Unix운영체제는 proc파일시템을 제공한다. proc는 process infomation pseudo의 줄임말이다. 즉 proc파일시스템은 커널에서제공하는 프로세스정보를 저장하고 있는 파일시스템이라고 정리할수 있다.

이 문서는 proc파일시스템을 이용해서 실제 프로세스목록을 출력하는 프로그램제작과 관련된 내용을 담고 있다. 문서는 리눅스운영체제(kernel 2.4.x)를 기준으로 작성되었다.


2절. proc 파일시스템

2.1절. proc 파일시스템에 대하여

리눅스 커널의 주된임무중 하나는 프로세스를 실행시키고 이들을 유지하는 일이며, 이를 위해서 커널데이타구조체를 내부적으로 사용하게 된다. 그런데 이런 프로세스정보는 커널에게만 필요한게 아니고 시스템 관리자혹은 프로그래머들에게도 절대적으로 필요한 정보이다.

그렇다면 필요한 정보를 얻어내기 위해서 어떻게 해야할까? 직접 커널에게 커널데이타구조체를 요청해서 그걸 일일이 분석해야 할까 ? 물론 그렇게도 할수 있겠지만, 이것은 매우 복잡한 작업이며, 또한 (커널모드에서 직접이루어지는 작업임으로)위험한작업이기도 하다. 그래서 리눅스시스템은 사용자레벨에서 프로세스의 상태를 간단하게 확인가능하도록 하기위해서 proc파일시스템을 제공한다.

우리는 복잡하게 커널로부터 여러가지 커널데이타구조체를 요청할필요 없이 proc파일시스템에서 제공하는 정보들을 읽어들이는 정도로 간단하게 프로세스 상태를 얻어올수 있다.

작은 정보: proc파일시스템은 대부분의 유닉스운영체제에서 채택되어지고 있지만, 데이타를 저장하는 범위와 포맷에 있어서 운영체제간 차이점을 보인다. 저장하는 범위의 경우 대부분의 유닉스운영체제는 단지 프로세스 정보만을 제공하는 반면, 리눅스는 프로세스 정보뿐만 아니라 네트웍정보, 파일시스템, 디바이스 정보, 메모리정보, CPU정보등 다양한 정보들을 제공한다.

특히 리눅스의 경우 몇몇정보들에 대해서는 단지 열람만 가능한 수준이 아닌 직접수정을 통해서 커널의 행동을 변경시켜줄수도 있다. 이것은 다른 유닉스에 비해서 매우 확장된 부분이라고 할수 있다.

예를 들어 ICMP요청에 대한 응답을 막고 싶다면 "echo 0 /proc/sys/net/ipv4/icmp_echo_ignore_all"하는 정도로 간단하게 커널의 행동을 변경시켜줄수 있다. 다른운영체제에서의 이러한 작업은 전용관리도구를 사용하든지 리붓팅을 시키든지 해야한다.

저장되는 포맷을 보자면 리눅스는 일반 ASCII문자로 이루어진 반면 다른 유닉스들은 구조체로 정보가 이루어져 있다. 리눅스의 경우 프로세스 정보가 일반문자로 이루어져 있어서 직관적으로 확인하기에 좋기는 하지만 프로그래밍을 할경우 이를 파싱해야되기 때문에 다른 유닉스들에 비해서 좀 불편한점이 되기도 한다.


2.2절. 프로세스 정보가져오기

리눅스의 경우 기능이 확장되긴 했지만 proc파일시스템의 가장큰 사용목적은 뭐니뭐니 해도 프로세스정보를 얻어오는 일이다.

기본적으로 proc파일시스템은 /proc디렉토리안에서 이루어지며, 프로세스정보는 /proc디렉토리밑에 각 프로세스의 PID를 이름으로하는 서브디렉토리 밑에 위치하게 된다. 예를들어 PID가 912인 프로세스라면, 912 프로세스의 정보는 /proc/912(이하 /proc/[PID])밑에 위치하게 된다.

/proc/[PID] 디렉토리밑에는 다시 몇개의 디렉토리와 몇개의 파일들로 이루어져 있다.

 /proc/[PID]/ -+-- cmdline
               |
               +-- cwd
               |
               +-- environ
               |
               +-- exe 
               |
               +-- fd -------+-- 0
               |             |
               +-- maps      +-- 1
               |
               +-- root 
               |
               +-- stat 
               |
               +-- statm 
               |
               +-- status
			
리눅스의 경우 위와 같은 파일들로 이루어져 있다. 각 파일이 가지고 있는 자세한 정보들에 대해서는 proc의 man페이지를 참고하기 바란다.

리눅스의 경우 각각의 정보들은 일반 ASCII텍스트문자로 이루어져있고, 대부분의 경우 공백문자(' ')로 필드의 구분이 되어있음으로, 쉽게 원하는 정보들을 얻어올수 있다.


3절. 나만의 ps제작

프로세스정보를 확인하기 위해서 리눅스는 ps라는 도구를 제공한다. ps를 사용함으로써, 우리는 프로세스의 각종중요한 정보들을 얻어오고, 얻어온 정보는 시스템관리와 프로그래밍을 위한 중요한 데이타로 사용한다.

그림 1. ps를 이용한 프로세스상태 확인

우리가 얻고자하는 프로세스데이타는 다음과 같다.

  • 프로세스의 실행유저

  • 프로세스 아이디(PID)

  • 부모프로세스 아이디(PPID)

  • 부모프로세스 상태(Zombie, Run, Sleep 등)

  • 프로세스 이름

  • CPU 사용율(%)

  • VMEM(가상메모리) 사용율

ps에 비해서 몇가지 빠진것들이 있긴하지만 프로세스를 관리하는데 필요한 최소한의 정보는 가져온다.

이 ps도 기본적으로 proc파일시스템에있는 프로세스정보를 이용해서 가져온다. 더 정확히 말하자면 stats에서 필요한 정보를 가져온다. 다음은 실제 stats의 파일내용이다. 원래는 하나의 행으로 되어있으나 출력하기 쉽게 여러개의 행으로 분리했다.

[root@localhost 2489]# cat stat
2489 (vi) T 2251 2489 2251 772 2581 0 187 0 455 0 12 4 0 0 9 0 0 0 181334 
6950912 0 4294967295 134512640 136413760 3221223720 3221222316 
1074893473 0 0 12288 1333808895 3222310480 0 0 17 0
		
우리가 만들고자하는 프로그램은 위의 stat 정보를 분석하게 될것이다.


3.1절. 예제코드

프로그램의 이름은 qps로 하도록 하겠다.

이프로그램의 쏘쓰는 몇개의 모듈로 이루어져 있으며, 쏘쓰관리를 위해서 Makefile을 사용할것이다. 다음은 만들고자 하는 qps의 쏘쓰트리 구조이다.

-+-- Makefile
 |
 +-- main.cc
 |
 +-- proc.cc
 |
 +-- qps.cc
 |
 +-- include ----+-- proc.h 
                 |
                 +-- qps.h
			
다음은 각 파일들에 대한 설명이다.

표 1. qps 쏘쓰파일 설명

Makefile make에서 사용할 make rule 파일
main.cc main함수를 포함하는 코드, 최소한의 코드만을 가진다
proc.cc 실제 proc파일시스템을 참조해서 각종 프로세스정보를 얻어온다.
qps.cc proc.cc에 정의된 함수를 호출하여 프로세스정보를 얻어오고 이를 화면에 보기좋게 출력한다.
include/proc.h proc.cc에서 사용될 함수선언
include/qps.h qps.cc에서 사용될 함수선언

3.1.1절. Makefile

쏘쓰코드들을 관리하기 위한 Makefile이다. 이해하는데 별다른 어려움은 없을것이다.

	
#############################################################################
# Makefile for building qps
# Generated by tmake at 23:10, 2003/04/08
#     Project: qps
#    Template: app
#############################################################################

####### Compiler, tools and options

CC  =   gcc
CXX =   g++
CFLAGS  =   -pipe -Wall -W -O2 -DNO_DEBUG
CXXFLAGS=   -pipe -Wall -W -O2 -DNO_DEBUG
INCPATH =   -I./include
LINK    =   g++
LFLAGS  =

TAR =   tar -cf
GZIP    =   gzip -9f

####### Files

HEADERS =   include/proc.h \
        include/qps.h
SOURCES =   main.cc \
        proc.cc \
        qps.cc
OBJECTS =   main.o \
        proc.o \
        qps.o

TARGET  =   qps
INTERFACE_DECL_PATH = .

####### Implicit rules

.SUFFIXES: .cpp .cxx .cc .C .c

.cpp.o:
    $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<

.cxx.o:
    $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<

.cc.o:
    $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<

.C.o:
    $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<

.c.o:
    $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<

####### Build rules
all: $(TARGET)

$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC)
    $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJMOC) $(LIBS)

dist:
    $(TAR) qps.tar qps.pro $(SOURCES) $(HEADERS) $(INTERFACES) $(DIST)
    $(GZIP) qps.tar

clean:
    -rm -f $(OBJECTS) $(OBJMOC) $(SRCMOC) $(UICIMPLS) $(UICDECLS) $(TARGET)
    -rm -f *~ core

####### Compile

main.o: main.cc

proc.o: proc.cc

qps.o: qps.cc
				


3.1.2절. proc.h, proc.cc

실질적으로 stat를 분석해서 프로세스데이타를 얻어오는 함수들을 포함한다. opendir(2)함수를 이용해서 /proc 디렉토리밑에 있는 파일들의 목록을 얻어오고, 만약 얻어온 파일이 디렉토리이면서 숫자로되어있을경우 프로세스정보 디렉토리라고 판단하고, 서브디렉토리에 있는 stat 파일을 읽어들인다.

읽어들인 stat정보는 " "를 기준으로 파싱해서 배열(vector)에 집어넣는다. 더불어 우리가 만들고자하는 qps프로그램은 해당프로세스의 유저이름도 가져와야 한다. /proc/[PID]/stat 파일은 프로세스소유자의 권한으로 만들어진다. 우리는 stat(2)계열함수를 사용하면 해당 파일의 UID를 얻어올수 있다는걸 알수있다. 또한 getpwuid(3)를 이용하면 해당 UID에 대한 유저이름도 얻어올수 있다.

이렇게 해서 하나의 프로세스에 대한정보가 만들어졌다. 그런데 우리는 프로세스의 목록을 가져와야 함으로 이들 정보는 다시 배열의 원소로 들어가야 할것이다. 이러한 자료구조(배열의 배열)를 위해서 필자는 (속편하게)vector를 사용했다.

다음은 실제 코드들이다. 위의 내용들은 코드를 통해서 이해하기 바란다. 그리어려운 코드들은 아님으로 주석만으로도 충분히 이해가능할것이다.

예제 : include/proc.h

#ifndef _PROC_H_
#define _PROC_H_

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <vector>
#include <dirent.h>
#include <string>

using namespace std;

// 프로세스 데이타 저장 
typedef struct _process_info
{
    char            username[32];  // 유저이름
    vector<string>  process;       // stat정보들이 들어간다   	
} process_info;

// 프로세스의 리스트를 유지하기 위한 자료구조
// 결국은 배열의 배열이다. 
typedef vector<process_info> Plist;

class Proc
{
    private:
        int         Processnum;     // 현재 프로세스 갯수
        Plist       ProcList;       // 프로세스정보 리스트

    public:
      
        // 생성자및 소멸자 
        Proc();
        ~Proc();

        void            MakeProcInfo();      // 프로세스 정보를 얻어온다.
        vector<string>  ProcParser(char *);  // stat파일을 파싱한다 
        int             ProcNum();           // 현재프로세스 갯수를 넘겨준다.
        int             IsDigit(char *);     // 문자열이 숫자인지 확인한다. 
        Plist           GetProcList();       // 프로세스정보 리스트를 되돌려준다.
};

#endif
				

예제 : proc.cc

#include "proc.h"
#include <iostream>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>

Proc::Proc()
{
}

Proc::~Proc()
{
}

/*
 * 프로세스 정보를 가져온다. 
 * 그러기 위해서 /proc/[PID]/stat파일을 읽어들이고 이를 
 * 필드별로 파싱한다. 파싱은 " "문자를 기준으로 한다.  
 * 또한 프로세스를 생성한 유저 이름도 얻어온다. 
 */
void Proc::MakeProcInfo()
{
    DIR *directory;
    struct dirent *entry = NULL;
    char proc_file[40];
    vector<string> procinfo; 
    process_info lprocess_info;
    struct stat lstat;

    ProcList.clear();

    // proc 디렉토리를 열어서 파일(디렉토리포함)의 리스트를 
    // 얻어온다.
    if ((directory = opendir("/proc")) == NULL)
    {
        perror("opendir error :");
        exit(0);
    }

    while((entry = readdir(directory)) != NULL)
    {
        if (strcmp(entry->d_name, ".") !=0 &&
            strcmp(entry->d_name, "..") != 0)
        {
            sprintf(proc_file,"/proc/%s/stat", entry->d_name);
            // stat 파일이 존재하는지 확인하고 확인하고
            if (access(proc_file, F_OK) != 0)
            {
                continue;
            }

            // 디렉토리 이름이 숫자인지 확인한다. 
            // 디렉토리 이름이 숫자라면 이를 파싱한다.  
            // 또한 stat파일의 소유 UID를 이용해서 유저이름도 얻어온다. 
            if (IsDigit(entry->d_name))
            {
                struct passwd *upasswd;
                stat(proc_file,&lstat);
                lprocess_info.process  = ProcParser(proc_file);

                upasswd = getpwuid(lstat.st_uid);
                strncpy(lprocess_info.username, upasswd->pw_name, 32);
                if(atoi(lprocess_info.process[0].c_str()) == atoi(entry->d_name))
                {
                    ProcList.push_back(lprocess_info);
                }
            }
            else
            {
            }
        }
    }

}

/*
 * MakeProcInfo를 통해만들어진 프로세스정보 리스트를 되돌려준다.
 */
Plist Proc::GetProcList()
{
    return ProcList;
}

/* 
 * 프로세스의 갯수를 되돌려준다.  
 * 갯수는 프로세스자료구조(vector)의 원소의 크기가 된다. 
 */
int Proc::ProcNum()
{
    return ProcList.size();
}

/*
 * stat 파일을 열어서 " "문자를 기준으로 파싱해서 
 * 배열(vector)에 집어넣는다.   
 */
vector<string> Proc::ProcParser(char *proc_file)
{
    FILE *fp;
    char buf[512] = {0x00,};
    char field[80];
    int index = 0;
    unsigned int i, j = 0;
    vector<string> vproc;

    fp = fopen(proc_file, "r");
    if (fp == NULL)
    {
        perror("error : ");
        exit(0);
    }

    memset(field, 0x00, 80);
    fgets(buf, 511, fp);
    for(i = 0; i < strlen(buf); i++)
    {
        if (buf[i] != ' ' && i != (strlen(buf) -1))
        {
            field[j++] = buf[i];
        }
        else
        {
            if (index == 1)
            {
                field[strlen(field)-1] = 0x00;
                vproc.push_back(field+1);
            }
            else
                vproc.push_back(field);
            memset(field, 0x00, 80);
            j = 0;
            index++;
        }
    }
    fclose(fp);
    return vproc;
}   

/*
 * 파일이름이 숫자인지 확인한다. 
 */ 
int Proc::IsDigit(char *str)
{
    int i;
    for (i = 0; i < strlen(str); i++)
    {
        if (isdigit(str[i])==0)
            return 0;
    }
    return 1;
}
				


3.1.3절. qps.h, qps.cc

위에서 설명한 proc.cc를 통해서 stat를 분석한 프로세스데이타가 만들어졌음으로 이제 이것을 가지고 와서 화면에 적당히 뿌려줘야 할것이다. 다음은 이와 관련된 함수들이다. 프로세세의 CPU사용율을 가져오기 위한 getcputime()함수외에는 별특별한건 없을것이다.

include/qps.h

#ifndef _QPS_H_
#define _QPS_H_
#include "proc.h"

// 프로세스 목록을 적당히 가공해서 출력한다.
int viewProc();

// 각프로세스의 CPU 사용율을 얻어온다. 
int getcputime(ulong utime, ulong stime, ulong starttime, int seconds);

// OS의 uptime(부팅후 지금까지의 가동시간)을 얻어온다.  
int uptime();
#endif
				

qps.cc

// Local 헤더파일
#include "qps.h"

// 표준 C++ 헤더파일들
#include <iostream>
#include <vector>
#include <string>

// 표준 C 헤더파일
#include <stdio.h>

using namespace std;

/*
 * 프로세스정보 리스트를 얻어와서 보기좋게 출력한다.  
 */
int viewProc()
{
    Proc *mproc;
    unsigned i;
    int pcpu;
    int seconds = 0;

    Plist ProcList;
    mproc = new Proc;
    mproc->MakeProcInfo();

    // 프로세스정보 리스트를 얻어온다. 
    ProcList = mproc->GetProcList(); 

    int total_cpu = 0;  

    // OS의 uptime을 얻어온다. 
    // 얻어온 정보는 프로세스의 CPU 사용율을 구하기 위한 기초정보로 
    // 사용된다.  
    seconds = uptime();
    printf("%-10s %7s %7s %2s %16s %4s %9s\n", "USER", "PID", "PPID",
                                              "ST", "NAME", "CPU", "VMEM"); 
    printf("==============================================================\n");
    for (i = 0; i < mproc->ProcNum(); i++)
    {
        // CPU사용율을 얻어온다. 
        pcpu = getcputime(atol(ProcList[i].process[13].c_str()),
            atol(ProcList[i].process[14].c_str()),
            atol(ProcList[i].process[21].c_str()), seconds);

        // 보기좋게 출력한다. 
        printf("%-10s %7s %7s %2s %16s %2d.%d %9s\n", ProcList[i].username, 
                                    ProcList[i].process[0].c_str(),
                                    ProcList[i].process[3].c_str(),
                                    ProcList[i].process[2].c_str(), 
                                    ProcList[i].process[1].c_str(), pcpu/10, pcpu%10,
                                    ProcList[i].process[22].c_str());
    }   
    return 1;
}

/*
 * 프로세스의 CPU사용율을 얻기 위해서 사용한다. 
 * utime     : 유저모드에서 프로세스가 스케쥴링되면서 사용한 jiffies 수이다. 
 *             프로세스가 스케쥴링될때마다 증가되는 수치이다.  
 * stime     : 커널모드에서 프로세스가 스케쥴링되면서 사용한 jiffies 수이다. 
 * starttime : 운영체제가 시작되고나서 몇 jiffies가 지난후 
 *             프로세스가 시작되었는지 
 */
int getcputime(ulong utime, ulong stime, ulong starttime, int seconds)
{   
    unsigned long long total_time;
    int pcpu=0;

    // 유저 jiffies 와 커널 jiffies를 더한다.
    total_time = utime + stime;

    // 100은 HZ값이다. HZ이 작아질수록 context switching가 빨라진다.  
    // 이값은 /usr/include/asm/param.h에 선언되어 있다. 
    // 100. 대신에 (unsigned long long)HZ 정도로 코드를 
    // 작성하는게 좀더 안전할것이다. 여기에서는 직관적으로 설명하기 
    // 위해서 하드코딩했다.  
    seconds = seconds - (int)(starttime/100.);

    if(seconds)
    {
        pcpu = (int)(total_time * 1000ULL/100.)/seconds;
    }

    return pcpu;
}

/*
 * 운영체제가 부팅하고 나서 얼마의 시간이 지났는지
 */
int uptime()
{
    FILE *fp;
    char buf[36];
    double stime;
    double idletime;

    if ((fp = fopen("/proc/uptime", "r")) == NULL)
    {
        perror("fopen error : ");
        exit(0);
    }
    fgets(buf, 36, fp);
    sscanf(buf, "%lf %lf", &stime, &idletime);
    fclose(fp);

    return (int)stime;
}
				


3.1.4절. main.cc

main함수다. 더이상 설명할 필요도 없는 간단한 코드이다.

예제 main.cc

#include <iostream>
#include "qps.h"

int main(int argc, char **argv)
{
        viewProc();
}
				


3.2절. 테스트

컴파일은 make를 이용하면 된다. 다음은 우리가 만든 프로그램을 실행시킨 화면이다.

그림 2. qps 테스트화면

그럭저럭 잘돌아가는걸 확인할수 있을것이다.

4절. 결론

이상 proc파일시스템을 이용해서 어떻게 프로세스정보를 얻어오는지에 대해서 알아고, 이 얻어온정보를 가공해서 실제 관리자나, 프로그래머에게 유용한 정보로 만드는 방법을 알아보았다.

시간이 남는다면 몇가지 다른 부가적인 시스템정보까지 포함시켜서 Top와 같은 좀더 강력한 프로그램을 만드는것도 재미있을것이다. 혹은 QT, GTK등을 이용해서 GUI환경에서 작동하는 시스템프로세스 모니터링 프로그램을 만들수도 있을것이다.

CPU사용률 구하는 공식

CPU사용률은 /proc/stat를 참고 Linux

2006/04/17 11:55

참조 : http://blog.naver.com/darkeye75/80023571727

CPU사용률은 /proc/stat를 참고하면 된다. [root@coco /root]# cat /proc/stat

cpu번호      user모드      nice user모드            system모드           idle 상태

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

cpu           1714278               9666                      631901             135528477
cpu0           842765               5302                      372331               67721763
cpu1           871513               4364                      259570               67806714



CPU사용률로써 부팅후 지금까지 소모된 jiffies의 크기를 나타낸다. 5개의 필드로 구성되어 있으며, 첫번째 필드는 CPU번호, 두번째 필드는 user 모드, 세번째 필드는 low priority(nice상태)의 user모드를 나타낸다. 네번째 필드는 system 모드 마지막은 idle 테스크의 jiffies 소모 값을 나타낸다. 우리가 필요로 하는 값은 두번째, 네번째, 다섯번째 필드의 값으로 이들 값을 이용해서 CPU의 사용율을 얻어올 수 있다.
cpu라고 적힌 부분의 값은 전체 cpu의 상태값을 나타내며, cpu0는 첫번째 cpu 상태값, cpu1는 두번째 cpu 상태값을 나타냅니다. 현재 제가 보여드린 시스템은 펜3 cpu 2개짜리 서버입니다.

만약 CPU의 IDLE상태만을 알고 싶다면 다섯번째 필드값만을 조사하면 될것이다.

이렇게 해서 CPU의 jiffies를 가지고 오긴 했지만 이것만으로는 우리가 원하는 정보를 얻어올순 없다. 실제 우리가 원하는 정보는 전체 CPU사용 jiffies중 idle jiffies를 얻어와야 하고 이것을 다시 백분율로 표시해야 하므로 약간의 가공을 해주어야 한다.

방법은 간단하다. 일정시간 소비된 idel jiffies를 총 소비된 jiffies로 나눠 주면 된다. (idle jiffies)*100 / (idle jiffies + use jiffies + system jiffies + low prio jiffies)
위의 방법을 통하면 cpu의 idle의 백분율 값을 얻어올 수 있다. 일정 시간은 초단위로 하면 되고, 소비된 jiffies를 구해야 함으로 이전의 jiffies값을 가지고 있어야 된다.


cpu 전체값 = (user모드+nice user 모드+system 모드+idle 상태)
user 모드 사용율 = (user 모드)*100 / cpu 전체값 
user 모드 사용율 = (user모드 / cpu 전체값) * 100
system 모드 사용율 = (system 모드 / cpu 전체값) * 100

좋은 하루 되십시오.

시리얼통신에 관하여

참조 : http://www.easysw.com/~mike/serial/serial.html#2_5_1

Serial Programming Guide
for
POSIX Operating Systems

5th Edition, 6th Revision
Copyright 1994-2005 by Michael R. Sweet

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in Appendix C, GNU Free Documentation License.


Table of Contents



Introduction Chapter 1, Basics of Serial Communications Chapter 2, Configuring the Serial Port Chapter 3, MODEM Communications Chapter 4, Advanced Serial Programming Appendix A, Pinouts Appendix B, ASCII Control Codes Appendix C, GNU Free Documentation License Appendix D, Change History

Introduction

The Serial Programming Guide for POSIX Operating Systems will teach you how to successfully, efficiently, and portably program the serial ports on your UNIX® workstation or PC. Each chapter provides programming examples that use the POSIX (Portable Standard for UNIX) terminal control functions and should work with very few modifications under IRIX®, HP-UX, SunOS®, Solaris®, Digital UNIX®, Linux®, and most other UNIX operating systems. The biggest difference between operating systems that you will find is the filenames used for serial port device and lock files.

License

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in Appendix C, GNU Free Documentation License.

Organization

This guide is organized into the following chapters and appendices:


Chapter 1, Basics of Serial Communications

This chapter introduces serial communications, RS-232 and other standards that are used on most computers as well as how to access a serial port from a C program.

What Are Serial Communications?

Computers transfer information (data) one or more bits at a time. Serial refers to the transfer of data one bit at a time. Serial communications include most network devices, keyboards, mice, MODEMs, and terminals.

When doing serial communications each word (i.e. byte or character) of data you send or receive is sent one bit at a time. Each bit is either on or off. The terms you'll hear sometimes are mark for the on state and space for the off state.

The speed of the serial data is most often expressed as bits-per-second ("bps") or baudot rate ("baud"). This just represents the number of ones and zeroes that can be sent in one second. Back at the dawn of the computer age, 300 baud was considered fast, but today computers can handle RS-232 speeds as high as 430,800 baud! When the baud rate exceeds 1,000, you'll usually see the rate shown in kilo baud, or kbps (e.g. 9.6k, 19.2k, etc). For rates above 1,000,000 that rate is shown in megabaud, or Mbps (e.g. 1.5Mbps).

When referring to serial devices or ports, they are either labeled as Data Communications Equipment ("DCE") or Data Terminal Equipment ("DTE"). The difference between these is simple - every signal pair, like transmit and receive, is swapped. When connecting two DTE or two DCE interfaces together, a serial null-MODEM cable or adapter is used that swaps the signal pairs.

What Is RS-232?

RS-232 is a standard electrical interface for serial communications defined by the Electronic Industries Association ("EIA"). RS-232 actually comes in 3 different flavors (A, B, and C) with each one defining a different voltage range for the on and off levels. The most commonly used variety is RS-232C, which defines a mark (on) bit as a voltage between -3V and -12V and a space (off) bit as a voltage between +3V and +12V. The RS-232C specification says these signals can go about 25 feet (8m) before they become unusable. You can usually send signals a bit farther than this as long as the baud is low enough.

Besides wires for incoming and outgoing data, there are others that provide timing, status, and handshaking:

Table 1 - RS-232 Pin Assignments
Pin Description Pin Description Pin Description Pin Description Pin Description
1 Earth Ground 6 DSR - Data Set Ready 11 Unassigned 16 Secondary RXD 21 Signal Quality Detect
2 TXD - Transmitted Data 7 GND - Logic Ground 12 Secondary DCD 17 Receiver Clock 22 Ring Detect
3 RXD - Received Data 8 DCD - Data Carrier Detect 13 Secondary CTS 18 Unassigned 23 Data Rate Select
4 RTS - Request To Send 9 Reserved 14 Secondary TXD 19 Secondary RTS 24 Transmit Clock
5 CTS - Clear To Send 10 Reserved 15 Transmit Clock 20 DTR - Data Terminal Ready 25 Unassigned

Two standards for serial interfaces you may also see are RS-422 and RS-574. RS-422 uses lower voltages and differential signals to allow cable lengths up to about 1000ft (300m). RS-574 defines the 9-pin PC serial connector and voltages.

Signal Definitions

The RS-232 standard defines some 18 different signals for serial communications. Of these, only six are generally available in the UNIX environment.

GND - Logic Ground

Technically the logic ground is not a signal, but without it none of the other signals will operate. Basically, the logic ground acts as a reference voltage so that the electronics know which voltages are positive or negative.

TXD - Transmitted Data

The TXD signal carries data transmitted from your workstation to the computer or device on the other end (like a MODEM). A mark voltage is interpreted as a value of 1, while a space voltage is interpreted as a value of 0.

RXD - Received Data

The RXD signal carries data transmitted from the computer or device on the other end to your workstation. Like TXD, mark and space voltages are interpreted as 1 and 0, respectively.

DCD - Data Carrier Detect

The DCD signal is received from the computer or device on the other end of your serial cable. A space voltage on this signal line indicates that the computer or device is currently connected or on line. DCD is not always used or available.

DTR - Data Terminal Ready

The DTR signal is generated by your workstation and tells the computer or device on the other end that you are ready (a space voltage) or not-ready (a mark voltage). DTR is usually enabled automatically whenever you open the serial interface on the workstation.

CTS - Clear To Send

The CTS signal is received from the other end of the serial cable. A space voltage indicates that it is alright to send more serial data from your workstation.

CTS is usually used to regulate the flow of serial data from your workstation to the other end.

RTS - Request To Send

The RTS signal is set to the space voltage by your workstation to indicate that more data is ready to be sent.

Like CTS, RTS helps to regulate the flow of data between your workstation and the computer or device on the other end of the serial cable. Most workstations leave this signal set to the space voltage all the time.

Asynchronous Communications

For the computer to understand the serial data coming into it, it needs some way to determine where one character ends and the next begins. This guide deals exclusively with asynchronous serial data.

In asynchronous mode the serial data line stays in the mark (1) state until a character is transmitted. A start bit preceeds each character and is followed immediately by each bit in the character, an optional parity bit, and one or more stop bits. The start bit is always a space (0) and tells the computer that new serial data is available. Data can be sent or received at any time, thus the name asynchronous.

Figure 1 - Asynchronous Data Transmission

The optional parity bit is a simple sum of the data bits indicating whether or not the data contains an even or odd number of 1 bits. With even parity, the parity bit is 0 if there is an even number of 1's in the character. With odd parity, the parity bit is 0 if there is an odd number of 1's in the data. You may also hear the terms space parity, mark parity, and no parity. Space parity means that the parity bit is always 0, while mark parity means the bit is always 1. No parity means that no parity bit is present or transmitted.

The remaining bits are called stop bits. There can be 1, 1.5, or 2 stop bits between characters and they always have a value of 1. Stop bits traditionally were used to give the computer time to process the previous character, but now only serve to synchronize the receiving computer to the incoming characters.

Asynchronous data formats are usually expressed as "8N1", "7E1", and so forth. These stand for "8 data bits, no parity, 1 stop bit" and "7 data bits, even parity, 1 stop bit" respectively.

What Are Full Duplex and Half Duplex?

Full duplex means that the computer can send and receive data simultaneously - there are two separate data channels (one coming in, one going out).

Half duplex means that the computer cannot send or receive data at the same time. Usually this means there is only a single data channel to talk over. This does not mean that any of the RS-232 signals are not used. Rather, it usually means that the communications link uses some standard other than RS-232 that does not support full duplex operation.

Flow Control

It is often necessary to regulate the flow of data when transferring data between two serial interfaces. This can be due to limitations in an intermediate serial communications link, one of the serial interfaces, or some storage media. Two methods are commonly used for asynchronous data.

The first method is often called "software" flow control and uses special characters to start (XON or DC1, 021 octal) or stop (XOFF or DC3, 023 octal) the flow of data. These characters are defined in the American Standard Code for Information Interchange ("ASCII"). While these codes are useful when transferring textual information, they cannot be used when transferring other types of information without special programming.

The second method is called "hardware" flow control and uses the RS-232 CTS and RTS signals instead of special characters. The receiver sets CTS to the space voltage when it is ready to receive more data and to the mark voltage when it is not ready. Likewise, the sender sets RTS to the space voltage when it is ready to send more data. Because hardware flow control uses a separate set of signals, it is much faster than software flow control which needs to send or receive multiple bits of information to do the same thing. CTS/RTS flow control is not supported by all hardware or operating systems.

What Is a Break?

Normally a receive or transmit data signal stays at the mark voltage until a new character is transferred. If the signal is dropped to the space voltage for a long period of time, usually 1/4 to 1/2 second, then a break condition is said to exist.

A break is sometimes used to reset a communications line or change the operating mode of communications hardware like a MODEM. Chapter 3, Talking to MODEMs covers these applications in more depth.

Synchronous Communications

Unlike asynchronous data, synchronous data appears as a constant stream of bits. To read the data on the line, the computer must provide or receive a common bit clock so that both the sender and receiver are synchronized.

Even with this synchronization, the computer must mark the beginning of the data somehow. The most common way of doing this is to use a data packet protocol like Serial Data Link Control ("SDLC") or High-Speed Data Link Control ("HDLC").

Each protocol defines certain bit sequences to represent the beginning and end of a data packet. Each also defines a bit sequence that is used when there is no data. These bit sequences allow the computer to see the beginning of a data packet.

Because synchronous protocols do not use per-character synchronization bits they typically provide at least a 25% improvement in performance over asynchronous communications and are suitable for remote networking and configurations with more than two serial interfaces.

Despite the speed advantages of synchronous communications, most RS-232 hardware does not support it due to the extra hardware and software required.

Accessing Serial Ports

Like all devices, UNIX provides access to serial ports via device files. To access a serial port you simply open the corresponding device file.

Serial Port Files

Each serial port on a UNIX system has one or more device files (files in the /dev directory) associated with it:

Table 2 - Serial Port Device Files
System Port 1 Port 2
IRIX® /dev/ttyf1 /dev/ttyf2
HP-UX /dev/tty1p0 /dev/tty2p0
Solaris®/SunOS® /dev/ttya /dev/ttyb
Linux® /dev/ttyS0 /dev/ttyS1
Digital UNIX® /dev/tty01 /dev/tty02

Opening a Serial Port

Since a serial port is a file, the open(2) function is used to access it. The one hitch with UNIX is that device files are usually not accessable by normal users. Workarounds include changing the access permissions to the file(s) in question, running your program as the super-user (root), or making your program set-userid so that it runs as the owner of the device file (not recommended for obvious security reasons...)

For now we'll assume that the file is accessable by all users. The code to open serial port 1 on a PC running Linux is show in Listing 1.

Listing 1 - Opening a serial port.

    #include <stdio.h>   /* Standard input/output definitions */
    #include <string.h>  /* String function definitions */
    #include <unistd.h>  /* UNIX standard function definitions */
    #include <fcntl.h>   /* File control definitions */
    #include <errno.h>   /* Error number definitions */
    #include <termios.h> /* POSIX terminal control definitions */

    /*
     * 'open_port()' - Open serial port 1.
     *
     * Returns the file descriptor on success or -1 on error.
     */

    int
    open_port(void)
    {
      int fd; /* File descriptor for the port */


      fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
      if (fd == -1)
      {
       /*
	* Could not open the port.
	*/

	perror("open_port: Unable to open /dev/ttyS0 - ");
      }
      else
	fcntl(fd, F_SETFL, 0);

      return (fd);
    }

Other systems would require the corresponding device file name, but otherwise the code is the same.

Open Options

You'll notice that when we opened the device file we used two other flags along with the read+write mode:

    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);

The O_NOCTTY flag tells UNIX that this program doesn't want to be the "controlling terminal" for that port. If you don't specify this then any input (such as keyboard abort signals and so forth) will affect your process. Programs like getty(1M/8) use this feature when starting the login process, but normally a user program does not want this behavior.

The O_NDELAY flag tells UNIX that this program doesn't care what state the DCD signal line is in - whether the other end of the port is up and running. If you do not specify this flag, your process will be put to sleep until the DCD signal line is the space voltage.

Writing Data to the Port

Writing data to the port is easy - just use the write(2) system call to send data it:

    n = write(fd, "ATZ\r", 4);
    if (n < 0)
      fputs("write() of 4 bytes failed!\n", stderr);

The write function returns the number of bytes sent or -1 if an error occurred. Usually the only error you'll run into is EIO when a MODEM or data link drops the Data Carrier Detect (DCD) line. This condition will persist until you close the port.

Reading Data from the Port

Reading data from a port is a little trickier. When you operate the port in raw data mode, each read(2) system call will return the number of characters that are actually available in the serial input buffers. If no characters are available, the call will block (wait) until characters come in, an interval timer expires, or an error occurs. The read function can be made to return immediately by doing the following:

    fcntl(fd, F_SETFL, FNDELAY);

The FNDELAY option causes the read function to return 0 if no characters are available on the port. To restore normal (blocking) behavior, call fcntl() without the FNDELAY option:

    fcntl(fd, F_SETFL, 0);

This is also used after opening a serial port with the O_NDELAY option.

Closing a Serial Port

To close the serial port, just use the close system call:

    close(fd);

Closing a serial port will also usually set the DTR signal low which causes most MODEMs to hang up.


Chapter 2, Configuring the Serial Port

This chapter discusses how to configure a serial port from C using the POSIX termios interface.

The POSIX Terminal Interface

Most systems support the POSIX terminal (serial) interface for changing parameters such as baud rate, character size, and so on. The first thing you need to do is include the file <termios.h>; this defines the terminal control structure as well as the POSIX control functions.

The two most important POSIX functions are tcgetattr(3) and tcsetattr(3). These get and set terminal attributes, respectively; you provide a pointer to a termios structure that contains all of the serial options available:

Table 3 - Termios Structure Members
Member Description
c_cflag Control options
c_lflag Line options
c_iflag Input options
c_oflag Output options
c_cc Control characters
c_ispeed Input baud (new interface)
c_ospeed Output baud (new interface)

Control Options

The c_cflag member controls the baud rate, number of data bits, parity, stop bits, and hardware flow control. There are constants for all of the supported configurations.
Table 4 - Constants for the c_cflag Member
Constant Description
CBAUD Bit mask for baud rate
B0 0 baud (drop DTR)
B50 50 baud
B75 75 baud
B110 110 baud
B134 134.5 baud
B150 150 baud
B200 200 baud
B300 300 baud
B600 600 baud
B1200 1200 baud
B1800 1800 baud
B2400 2400 baud
B4800 4800 baud
B9600 9600 baud
B19200 19200 baud
B38400 38400 baud
B57600 57,600 baud
B76800 76,800 baud
B115200 115,200 baud
EXTA External rate clock
EXTB External rate clock
CSIZE Bit mask for data bits
CS5 5 data bits
CS6 6 data bits
CS7 7 data bits
CS8 8 data bits
CSTOPB 2 stop bits (1 otherwise)
CREAD Enable receiver
PARENB Enable parity bit
PARODD Use odd parity instead of even
HUPCL Hangup (drop DTR) on last close
CLOCAL Local line - do not change "owner" of port
LOBLK Block job control output
CNEW_RTSCTS
CRTSCTS
Enable hardware flow control (not supported on all platforms)

The c_cflag member contains two options that should always be enabled, CLOCAL and CREAD. These will ensure that your program does not become the 'owner' of the port subject to sporatic job control and hangup signals, and also that the serial interface driver will read incoming data bytes.

The baud rate constants (CBAUD, B9600, etc.) are used for older interfaces that lack the c_ispeed and c_ospeed members. See the next section for information on the POSIX functions used to set the baud rate.

Never initialize the c_cflag (or any other flag) member directly; you should always use the bitwise AND, OR, and NOT operators to set or clear bits in the members. Different operating system versions (and even patches) can and do use the bits differently, so using the bitwise operators will prevent you from clobbering a bit flag that is needed in a newer serial driver.

Setting the Baud Rate

The baud rate is stored in different places depending on the operating system. Older interfaces store the baud rate in the c_cflag member using one of the baud rate constants in table 4, while newer implementations provide the c_ispeed and c_ospeed members that contain the actual baud rate value.

The cfsetospeed(3) and cfsetispeed(3) functions are provided to set the baud rate in the termios structure regardless of the underlying operating system interface. Typically you'd use the code in Listing 2 to set the baud rate.

Listing 2 - Setting the baud rate.

    struct termios options;

    /*
     * Get the current options for the port...
     */

    tcgetattr(fd, &options);

    /*
     * Set the baud rates to 19200...
     */

    cfsetispeed(&options, B19200);
    cfsetospeed(&options, B19200);

    /*
     * Enable the receiver and set local mode...
     */

    options.c_cflag |= (CLOCAL | CREAD);

    /*
     * Set the new options for the port...
     */

    tcsetattr(fd, TCSANOW, &options);

The tcgetattr(3) function fills the termios structure you provide with the current serial port configuration. After we set the baud rates and enable local mode and serial data receipt, we select the new configuration using tcsetattr(3). The TCSANOW constant specifies that all changes should occur immediately without waiting for output data to finish sending or input data to finish receiving. There are other constants to wait for input and output to finish or to flush the input and output buffers.

Most systems do not support different input and output speeds, so be sure to set both to the same value for maximum portability.

Table 5 - Constants for tcsetattr
Constant Description
TCSANOW Make changes now without waiting for data to complete
TCSADRAIN Wait until everything has been transmitted
TCSAFLUSH Flush input and output buffers and make the change

Setting the Character Size

Unlike the baud rate, there is no convienience function to set the character size. Instead you must do a little bitmasking to set things up. The character size is specified in bits:

    options.c_cflag &= ~CSIZE; /* Mask the character size bits */
    options.c_cflag |= CS8;    /* Select 8 data bits */

Setting Parity Checking

Like the character size you must manually set the parity enable and parity type bits. UNIX serial drivers support even, odd, and no parity bit generation. Space parity can be simulated with clever coding.

  • No parity (8N1):
    options.c_cflag &= ~PARENB
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    

  • Even parity (7E1):
    options.c_cflag |= PARENB
    options.c_cflag &= ~PARODD
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS7;
    

  • Odd parity (7O1):
    options.c_cflag |= PARENB
    options.c_cflag |= PARODD
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS7;
    

  • Space parity is setup the same as no parity (7S1):
    options.c_cflag &= ~PARENB
    options.c_cflag &= ~CSTOPB
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    

Setting Hardware Flow Control

Some versions of UNIX support hardware flow control using the CTS (Clear To Send) and RTS (Request To Send) signal lines. If the CNEW_RTSCTS or CRTSCTS constants are defined on your system then hardware flow control is probably supported. Do the following to enable hardware flow control:

    options.c_cflag |= CNEW_RTSCTS;    /* Also called CRTSCTS */

Similarly, to disable hardware flow control:

    options.c_cflag &= ~CNEW_RTSCTS;

Local Options

The local modes member c_lflag controls how input characters are managed by the serial driver. In general you will configure the c_lflag member for canonical or raw input.

Table 6 - Constants for the c_lflag Member
Constant Description
ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
ICANON Enable canonical input (else raw)
XCASE Map uppercase \lowercase (obsolete)
ECHO Enable echoing of input characters
ECHOE Echo erase character as BS-SP-BS
ECHOK Echo NL after kill character
ECHONL Echo NL
NOFLSH Disable flushing of input buffers after interrupt or quit characters
IEXTEN Enable extended functions
ECHOCTL Echo control characters as ^char and delete as ~?
ECHOPRT Echo erased character as character erased
ECHOKE BS-SP-BS entire line on line kill
FLUSHO Output being flushed
PENDIN Retype pending input at next read or input char
TOSTOP Send SIGTTOU for background output

Choosing Canonical Input

Canonical input is line-oriented. Input characters are put into a buffer which can be edited interactively by the user until a CR (carriage return) or LF (line feed) character is received.

When selecting this mode you normally select the ICANON, ECHO, and ECHOE options:

    options.c_lflag |= (ICANON | ECHO | ECHOE);

Choosing Raw Input

Raw input is unprocessed. Input characters are passed through exactly as they are received, when they are received. Generally you'll deselect the ICANON, ECHO, ECHOE, and ISIG options when using raw input:

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

A Note About Input Echo

Never enable input echo (ECHO, ECHOE) when sending commands to a MODEM or other computer that is echoing characters, as you will generate a feedback loop between the two serial interfaces!

Input Options

The input modes member c_iflag controls any input processing that is done to characters received on the port. Like the c_cflag field, the final value stored in c_iflag is the bitwise OR of the desired options.

Table 7 - Constants for the c_iflag Member
Constant Description
INPCK Enable parity check
IGNPAR Ignore parity errors
PARMRK Mark parity errors
ISTRIP Strip parity bits
IXON Enable software flow control (outgoing)
IXOFF Enable software flow control (incoming)
IXANY Allow any character to start flow again
IGNBRK Ignore break condition
BRKINT Send a SIGINT when a break condition is detected
INLCR Map NL to CR
IGNCR Ignore CR
ICRNL Map CR to NL
IUCLC Map uppercase to lowercase
IMAXBEL Echo BEL on input line too long

Setting Input Parity Options

You should enable input parity checking when you have enabled parity in the c_cflag member (PARENB). The revelant constants for input parity checking are INPCK, IGNPAR, PARMRK , and ISTRIP. Generally you will select INPCK and ISTRIP to enable checking and stripping of the parity bit:

    options.c_iflag |= (INPCK | ISTRIP);

IGNPAR is a somewhat dangerous option that tells the serial driver to ignore parity errors and pass the incoming data through as if no errors had occurred. This can be useful for testing the quality of a communications link, but in general is not used for practical reasons.

PARMRK causes parity errors to be 'marked' in the input stream using special characters. If IGNPAR is enabled, a NUL character (000 octal) is sent to your program before every character with a parity error. Otherwise, a DEL (177 octal) and NUL character is sent along with the bad character.

Setting Software Flow Control

Software flow control is enabled using the IXON, IXOFF, and IXANY constants:

    options.c_iflag |= (IXON | IXOFF | IXANY);

To disable software flow control simply mask those bits:

    options.c_iflag &= ~(IXON | IXOFF | IXANY);

The XON (start data) and XOFF (stop data) characters are defined in the c_cc array described below.

Output Options

The c_oflag member contains output filtering options. Like the input modes, you can select processed or raw data output.

Table 8 - Constants for the c_oflag Member
Constant Description
OPOST Postprocess output (not set = raw output)
OLCUC Map lowercase to uppercase
ONLCR Map NL to CR-NL
OCRNL Map CR to NL
NOCR No CR output at column 0
ONLRET NL performs CR function
OFILL Use fill characters for delay
OFDEL Fill character is DEL
NLDLY Mask for delay time needed between lines
NL0 No delay for NLs
NL1 Delay further output after newline for 100 milliseconds
CRDLY Mask for delay time needed to return carriage to left column
CR0 No delay for CRs
CR1 Delay after CRs depending on current column position
CR2 Delay 100 milliseconds after sending CRs
CR3 Delay 150 milliseconds after sending CRs
TABDLY Mask for delay time needed after TABs
TAB0 No delay for TABs
TAB1 Delay after TABs depending on current column position
TAB2 Delay 100 milliseconds after sending TABs
TAB3 Expand TAB characters to spaces
BSDLY Mask for delay time needed after BSs
BS0 No delay for BSs
BS1 Delay 50 milliseconds after sending BSs
VTDLY Mask for delay time needed after VTs
VT0 No delay for VTs
VT1 Delay 2 seconds after sending VTs
FFDLY Mask for delay time needed after FFs
FF0 No delay for FFs
FF1 Delay 2 seconds after sending FFs

Choosing Processed Output

Processed output is selected by setting the OPOST option in the c_oflag member:

    options.c_oflag |= OPOST;

Of all the different options, you will only probably use the ONLCR option which maps newlines into CR-LF pairs. The rest of the output options are primarily historic and date back to the time when line printers and terminals could not keep up with the serial data stream!

Choosing Raw Output

Raw output is selected by resetting the OPOST option in the c_oflag member:

    options.c_oflag &= ~OPOST;

When the OPOST option is disabled, all other option bits in c_oflag are ignored.

Control Characters

The c_cc character array contains control character definitions as well as timeout parameters. Constants are defined for every element of this array.

Table 9 - Control Characters in the c_cc Member
Constant Description Key
VINTR Interrupt CTRL-C
VQUIT Quit CTRL-Z
VERASE Erase Backspace (BS)
VKILL Kill-line CTRL-U
VEOF End-of-file CTRL-D
VEOL End-of-line Carriage return (CR)
VEOL2 Second end-of-line Line feed (LF)
VMIN Minimum number of characters to read -
VSTART Start flow CTRL-Q (XON)
VSTOP Stop flow CTRL-S (XOFF)
VTIME Time to wait for data (tenths of seconds) -

Setting Software Flow Control Characters

The VSTART and VSTOP elements of the c_cc array contain the characters used for software flow control. Normally they should be set to DC1 (021 octal) and DC3 (023 octal) which represent the ASCII standard XON and XOFF characters.

Setting Read Timeouts

UNIX serial interface drivers provide the ability to specify character and packet timeouts. Two elements of the c_cc array are used for timeouts: VMIN and VTIME. Timeouts are ignored in canonical input mode or when the NDELAY option is set on the file via open or fcntl.

VMIN specifies the minimum number of characters to read. If it is set to 0, then the VTIME value specifies the time to wait for every character read. Note that this does not mean that a read call for N bytes will wait for N characters to come in. Rather, the timeout will apply to the first character and the read call will return the number of characters immediately available (up to the number you request).

If VMIN is non-zero, VTIME specifies the time to wait for the first character read. If a character is read within the time given, any read will block (wait) until all VMIN characters are read. That is, once the first character is read, the serial interface driver expects to receive an entire packet of characters (VMIN bytes total). If no character is read within the time allowed, then the call to read returns 0. This method allows you to tell the serial driver you need exactly N bytes and any read call will return 0 or N bytes. However, the timeout only applies to the first character read, so if for some reason the driver misses one character inside the N byte packet then the read call could block forever waiting for additional input characters.

VTIME specifies the amount of time to wait for incoming characters in tenths of seconds. If VTIME is set to 0 (the default), reads will block (wait) indefinitely unless the NDELAY option is set on the port with open or fcntl.


Chapter 3, MODEM Communications

This chapter covers the basics of dialup telephone Modulator/Demodulator (MODEM) communications. Examples are provided for MODEMs that use the defacto standard "AT" command set.

What Is a MODEM?

MODEMs are devices that modulate serial data into frequencies that can be transferred over an analog data link such as a telephone line or cable TV connection. A standard telephone MODEM converts serial data into tones that can be passed over the phone lines; because of the speed and complexity of the conversion these tones sound more like loud screeching if you listen to them.

Telephone MODEMs are available today that can transfer data across a telephone line at nearly 53,000 bits per second, or 53kbps. In addition, most MODEMs use data compression technology that can increase the bit rate to well over 100kbps on some types of data.

Communicating With a MODEM

The first step in communicating with a MODEM is to open and configure the port for raw input as shown in Listing 3 .

Listing 3 - Configuring the port for raw input.

    int            fd;
    struct termios options;

    /* open the port */
    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
    fcntl(fd, F_SETFL, 0);

    /* get the current options */
    tcgetattr(fd, &options);

    /* set raw input, 1 second timeout */
    options.c_cflag     |= (CLOCAL | CREAD);
    options.c_lflag     &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_oflag     &= ~OPOST;
    options.c_cc[VMIN]  = 0;
    options.c_cc[VTIME] = 10;

    /* set the options */
    tcsetattr(fd, TCSANOW, &options);

Next you need to establish communications with the MODEM. The best way to do this is by sending the "AT" command to the MODEM. This also allows smart MODEMs to detect the baud you are using. When the MODEM is connected correctly and powered on it will respond with the response "OK".

Listing 4 - Initializing the MODEM.

    int                  /* O - 0 = MODEM ok, -1 = MODEM bad */
    init_modem(int fd)   /* I - Serial port file */
    {
      char buffer[255];  /* Input buffer */
      char *bufptr;      /* Current char in buffer */
      int  nbytes;       /* Number of bytes read */
      int  tries;        /* Number of tries so far */

      for (tries = 0; tries < 3; tries ++)
      {
       /* send an AT command followed by a CR */
	if (write(fd, "AT\r", 3) < 3)
	  continue;

       /* read characters into our string buffer until we get a CR or NL */
	bufptr = buffer;
	while ((nbytes = read(fd, bufptr, buffer + sizeof(buffer) - bufptr - 1)) > 0)
	{
	  bufptr += nbytes;
	  if (bufptr[-1] == '\n' || bufptr[-1] == '\r')
            break;
	}

       /* nul terminate the string and see if we got an OK response */
	*bufptr = '\0';

	if (strncmp(buffer, "OK", 2) == 0)
	  return (0);
      }

      return (-1);
    }

Standard MODEM Commands

Most MODEMs support the "AT" command set, so called because each command starts with the "AT" characters. Each command is sent with the "AT" characters starting in the first column followed by the specific command and a carriage return (CR, 015 octal). After processing the command the MODEM will reply with one of several textual messages depending on the command.

ATD - Dial A Number

The ATD command dials the specified number. In addition to numbers and dashes you can specify tone ("T") or pulse ("P") dialing, pause for one second (","), and wait for a dialtone ("W"):

    ATDT 555-1212
    ATDT 18008008008W1234,1,1234
    ATD T555-1212WP1234

The MODEM will reply with one of the following messages:

    NO DIALTONE
    BUSY
    NO CARRIER
    CONNECT
    CONNECT baud

ATH - Hang Up

The ATH command causes the MODEM to hang up. Since the MODEM must be in "command" mode you probably won't use it during a normal phone call.

Most MODEMs will also hang up if DTR is dropped; you can do this by setting the baud to 0 for at least 1 second. Dropping DTR also returns the MODEM to command mode.

After a successful hang up the MODEM will reply with "NO CARRIER". If the MODEM is still connected the "CONNECT" or "CONNECT baud" message will be sent.

ATZ - Reset MODEM

The ATZ command resets the MODEM. The MODEM will reply with the string "OK".

Common MODEM Communication Problems

First and foremost, don't forget to disable input echoing. Input echoing will cause a feedback loop between the MODEM and computer.

Second, when sending MODEM commands you must terminate them with a carriage return (CR) and not a newline (NL). The C character constant for CR is "\r".

Finally, when dealing with a MODEM make sure you use a baud that the MODEM supports. While many MODEMs do auto-baud detection, some have limits (19.2kbps is common on older MODEMs) that you must observe.


Chapter 4, Advanced Serial Programming

This chapter covers advanced serial programming techniques using the ioctl(2) and select(2) system calls.

Serial Port IOCTLs

In Chapter 2, Configuring the Serial Port we used the tcgetattr and tcsetattr functions to configure the serial port. Under UNIX these functions use the ioctl(2) system call to do their magic.

The ioctl system call takes three arguments:

    int ioctl(int fd, int request, ...);

The fd argument specifies the serial port file descriptor. The request argument is a constant defined in the <termios.h> header file and is typically one of the constants listed in Table 10.

Table 10 - IOCTL Requests for Serial Ports
Request Description POSIX Function
TCGETS Gets the current serial port settings. tcgetattr
TCSETS Sets the serial port settings immediately. tcsetattr(fd, TCSANOW, &options)
TCSETSF Sets the serial port settings after flushing the input and output buffers. tcsetattr(fd, TCSAFLUSH, &options)
TCSETSW Sets the serial port settings after allowing the input and output buffers to drain/empty. tcsetattr(fd, TCSADRAIN, &options)
TCSBRK Sends a break for the given time. tcsendbreak, tcdrain
TCXONC Controls software flow control. tcflow
TCFLSH Flushes the input and/or output queue. tcflush
TIOCMGET Returns the state of the "MODEM" bits. None
TIOCMSET Sets the state of the "MODEM" bits. None
FIONREAD Returns the number of bytes in the input buffer. None

Getting the Control Signals

The TIOCMGET ioctl gets the current "MODEM" status bits, which consist of all of the RS-232 signal lines except RXD and TXD, listed in Table 11.

To get the status bits, call ioctl with a pointer to an integer to hold the bits, as shown in Listing 5 .

Listing 5 - Getting the MODEM status bits.

    #include <unistd.h>
    #include <termios.h>

    int fd;
    int status;

    ioctl(fd, TIOCMGET, &status);
Table 11 - Control Signal Constants
Constant Description
TIOCM_LE DSR (data set ready/line enable)
TIOCM_DTR DTR (data terminal ready)
TIOCM_RTS RTS (request to send)
TIOCM_ST Secondary TXD (transmit)
TIOCM_SR Secondary RXD (receive)
TIOCM_CTS CTS (clear to send)
TIOCM_CAR DCD (data carrier detect)
TIOCM_CD Synonym for TIOCM_CAR
TIOCM_RNG RNG (ring)
TIOCM_RI Synonym for TIOCM_RNG
TIOCM_DSR DSR (data set ready)

Setting the Control Signals

The TIOCMSET ioctl sets the "MODEM" status bits defined above. To drop the DTR signal you can use the code in Listing 6.

Listing 6 - Dropping DTR with the TIOCMSET ioctl.

    #include <unistd.h>
    #include <termios.h>

    int fd;
    int status;

    ioctl(fd, TIOCMGET, &status);

    status &= ~TIOCM_DTR;

    ioctl(fd, TIOCMSET, &status);

The bits that can be set depend on the operating system, driver, and modes in use. Consult your operating system documentation for more information.

Getting the Number of Bytes Available

The FIONREAD ioctl gets the number of bytes in the serial port input buffer. As with TIOCMGET you pass in a pointer to an integer to hold the number of bytes, as shown in Listing 7.

Listing 7 - Getting the number of bytes in the input buffer.

    #include <unistd.h>
    #include <termios.h>

    int fd;
    int bytes;

    ioctl(fd, FIONREAD, &bytes);

This can be useful when polling a serial port for data, as your program can determine the number of bytes in the input buffer before attempting a read.

Selecting Input from a Serial Port

While simple applications can poll or wait on data coming from the serial port, most applications are not simple and need to handle input from multiple sources.

UNIX provides this capability through the select(2) system call. This system call allows your program to check for input, output, or error conditions on one or more file descriptors. The file descriptors can point to serial ports, regular files, other devices, pipes, or sockets. You can poll to check for pending input, wait for input indefinitely, or timeout after a specific amount of time, making the select system call extremely flexible.

Most GUI Toolkits provide an interface to select; we will discuss the X Intrinsics ("Xt") library later in this chapter.

The SELECT System Call

The select system call accepts 5 arguments:

    int select(int max_fd, fd_set *input, fd_set *output, fd_set *error,
               struct timeval *timeout);

The max_fd argument specifies the highest numbered file descriptor in the input, output, and error sets. The input, output, and error arguments specify sets of file descriptors for pending input, output, or error conditions; specify NULL to disable monitoring for the corresponding condition. These sets are initialized using three macros:

    FD_ZERO(&fd_set);
    FD_SET(fd, &fd_set);
    FD_CLR(fd, &fd_set);

The FD_ZERO macro clears the set entirely. The FD_SET and FD_CLR macros add and remove a file descriptor from the set, respectively.

The timeout argument specifies a timeout value which consists of seconds (timeout.tv_sec) and microseconds (timeout.tv_usec ). To poll one or more file descriptors, set the seconds and microseconds to zero. To wait indefinitely specify NULL for the timeout pointer.

The select system call returns the number of file descriptors that have a pending condition, or -1 if there was an error.

Using the SELECT System Call

Suppose we are reading data from a serial port and a socket. We want to check for input from either file descriptor, but want to notify the user if no data is seen within 10 seconds. To do this we'll need to use the select system call, as shown in Listing 8.

Listing 8 - Using SELECT to process input from more than one source.

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <sys/select.h>

    int            n;
    int            socket;
    int            fd;
    int            max_fd;
    fd_set         input;
    struct timeval timeout;

    /* Initialize the input set */
    FD_ZERO(&input);
    FD_SET(fd, &input);
    FD_SET(sock, &input);

    max_fd = (sock > fd ? sock : fd) + 1;

    /* Initialize the timeout structure */
    timeout.tv_sec  = 10;
    timeout.tv_usec = 0;

    /* Do the select */
    n = select(max_fd, &input, NULL, NULL, &timeout);

    /* See if there was an error */
    if (n < 0)
      perror("select failed");
    else if (n == 0)
      puts("TIMEOUT");
    else
    {
      /* We have input */
      if (FD_ISSET(fd, &input))
	process_fd();
      if (FD_ISSET(sock, &input))
	process_socket();
    }

You'll notice that we first check the return value of the select system call. Values of 0 and -1 yield the appropriate warning and error messages. Values greater than 0 mean that we have data pending on one or more file descriptors.

To determine which file descriptor(s) have pending input, we use the FD_ISSET macro to test the input set for each file descriptor. If the file descriptor flag is set then the condition exists (input pending in this case) and we need to do something.

Using SELECT with the X Intrinsics Library

The X Intrinsics library provides an interface to the select system call via the XtAppAddInput(3x) and XtAppRemoveInput(3x) functions:

int XtAppAddInput(XtAppContext context, int fd, int mask,
                  XtInputProc proc, XtPointer data);
void XtAppRemoveInput(XtAppContext context, int input);

The select system call is used internally to implement timeouts, work procedures, and check for input from the X server. These functions can be used with any Xt-based toolkit including Xaw, Lesstif, and Motif.

The proc argument to XtAppAddInput specifies the function to call when the selected condition (e.g. input available) exists on the file descriptor. In the previous example you could specify the process_fd or process_socket functions.

Because Xt limits your access to the select system call, you'll need to implement timeouts through another mechanism, probably via XtAppAddTimeout(3x).


Appendix A, Pinouts

This appendix provides pinout information for many of the common serial ports you will find.

RS-232 Pinouts

RS-232 comes in three flavors (A, B, C) and uses a 25-pin D-Sub connector:

Figure 2 - RS-232 Connector

Table 12 - RS-232 Signals
Pin Description Pin Description
1 Earth Ground 14 Secondary TXD
2 TXD - Transmitted Data 15 Transmit Clock
3 RXD - Received Data 16 Secondary RXD
4 RTS - Request To Send 17 Receiver Clock
5 CTS - Clear To Send 18 Unassigned
6 DSR - Data Set Ready 19 Secondary RTS
7 GND - Logic Ground 20 DTR - Data Terminal Ready
8 DCD - Data Carrier Detect 21 Signal Quality Detect
9 Reserved 22 Ring Detect
10 Reserved 23 Data Rate Select
11 Unassigned 24 Transmit Clock
12 Secondary DCD 25 Unassigned
13 Secondary CTS

RS-422 Pinouts

RS-422 also uses a 25-pin D-Sub connector, but with differential signals:

Figure 3 - RS-422 Connector

Table 13 - RS-422 Signals
Pin Description Pin Description
1 Earth Ground 14 TXD+
2 TXD- - Transmitted Data 15 Transmit Clock-
3 RXD- - Received Data 16 RXD+
4 RTS- - Request To Send 17 Receiver Clock-
5 CTS- - Clear To Send 18 Unassigned
6 DSR - Data Set Ready 19 RTS+
7 GND - Logic Ground 20 DTR- - Data Terminal Ready
8 DCD- - Data Carrier Detect 21 Signal Quality Detect
9 Reserved 22 Unassigned
10 Reserved 23 DTR+
11 Unassigned 24 Transmit Clock+
12 DCD+ 25 Receiver Clock+
13 CTS+

RS-574 (IBM PC/AT) Pinouts

The RS-574 interface is used exclusively by PC manufacturers and uses a 9-pin male D-Sub connector:

Figure 4 - RS-574 Connector

Table 14 - RS-574 (IBM PC/AT) Signals
Pin Description Pin Description
1 DCD - Data Carrier Detect 6 Data Set Ready
2 RXD - Received Data 7 RTS - Request To Send
3 TXD - Transmitted Data 8 CTS - Clear To Send
4 DTR - Data Terminal Ready 9 Ring Detect
5 GND - Logic Ground

SGI Pinouts

Older SGI equipment uses a 9-pin female D-Sub connector. Unlike RS-574, the SGI pinouts nearly match those of RS-232:

Figure 5 - SGI 9-Pin Connector

Table 15 - SGI 9-Pin DSUB Signals
Pin Description Pin Description
1 Earth Ground 6 DSR - Data Set Ready
2 TXD - Transmitted Data 7 GND - Logic Ground
3 RXD - Received Data 8 DCD - Data Carrier Detect
4 RTS - Request To Send 9 DTR - Data Terminal Ready
5 CTS - Clear To Send

The SGI Indigo, Indigo2, and Indy workstations use the Apple 8-pin MiniDIN connector for their serial ports:

Figure 6 - SGI 8-Pin Connector

Table 16 - SGI 8-Pin MiniDIN Signals
Pin Description Pin Description
1 DTR - Data Terminal Ready 5 RXD - Received Data
2 CTS - Clear To Send 6 RTS - Request To Send
3 TXD - Transmitted Data 7 DCD - Data Carrier Detect
4 GND - Logic Ground 8 GND - Logic Ground

Appendix B, ASCII Control Codes

This chapter lists the ASCII control codes and their names.

Control Codes

The following ASCII characters are used for control purposes:
Table 17 - ASCII Control Codes
Name Binary Octal Decimal Hexadecimal
NUL 00000000 000 0 00
SOH 00000001 001 1 01
STX 00000010 002 2 02
ETX 00000011 003 3 03
EOT 00000100 004 4 04
ENQ 00000101 005 5 05
ACK 00000110 006 6 06
BEL 00000111 007 7 07
BS 00001000 010 8 08
HT 00001001 011 9 09
NL 00001010 012 10 0A
VT 00001011 013 11 0B
NP, FF 00001100 014 12 0C
Name Binary Octal Decimal Hexadecimal
CR 00001101 015 13 0D
SO 00001110 016 14 0E
SI 00001111 017 15 0F
DLE 00010000 020 16 10
XON, DC1 00010001 021 17 11
DC2 00010010 022 18 12
XOFF, DC3 00010011 023 19 13
DC4 00010100 024 20 14
NAK 00010101 025 21 15
SYN 00010110 026 22 16
ETB 00010111 027 23 17
CAN 00011000 030 24 18
EM 00011001 031 25 19
SUB 00011010 032 26 1A
ESC 00011011 033 27 1B
FS 00011100 034 28 1C
GS 00011101 035 29 1D
RS 00011110 036 30 1E
US 00011111 037 31 1F

Appendix C, GNU Free Documentation License

Version 1.2, November 2002

Copyright (C) 2000,2001,2002  Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

0. PREAMBLE

The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

1. APPLICABILITY AND DEFINITIONS

This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.

The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.

A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".

Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.

A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.

The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.

2. VERBATIM COPYING

You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and you may publicly display copies.

3. COPYING IN QUANTITY

If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

4. MODIFICATIONS

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

  • A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
  • B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
  • C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
  • D. Preserve all the copyright notices of the Document.
  • E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
  • F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
  • G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
  • H. Include an unaltered copy of this License.
  • I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
  • J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
  • K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
  • L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
  • M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
  • N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
  • O. Preserve any Warranty Disclaimers.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.

You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

5. COMBINING DOCUMENTS

You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.

The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements."

6. COLLECTIONS OF DOCUMENTS

You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKS

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.

8. TRANSLATION

Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.

If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.

9. TERMINATION

You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

10. FUTURE REVISIONS OF THIS LICENSE

The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.

Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.

How to use this License for your documents

To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:

      Copyright (c)  YEAR  YOUR NAME.
      Permission is granted to copy, distribute and/or modify this document
      under the terms of the GNU Free Documentation License, Version 1.2
      or any later version published by the Free Software Foundation;
      with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
	 Texts.  A copy of the license is included in the section entitled "GNU
      Free Documentation License".

If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:

    with the Invariant Sections being LIST THEIR TITLES, with the
    Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.

If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.

If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.


Appendix D, Change History

This appendix lists the changes that have been made in this edition.

Edition 5, Revision 6

The following changes were made for the 6th revision:

  • The select() example did not correctly use the FD macros.
  • The title page and this appendix were not properly updated.

Edition 5, Revision 5

The following changes were made for the 5th revision:

  • The select() documentation did not correctly describe the FD macros.
  • Appendix C was missing the "how to use" part of the GNU FDL.

Edition 5, Revision 4

The following changes were made for the 4th revision:

  • Changed the description of the read() system call semantics in chapter 1.
  • Added descriptions for VSTART and VSTOP to chapter 2.

Edition 5, Revision 3

The following changes were made for the 3rd revision:

  • Now use the GNU Free Documentation License for the guide.
  • Changed the examples to use the Linux serial port filenames.
  • Put the infrastructure in place to allow for easier translations of the guide.
  • The guide text is now fully justified.

terminal 통신

12 저수준 터미널 인터페이스

이 장은 터미널 디바이스의 여러 가지를 정하는 함수를 설명하고 있다. 당신은 이 함수들을 통해서 입력의 반향(echoing)을 없애는 것과 같은 일을 할 수 있고; 속도와 흐름제어와 같은 직렬통신 문자들을 설정할 수도 있고; 파일의 끝, 코멘트-라인 편집, 신호 보내기, 그리고 유사한 신호 함수들을 위해 사용된 문자들을 변경할 수도 있다.

이 장에 있는 함수들의 대부분은 파일 기술자 상에서 동작한다. 8장[Low-Level I/O] 에 파일 기술자가 무엇이고, 터미널 디바이스를 위해 파일 기술자를 어떻게 개방하는지에 대한 자세한 정보가 있다.


12. 1 터미널 확인하기

이 장에서 설명하고 있는 함수들은 터미널 디바이스에 해당하는 파일에서만 동작한다. 당신은 isatty 함수를 사용해서 터미널과 연관된 파일기술자인지 어떤지를 알아낼 수 있다. isatty 와 ttyname 함수들을 위한 프로토타입은 헤더파일 'unistd. h'에 선언되어 있다.

함수 : int isatty (int filedes)

이 함수는 만일 filedes가 터미널 디바이스와 연관된 파일 기술자이면 1을 반환하고, 그렇지 않으면 0을 반환한다. 만일 파일기술자가 터미널과 연관되어 있다면, 당신은 ttyname함수를 사용해서 그 연관된 파일 이름을 얻을 수 있다. 또한 ctermid함수를 참고하라, 그것은 24. 7. 1절 [Identifying the Termina. ] 에 있다.

함수 : char *ttyname(int filedes)

만일 파일 기술자 filedes 가 터미널 디바이스와 연관되어 있으면, ttynaem함수는 정적으로 할당되고, 널 문자로 끝나는 문자열에 터미널 파일의 파일이름을 저장하여 그것을 가리키는 포인터를 반환한다. 만일 파일 기술자가 터미널과 연관되지 않거나, 또는 그 파일이름을 알아낼 수 없다면, 널 포인터를 반환한다.


12. 2 입/출력 큐

이 절에 있는 많은 함수들은 터미널 디바이스의 입력과 출력 큐(queues)를 조회한다. 이들 큐는 입/출력 스트림들에 의해 실행된 버퍼링의 커널안에 있는 버퍼링의 한 형식이다. ( 7장 [I/O on Streams] 참조. )

터미널 입력 큐는 또한 선행입력(typeahead) 버퍼로써 사용되어진다. 그 큐는 터미널로부터 받아들여졌지만, 아직 어느 프로세스에 의해서도 읽혀지지 않은 문자들은 저장한다.   -- 역자주: typeahead : 어떠한 이유로 인하여 입력의 속도가 프로그램의 작업처리 속도보다 빠를 경우 아직 처리되지 못한 입력들은 잠시 내부에 있는 기억장치에 저장해두고 나중에 처리하는 방법

터미널의 입력 큐의 크기는 _POSIX_MAX_INPUT 과 MAX_INPUT 파라미터로 표현된다; 27. 6절 [Limits for Files] 참조. 만일 IXOFF 입력 모드 비트가 설정되어서 입력 흐름제어가 가능하다면( 12. 4. 4절 [Input Modes] 참조), 터미널 드라이버는 큐가 오버플로우가 나는 것을 방지하기 위해, 필요할 때 터미널로 STOP 와 START 문자들을 전송한다. 그렇지 않다면, 터미널로부터 너무 많은 입력이 쇄도할 경우 그 입력을 잃어버릴지도 모른다. ( 이런 상황은 손으로 타이핑을 통해 입력하는 것으로는 불가능하다. )

터미널 출력 큐는 입력큐와 같지만, 출력을 위해 쓰인다. 출력큐는 프로세스에 의해 출력되어 졌지만, 아직 터미널로 전송되지 않은 문자들을 저장하고 있다. 만일 IXON 입력 모드 비트( 12. 4. 4절 [Input Modes] 참조)가 설정되어서 출력 흐름 제어가 가능하다면, 터미널 드라이버는 멈춤을 지시하기 위해 터미널에서 보낸 STOP 문자들과 출력의 재전송에 따른다.

터미널의 입력큐의 소거(Clearing)란 받아들이기는 했지만 아직 읽혀지지 않은 문자들을 버리는 것을 의미한다. 유사하게, 터미널 출력큐를 소거하기란 출력됐지만, 아직 전송되지 않은 문자들을 버리는 것을 의미한다.


12. 3 입력의 두 가지 스타일: Canonical 또는 Not

POSIX시스템은 입력의 두 가지 기본 모드를 제공한다: 정규와 비정규(canonical and noncanonical)

정규(canonical) 입력 프로세싱 모드에서, 터미널 입력은 새줄문자('\n'), EOF, 또는 EOL 문자들로 종료되는 한 라인으로 처리된다. 어떤 입력도 사용자에 의해 한 라인 전체의 입력이 종료되기 전에 읽혀질 수 없고, read 함수는( 8. 2절 [I/O Primitives] 참조), 얼마나 많은 바이트가 요청되었는지에 상관없이, 많아야 오직 한 줄의 입력을 반환할 뿐이다.

정규입력 모드에서, 운영체제는 입력 편집 도구를 제공한다: ERASE 와 KILL 문자들은 텍스트의 현재의 줄에서 편집 명령을 수행하기 위해 특별하게 해석되어진다.

상수 _POSIX_MAX_CANON 과 MAX_CANON는 정규 입력의 한 줄에 나타낼 수 있는 최대 바이트 수를 한정한다. 27. 6절 [Limits for Files] 참조.

비정규입력( noncanonical input ) 프로세싱 모드에서, 문자들은 라인들로 묶여지지 않고, ERASE 와 KILL 프로세싱은 수행되지 않는다. 비정규입력에서 읽혀진 바이트들은 MIN 과 TIME을 설정함으로 인해서 제어된다. 12. 4. 10절 [Noncanonical Input] 참조.

대부분의 프로그램들이 정규입력을 사용하는 것은, 정규입력의 방법이 사용자에게 라인으로 입력을 편집할 수 있는 방법을 제공하기 때문이다. 비정규입력을 사용하는 보통의 이유는 프로그램이 단일-문자 명령들을 받아들이거나 또는 프로그램 자체가 편집도구를 제공할 때 사용된다.

정규 혹은 비정규의 선택은 구조체 struct termios의 멤버인 c_lflag에 ICANON 플로그에 의해 제어된다. 12. 4. 7절 [Local Modes] 참조.


12. 4 터미널 모드

이 절은 어떻게 입력과 출력이 수행되어지는지를 제어하는 다양한 터미널 속성들은 설명하고 있다. 이곳에서 설명한 함수, 자료구조, 그리고 기호 상수들은 모두 헤더파일 'termios. h'에 선언되어 있다.


12. 4. 1 터미널 모드 데이터 타입들

터미널 속성의 전부는 구조체 struct termios 에 저장되어 있다. 이 구조체는 속성들을 읽고, 설정하기 위한 함수 tcgetattr 과 tcsetattr에서 사용된다.

데이터 타입 : struct termios

터미널의 모든 입출력 속성을 기록하는 구조체. 이 구조체는 적어도 다음과 같은 멤버들을 포함하고 있다.

tcflag_t c_iflag

입력 모드를 위한 플래그들을 정하는 비트마스크; 12. 4. 4절 [Input Modes] 참조.

tcflag_t c_oflag

출력모드를 위해 플래그들을 정하는 비트마스크; 12. 4. 5절 [Output Modes] 참조.

tcflag_t c_cflag

제어모드를 위해 플래그들을 정하는 비트마스크; 12. 4. 6절 [Control Modes] 참조.

tcflag_t c_lflag

로컬모드를 위해 플래그들은 정하는 비트마스크; 12. 4. 7절 [Local Modes] 참조.

cc_t c_cc[NCCS]

다양한 제어 함수들과 연관된 문자들을 정하는 배열; 12. 4. 9절 [Special Characters] 참조.

구조체 struct termios 는 또한 입력과 출력 전송 속도를 부호화(encode)한 멤버들을 갖고 있지만, 아직 설명하지 않았다. 12. 4. 8절 [Line Speed]를 참조로 해서 어떻게 속도 값을 시험하고 저장하는지를 살펴보라.

다음절에서 구조체 struct termios 의 멤버들에 대한 자세한 설명을 할 것이다.

데이터 타입 : tcflag__t

unsigned integer 형으로, 터미널플래그들을 위한 다양한 비트마스크를 나타내는데 사용한다.

데이터 타입 : cc__t

unsigned integer 형으로 다양한 터미널 제어 함수들과 연관된 문자들을 나타내기 위해 사용한다.

매크로 : int NCCS

이 매크로의 값은 c_cc 배열에 있는 요소들의 개수이다.

12. 4. 2 터미널 모드 함수들

함수 : int tcgetattr(int filedes, struct termios *termios_p)

이 함수는 파일기술자 filedes와 연관된 터미널 디바이스의 속성을 시험하는데 사용된다. 그 속성은 구조체 termios_p가 가리키는 곳으로 반환된다.
만일 성공하면, tcgetattr 은 0을 반환하고, 실패하면 -1을 반환한다.  
다음의 errno는 이 함수를 위해 정의된 에러상황이다.

EBADF : filedes 인수가 유용한 파일기술자가 아니다.

ENOTTY : filedes 가 터미널과 연관이 없다.

함수 : int tcsetattr(int filedes, int when, const struct termios *termios_p)

이 함수는 파일기술자 filedes와 연관된 터미널 디바이스의 속성을 설정한다. 새로운 속성들은 termios_p가 가리키고 있는 구조체로부터 가져온다.
when 인수는 이미 큐된( 큐에 저장되어 있는 ) 입력과 출력을 어떻게 취급할 것인지를 정하는 것으로 다음 값들중 하나를 사용할 수 있다.

TCSANOW : 즉시 속성을 변경시켜라.

TCSADRAIN

큐에 저장된 출력이 쓰여질 때까지 기다린 후에 속성을 변경하라. 당신은 변경하는 파라미터가 출력에 영향을 미칠 때 이 옵션을 사용한다.

TCSAFLUSH : 이것은 TCSADRAIN과 같지만, 큐에 저장된 입력을 버린다.

TCSASOFT

이것은 위에 있는 어떤 것과도 덧붙여 사용할 수 있는 플래그 비트이다. 이것은 터미널 하드웨어에 대한 상황의 변경을 금지하기 위한 것이다. 이것은 BSD 확장이다; BSD가 아닌 시스템에서는 아무런 영향을 받지 않는다.
만일 이 함수가 터미널을 제어하고 있는 배경 프로세스로부터 호출된다면, 보통 프로세스 그룹 안에 있는 모든 프로세스들은 터미널에 쓰기를 시도하는 프로세스가 있을 때와 같은 방법으로, SIGTTOU 시그널을 보낸다. 만일 함수를 호출한 프로세스 자신이 SIGTTOU 신호를 무시하거나 블록하고 있다면 이 명령은 수행되어 지고, 아무런 신호도 보내지 않는다. 24장 [Job control] 참조. 만일 성공하면,
tcsetattr 은 0을 반환하고, 실패하면 -1을 반환한다. 다음의 errno는 이 함수를 위해 정의된 에러 상황이다.

EBADF : filedes 인수는 유용한 파일기술자가 아니다.

ENOTTY : filedes는 터미널과 아무런 연관이 없다.

EINVAL : when 인수값이 유용하지 않거나, termios_p인수에 잘못된 데이터가 잘못되었거나.

tcgetattr 과 tcsetattr 이 filedes를 터미널과 연관된 파일기술자로 정했다고 할지라도, 그 속성은 파일기술자의 것이 아니라 터미널 디바이스 그 자체의 속성이다. 이것은 변경한 터미널 디바이스의 속성들이 불변함을 의미한다; 만일 나중에 다른 프로세스가 터미널 파일을 개방하면, 그것은 심지어 개방한 파일 기술자로 아무 일도 하지 못할지라도 변경된 속성들을 보일 것이다. 유사하게, 만일 단일 프로세스가 동일한 터미널 디바이스에 다중 또는 복제된 파일기술자들을 가졌다면, 터미널 속성 변경은 이 파일기술자 모두의 입력과 출력에 영향을 미친다. 이 의미는, 예를 들어, 당신은 한 개의 파일 기술자만을 개방할 수 없거나, 또는 반향 모드(echoed mode)로 보통의 라인 버퍼의 입력을 터미널로부터 읽기 위한 스트림을 개방할 수 없다; 그리고 동시에 당신은 비반향 모드로 동일한 터미널로부터 단일 문자를 읽기 위해 사용되는 다른 파일기술자를 가진다. 대신에 당신은, 두 개의 모드 사이에 어떤 것이 전면이고, 어떤 것이 후면인지 정확히 해야한다.


12. 4. 3 적당하게 터미널 모드 설정하기

당신이 터미널 모드를 설정할 때, 당신은 첫째로 특별한 터미널 디바이스의 현재의 모드를 얻기 위해 tcgetattr을 호출하고, 그 다음 실제로 당신이 관심을 가진 모드들을 갱신하고, tcseattr에 그 결과를 저장한다.

속성들의 설정을 선택하기 위해 구조체 struct termios를 간단히 초기화하고 직접적으로 tcsetatt에 인수로 주는 것은 나쁜 방법이다. 당신의 프로그램은 이 매뉴얼에 문서화되지 않은 멤버들을 지원하는 시스템에서 지금으로부터 수년 후에 실행되어질지도 모른다. 이해할 수 없는 값들로 이들 멤버들은 설정하는 것을 피하기 위한 방법은 그들을 변경하는 것을 피하는 것이다. 즉, 다른 터미널 디바이스는 적당하게 설정된 다른 모드를 필요로 한다. 그래서 당신은 하나의 터미널 디바이스로부터 다른 터미널 디바이스로 그냥 속성들을 카피하는 것은 피해야한다.

한 멤버가 c_iflag, c_oflag 그리고 c_cflag 등의 독립적 플래그들의 집합일 때, 그들 전체 멤버들을 설정하려하는 것은 잘못된 생각이다, 왜냐하면, 특별한 운영체제는 그들 자신만의 플래그들을 갖고 있기 때문이다. 대신에, 당신은 멤버의 현재의 값들을 얻어서, 당신의 프로그램에서 다룰 수 있는 플래그만 변경하고, 다른 플래그들을 변경하지 않도록 해라. 이곳에 구조체 struct termios의 다른 데이터를 그대로 유지하고, 오직 한 플래그(ISTRIP)만을 어떻게 변경하는지에 대한 예가 있다.

int
set_istrip (int desc, int value)
{
struct termios settings;
if (tcgetattr (desc, &settings) < 0) {
perror ("error in tcgetattr");
return 0;
}
settings. c_iflag &= ~ISTRIP;
if (value)
settings. c_iflag |= ISTRIP;
if (tcgetattr (desc, &settings) < 0) {
perror ("error in tcgetattr");
return;
}
return 1;
}

12. 4. 4 입력 모드들

이 절은 입력 프로세싱의 저수준 관점을 완전히 제어하는 터미널 속성 플래그들은 설명하고 있다: 에러 핸들링, 멈춤 신호들, 제어 흐름, 그리고 RET 와 LFD 문자들.

이들 플래그 모드는 구조체 struct termios 의 c_iflag안의 비트들이다. 그 멤버는 정수이고, 당신은 오퍼레이터 &, | 그리고 ^ 를 사용해서 플래그들은 변경할 수 있다. c_iflag_instead를 위한 전체 값을 정하여 시도하지 말고, 나머지는 손대지 말고 남겨둬라. ( 12. 4. 3절 [Setting Modes] 참조. )

INPCK

만일 이 비트가 설정되면, 입력 패리티 체크가 가능하다. 만일 설정되지 않으면, 입력에서 패리티 에러가 났는지 체크하지 않는다; 즉 그 문자들은 그 어플리케이션에 간단히 주어진다. 입력 프로세싱에서 패리티 체크는 패리티 검출의 여부에 독립하여 있고, 터미널 하드웨어에서의 패리티 발생은 가능하다; 12. 4. 6절 [Control Modes] 참조. 예를 들어, 당신은 INPCK 입력 모드 플래그를 소거하고 입력에서 패리티 에러들은 버리기 위해 PARENB 제어 모드 플래그를 설정할 수 있지만, 여전히 출력에서 패리티 에러는 발생한다.
만일 이 비트가 설정되면, 패리티 에러가 발생했을 때 IGNPAR 이나 PARMRK 비트가 설정되었는지에 의존하여 무슨 일이 발생한다. 이 비트를 설정하지 않는다면, 패리티 에러를 위한 한 바이트를 '\0'으로 응용프로그램에 주어야 한다.

IGNPAR

만일 이 비트가 설정되면, 구성(framing) 이나 패리티 에러를 위한 바이트가 무시되어진다. 이것은 INPCK 가 설정되어야지 만 유용하다.

PARMRK

만일 이 비트가 설정되면, 패리티나 구성 에러가 있는 입력 바이트들이 프로그램에 표시된다. 이 비트는 INPCK 가 설정되고 IGNPAR이 설정되지 않았을 때 유효하다. 잘못된 바이트를 표시하는 방법은 두 개의 선행 바이트, 377과 0을 사용하는 것이다. 그래서, 프로그램은 터미널로부터 받은 잘못된 한 개의 바이트로부터 세 개의 바이트들을 읽게되는 것이다. 만일 유용한 바이트가 0377의 값을 가지고, ISTRIP( 밑을 보라. ) 이 설정되지 않는다면, 그 프로그램은 그것을 패리티 에러 표시와 혼동할 것이다. 그래서 유용한 바이트 0377은 두 개의 바이트로 프로그램에 주어진다, 즉 이 경우에는 0377 0377로. . .

ISTRIP

만일 이 비트가 설정되면, 유용한 입력 바이트들이 일곱 비트로 구성되어 있다; 그렇지 않다면, 여덟 비트 모두가 읽혀서 프로그램에서 사용된다.

IGNBRK

만일 이 비트가 설정되면, 멈춤(break)의 상황이 무시된다.
멈춤(break) 상황은 한 바이트보다 긴 0-값을 가진 비트들의 열들을 비동기적 직렬 데이터 전송의 방법으로 정의된다.

BRKINT

만일 이 비트가 설정되고, IGNBRK 가 설정되지 않는다면, 멈춤(break)의 상황은 터미널 입력과 출력의 큐를 소거하고, 터미널과 연관있는 전면 프로세스 그룹을 위해서 SIGINT 신호를 발생한다. 만일 BRKINT 나 IGNBRK 가 설정되지 않았다면, 멈춤(break) 상황은 만일 PARMRK가 설정되지 않으면, 단일 문자 '\0'로 응용프로그램에 주어진다, 그렇지 않으면, 세 개의 문자열 '\377', '\0', '\0'로 주어진다.

IGNCR

만일 이 비트가 설정되면, 캐리지반환 문자('\r')는 입력에서 버려진다. 버려진 캐리지반환은 당신이 RET 키를 칠 때 캐리지반환과 라인피드(linefeed) 이 둘을 보낸 터미널에서 유용하게 될 것이다.

ICRNL

만일 이 비트가 설정되고 IGNCR 이 설정되지 않으면, 입력으로 받은 캐리지반환문자를('\r') 새줄문자('\n')로해서 응용프로그램에 주어진다.

INLCR

만일 이 비트가 설정되면, 입력으로 받은 새줄문자('\n')는 캐리지반환문자('\r')로 응용프로그램에 주어진다.

IXOFF

만일 이 비트가 설정되면, 입력의 정지/시작의 제어가 가능하다. 즉, 컴퓨터가, 프로그램이 처리하는 속도보다 더 빠르게 데이터가 도착하는 것을 방지하기 위해 STOP과 START 문자들을 보내는 것이다. 입력 데이터를 생성하는 실제 터미널 하드웨어에서 STOP 문자에 응답하여 전송을 중단하고, START 문자에 응답하여 다시 전송을 재개한다. 12. 4. 9. 4절 [Strat/Stop Characters] 참조.

IXON

만일 이 비트가 설정되면, 출력의 시작/정지의 제어가 가능하다. 즉, 만일 컴퓨터가 STOP 문자를 받으면, START 문자를 받을 때까지 출력을 중단한다. 이 경우, STOP 과 START 문자들은 결코 응용프로그램에 주어지는 것이 아니다. 만일 이 비트가 설정되지 않았다면 START와 STOP는 원래의 문자들로 읽혀진다. 12. 4. 9. 4절 [Start/Stop Characters] 참조.

IXANY

만일 이 비트가 설정되면, 출력이 STOP를 통해 정지되어져 있을 때, 어느 문자를 가지고도 출력을 재개할 수 있다. 즉, 오직 START 문자만 출력을 재개하는 것이 아니라는 것이다.

IMAXBEL

만일 이 비트가 설정되면, 벨이 울리도록 터미널에 BEL문자( code 007)를 보내어 터미널의 입력 버퍼를 가득 채운다.

12. 4. 5 출력 모드들

이 절은 출력 문자들을 어떻게 해석하고 화면을 채울 것인지를 제어하는 터미널 플래그와 필드들에 대해 설명한다. 이들 모두는 구조체 struct termios의 c_oflag 멤버에 들어있다.

c_oflag 멤버 자신은 정수형이고, 당신은 그 플래그들과 필드들을 오퍼레이터 &, |, 그리고 ^를 사용해서 갱신할 수 있다. c_oflag의 전체의 값을 변경하려 시도하지 말고_대신에 오직 정해진 하나의 플래그만 변경하고 나머지는 손대지 말고 남겨둬라 ( 12. 4. 3절 [Setting Modes] 참조.

매크로 : int OPOST

이 비트가 설정되면, 출력 데이터는 터미널 디바이스에 적당하게 표시되는 그런 특별히 정해지지 않은 방법으로 처리된다. 이것은 새줄문자 ('\n')를 캐리지반환과 라인피드의 쌍으로 대치시켜 포함한다. 만일 이 비트가 설정되지 않는다면, 그 문자들은 그대로 전송된다.

다음 세 개의 비트들은 BSD를 위한 것으로, BSD 가 아닌 시스템에는 아무런 영향을 주지 않는다. 모든 시스템에서, 그들은 OPOST가 설정 되어있어야 효과를 발휘한다.

    매크로 : int ONLCR

    만일 이 비트가 설정되면, 출력에 나타난 새줄문자를 문자의 쌍(pair)인 캐리지반환과 라인피드로 변환한다.

    매크로 : int OXTABS

    만일 이 비트가 설정되면, 출력에 나타난 탭 문자들을 8칼럼의 탭을 구현하는 적당한 공백으로 변환한다.

    매크로 : int ONOEOT

    만일 이 비트가 설정되면, 출력에 나타나는 C-d 문자(code 004)들을 버린다. 이들 문자들은 dial-up 터미널의 연결을 단절시키기 때문이다.

 

12. 4. 6 제어 모드들

이 절은 비동기 직렬 데이터 전송에 관계된 제어 파라미터인 터미널 플래그와 필드들을 설명한다. 이들 플래그들은 터미널 포트(네트웍에 연결된 가상-터미널처럼)의 다른 종류에는 통하지 않을지도 모른다. 이들 모두는 구조체 struct termios의 c_cflag 멤버에 존재한다.

c_cflag 멤버 자체는 정수형으로, 당신은 그 플래그와 필드들을 오퍼레이터 &, |, 그리고 ^ 을 사용해서 갱신할 수 있다. c_cflag의 전체 값들을 정하려 시도하지 말고, 대신에 오직 정해진 플래그들만 변경하고, 나머지는 손대지 말고 남겨둬라(12. 4. 3절 [Setting Modes] 참조. )

CLOCAL

만일 이 비트가 설정되면, 터미널이 "국부적으로" 연결되어 있고, 모뎀 상태 라인들은( 캐리어 검출과 같은)무시됨을 의미한다. 만일 이 비트가 설정되지 않고 당신이 O_NONOBLOCK 플래그를 설정하지 않고 open을 호출하면, open은 모뎀이 연결될 때까지 블록 된다.
만일 이 비트가 설정되지 않고, 모뎀 연결이 끊어지면, SIGHUP 신호를 터미널(만일 그것이 하나를 가진다면)을 위한 제어 프로세스 그룹에 보낸다. 보통, 이것은 탈출(exit) 하려는 프로세스에 때문이다; 21장 [Signal Handling] 참조. 연결이 끊어진 후에 터미널로부터 읽기는 파일끝 상황을 발생하고, 쓰기는 반환될 EIO 에러를 발생한다. 터미널 디바이스는 반드시 닫혀져야 하고, 그 상황을 소거하기 위해 재개방되어져야 한다.

HUPCL

만일 이 비트가 설정되면, 모든 프로세스가 폐쇄된 파일인 터미널 디바이스를 갖거나, 빠져나갈 때 모뎀 단절(disconnect)이 발생한다.

CREAD

만일 이 비트가 설정되면, 입력이 터미널로부터 읽혀질 수 있다. 그렇지 않다면, 입력은 그것이 도착했을 때 버려진다.

CSTOPB

만일 이 비트가 설정되면, 두 개의 stop 비트가 사용된다. 그렇지 않다면 오직 한 개의 stop 비트가 사용된다.

PARENB

만일 이 비트가 설정되면, 패리티 비트의 생성과 검출이 가능하게 된다. 12. 4. 4절 [Input Modes] 를 참조로 입력 패리티 에러가 어떻게 다루어지는지를 보아라.
만일 이 비트가 설정되지 않으면, 출력 문자들에 패리티 비트가 더해지지 않고, 입력 문자들에서는 패리티에러를 체크하지 않는다.

PARODD

이 비트는 PARENB가 설정됐을 때만 유용하다. 만일 PARODD가 설정되면, 홀수 패리티가 사용되고, 그렇지 않으면 짝수 패리티가 사용된다.
제어 모드 플래그들은 문자당 비트들의 개수를 나타내는 필드를 갖고 있다. 당신은 그 값을 추출하기 위해서 CSIZE 매크로를 사용할 수 있다. 이처럼 : settings. c_cflag & CSIZE

CSIZE : 이것은 문자당 비트들의 개수를 위한 마스크이다.

    CS5 : 바이트당 다섯 비트를 정한다.

    CS6 : 바이트당 여섯 비트를 정한다.

    CS7 : 바이트당 일곱 비트들을 정한다.

    CS8 : 바이트당 여덟 비트들을 정한다.

CCTS_OFLOW

이 비트가 설정되면, CTS와이어(RS232 프로토콜)에 기반한 출력의 흐름제어가 가능하다.

CRTS_IFLOW

이 비트가 설정되면, RTS 와이어(RS232 프로토콜)에 기반한 입력의 흐름제어가 가능하다.

MDMBUF

이 비트가 설정되면, 출력의 캐리어-기반 흐름제어가 가능하다.

12. 4. 7 국소 모드들

이 절은 구조체 struct termios 의 c_lflag 멤버의 플래그들을 설명한다. 이들 플래그들은 일반적으로 12. 4. 4절 [Input Modes] 에서 설명한, 모드 플래그들보다는 반향, 신호들, 그리고 정규와 비졍규입력의 선택 등과 같은 입력 프로세싱의 고-수준 관점을 제어한다.

c_flag 멤버 그 자체는 정수형이고, 당신은 오퍼레이터 &, |, 그리고 ^를 사용해서 그 플래그들과 필드를 변경할 수 있다. c_lflag의 전체 값들을 정하려 시도하지 말고 대신에, 정해진 플래그들만 변경하고, 다른 나머지 것들에는 손대지 말라.

ICANON

이 비트가 설정되면, 정규입력 프로세싱 모드로 된다. 그렇지 않다면, 입력은 비정규 모드로 처리된다. 12. 3절 [Canonical or Not] 참조.

ECHO : 이 비트가 설정되면, 입력문자가 반향된다.

ECHOE

이 비트가 설정되면, 스크린의 현재의 라인에 있는 마지막 문자를 지우는 역할을 하는 ERASE 문자를 사용 할 때 그 마지막 문자를 화면상에서 실제로 없앰으로서 사용자에게 문자가 실제로 지워졌음을 확인시킨다. 그렇지 않다면 지워진 문자는 다시 반향된다( 프린팅 터미널에 적당한). 이 비트는 오직 앞에 표시된 것만을 제어한다. ECHOE를 사용하지 않고도, ICANON 비트는 ERASE문자를 실제로 인식하고 입력을 지우는 것을 그 자체에서 제어하고 있다.

ECHOK

이 비트는 KILL 문자에 대한 표시를 가능하게 한다. 이것을 할 수 있는 두 가지 방법이 있다. KILL 문자가 눌려진 곳의 전체 라인을 화면에서 지우는 것이 좀더 나은 방법이다. 좀더 안 좋은 방법은 KILL 문자를 반향한 후에 새줄로 옮기는 것이다. 어떤 시스템은 한가지를 허용하고, 어떤 시스템은 다른 하나를 허용하고, 어떤 것은 당신에게 두가지중 하나를 선택하도록 허용한다. 이 비트가 설정되지 않으면, KILL 문자는 KILL 문자가 존재하지 않는 것처럼 단지 그것을 반향한다. 그러면 전의 입력을 지웠던 KILL 문자를 기억하는 것은 사용자의 몫이다; 화면에서 아무런 표시가 없기 때문이다. 이 비트는 오직 전의 표시된 것을 제어한다. ICANON 비트는 그 자체로 KILL 문자를 실제로 인식하고, 입력을 지운다.

ECHONL

이 비트가 설정되고 ICANON 비트가 또한 설정되면 새줄 ('\n') 문자는 심지어 ECHO 비트가 설정되지 않았을 때도 반향된다.

ISIG

INTR, QUIT, 그리고 SUSP 문자들을 인식하는지의 여부에 대한 제어 비트이다. 이 문자들과 연관된 함수들은 이 비트가 설정됐을 때만 수행된다. 규정이나 비규정 입력에 대한 것은 이들 문자들을 해석하는데 아무런 영향이 없다. 당신은 이들 문자들의 인식을 불가능할 때 주의해서 사용해야한다. 사용자에 의해 인터럽트될 수 없는 프로그램은 사용자에게 매우 불친절한 것이다. 만일 당신이 이 비트를 소거하면, 당신의 프로그램은 사용자에게 이들 문자들과 연관있는 신호를 보내도록 허용하거나, 그 프로그램으로부터 탈출하는 인터페이스를 제공해야한다. 12. 4. 9절 [Signal Characters] 참조.

IEXTEN

이 비트는 ISIG와 유사하지만, 특별한 문자들에 정의된 제어실행에 대한 것이다. 만일 이 비트가 설정되면, ICANON 과 ISIG 국소 모드 플래그와 IXON과 IXOFF 입력모드 플래그를 위한 디폴트 동작을 무효로 할 것이다.

NOFLSH

보통, INTR, QUIT, 그리고 SUSP 문자들은 터미널의 입력과 출력큐를 소거하게 한다. 만일 이 비트가 설정되면 그 큐들은 소거되지 않는다.

TOSTOP

이 비트가 설정되고 시스템이 작업 제어를 지원하면, SIGTTOU신호가 터미널에 쓰기를 시도하는 배경 프로세스에 의해 발생되어진다. 24. 4절 [Access to the Terminal] 참조.

다음 비트들은 BSD 확장이다. GNU 라이브러리는 당신이 그들을 요청하는 어느 시스템 상에서도 사용하도록 이들 심볼들을 정의했지만, BSD 시스템과 GNU시스템을 제외한 다른 시스템에는 아무런 영향을 미치지 않는 비트 설정이다.

ECHOKE

BSD 시스템에서, 이 비트는 ECHOK를 설정할 때, KILL 문자를 표시하는 두 가지 방법중 하나를 선택하게 한다. 만일 ECHOKE 가 설정되면, KILL문자는 화면에서 라인 전체를 지우고 그렇지 않으면 KILL문자는 화면의 다음 라인으로 옮긴다. ECHOKE의 설정은 ECHOK가 설정됐을 때만 유효하다.

ECHOPRT

이 비트는 하드카피 터미널을 조정하는 방법으로 ERASE 문자의 표시를 가능하게 한다.

ECHOCTL

만일 이 비트가 설정되면, 해당하는 텍스트 문자가 따르는 control문자를 '^'표시로 반향한다. 그래서 control-A 는 '^A'처럼 나타난다.

ALTWERASE

이 비트는 WERASE 문자가 지울 것인지 결정한다. 단어의 시작점에서 뒤쪽으로 문자를 지운다. 어디에서 단어를 시작할 것인지 의문이 생긴다. 만일 이 비트가 설정되면, 단어의 시작점은 공백문자 다음의 비공백 문자가 된다. 만일 이 비트가 설정되지 않으면, 단어의 시작점은 영숫자 문자이거나, 또는 그들이 없는 문자 다음의 underscore이다.

FLUSHO

이 비트는 사용자가 DISCARD 문자를 입력할 때 토글 된다. 이 비트가 설정되어 있는 동안에, 모든 출력은 버려진다. 12. 4. 9. 5절 [Othr Special] 참조.

NOKERNINFO

이 비트의 설정은 STATUS 문자의 처리가 불가능하도록 한다. 12. 4. 9. 5절 [Other Special] 참조.

PENDIN

이 비트가 설정되면, 다시 프린트할 필요가 있는 입력 라인이 있음을 알린다. REPRINT문자를 치면 이 비트가 설정된다. 그 비트는 재프린트가 종료될 때까지 설정상태가 지속된다. 12. 4. 9. 2절 [BSD Editing] 참조.

12. 4. 8 라인 속도

터미널 라인 속도는 터미널 상에서 얼마나 빨리 데이터를 읽고 쓸 수 있는지를 컴퓨터에게 알린다. 만일 터미널이 직렬 라인으로 연결되면, 터미널 속도를 그 라인의 실제 속도에 맞도록 지정해야지, 터미널 자신의 임의대로 그 속도를 지정할 수 없고, 만약 그렇게 했다면 통신은 단절된다. 실제 직렬 포트는 오직 어떤 표준 속도만을 받아들인다. 어떤 특별한 하드웨어는 심지어 모든 표준 속도를 지원하지 않을 것이다. 제로(zero)로 속도를 정하는 것은 연결된 상태를 끊고 모뎀 제어 신호를 끄는 것이다.

만일 터미널이 직렬 라인이 아니라면(예를 들어, 그것이 네트웍 연결이라면), 라인의 속도는 실제 데이터 전송속도에 영향을 받지 않지만, 어떤 프로그램에서는 필요한 채워넣기(padding)의 양을 결정하기 위해 그것을 사용할 것이다. 그것은 실제 터미널의 속도에 대응되는 라인 속도 값을 정하기에 가장 좋지만, 당신은 채워넣기(padding)의 다양한 양을 다양한 값으로 안전하게 시험해야 한다.

각 터미널을 위한 두 개의 라인 속도가 있는데, 입력을 위한 것과 출력을 위한 것이다. 당신은 그들을 독립적으로 정할 수 있지만, 대부분의 터미널에서는 동일한 속도를 사용한다.

속도 값은 구조체 struct termios 에 저장되어 있지만, 직접적으로 구조체 struct termios안에 있는 그들을 직접적으로 억세스 하려 시도하지 말라. 대신에, 당신은 그들을 읽고 저장하기 위해서 다음 함수를 사용하라.

함수 : speed_t cfgetospeed(const termios *termios_p)

이 함수는 구조체 *termios`p안에 저장되어 있는 출력 라인 속도를 반환한다.

함수 : speed_t cfgetispeed (const struct termios *termios_p)

이 함수는 구조체 *termios`p안에 저장되어 있는 입력 라인 속도를 반환한다.

함수 : int cfsetospeed (struct termios *termios_p, speed_t speed)

이 함수는 출력 속도로 *termios`p 에 speed를 반환한다. 보통의 반환 값은 0이고, 에러가 발생하면 -1을 반환한다. 만일 speed가 속도 값이 아니면, cfsetospeed는 -1을 반환한다.

함수 : int cfsetispeed (struct termios *termios_p, speed_t speed)

이 함수는 입력 속도로 *termios`p에 speed를 저장한다. 보통의 반환 값은 0이고 에러가 발생하면 -1을 반환한다. 만일 speed가 속도 값이 아니라면, cfsetopeeed는 -1을 반환한다.

함수 : int cfsetspeed (struct termios *termios_p, speed_t speed)

이 함수는 입력과 출력의 속도 둘을 위해 *termios`p로 speed를 저장한다. 보통 반환 값은 0이고, 에러가 발생하면 -1을 반환한다. 만일 speed가 속도 값이 아니라면, cfsetspeed는 -1을 반환한다. 이 함수는 4. 4 BSD 확장이다.

데이터타입: speed_t

speed_type은 unsigned integer형으로 라인의 속도를 나타내기 위해 사용된다.

cfsetospeed 와 cfsetispeed 함수는 그 시스템이 간단히 취급할 수 없는 속도 값일 경우에만 에러를 표시한다. 만일 당신이 기본적으로 받아들일 수 있는 속도 값을 지정하면, 이 함수는 성공할 것이다. 그러나 그들은 어떤 특별한 하드웨어가 정해진 속도를 실제로 지원할 수 있는지를 체크할 수 없고, 실제로 그들은 당신이 속도를 설정하려 계획하는 디바이스를 알지 못한다. 만일 당신이 처리될 수 없는 값으로 특별한 디바이스의 속도를 설정하려고 tcsetattr을 사용하면, tcsetattr은 -1을 반환한다.   이식성 노트: GNU 라이브러리에서, 함수들은 입력과 출력으로 초(second)당 비트로 계산된 속도를 받아들인다. 다른 라이브러리들은 속도를 특별한 코드로 지정해준다. POSIX. 1과 이식성을 위해서는, 당신은 속도를 나타내기 위한 다음의 심볼들 중의 하나를 사용해야만 한다; 그들의 정확한 숫자적 값들은 시스템_의존적이지만, 각 이름은 정해진 의미를 갖고 있다. B110은 110 bps를 위한 것이고, B300은 300 bps를 위한 것이고. . 등등. . 이것은 속도를 나타내기 위한 다른 방법들과 전혀 이식성이 없지만, 그들은 특별한 직렬 라인을 지원할 수 있는 속도이다.

B0 B50 B75 B110 B134 B150 B200

B300 B600 B1200 B1800 B2400 B4800

B9600 B19200 B38400

BSD는 유사이름으로 두 개의 부가적 속도 심볼들은 정의한다. EXTA 는 B19200과 같고, EXTB는 B38400과 같다. 이들 유사어는 오래된 것이다.

함수 : int cfmakeraw (struct termios *termios_p)

이 함수는 BSD에서 전통적으로 원래의 모드"로 불려지는 모드로 *termios`p를 설정하는 쉬운 방법을 제공한다. 그것은 정확히 이런 일을 한다.
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB);
termios_p->c_cflag |= CS8;

12. 4. 9 특별 문자들

정규입력에서, 터미널 구동기는 다양한 제어 함수들을 수행하는 특별문자들의 수를 인식한다. 이들은 편집입력을 위한 ERASE 문자(보통은 DEL)와 그리고 다른 편집문자들이 포함된다. SIGINT 신호를 보내기 위한 INTR 문자( 보통 C -c)와 다른 신호-발생 문자들은, 다른 정규입력이나 비정규 입력 모드에서 유용할 것이다. 모든 이들 문자들은 이 절에 설명되어 있다.

특별한 문자들은 구조체 struct termios의 c_cc 멤버에 정해져있다. 이 멤버는 배열이다; 각 요소는 특별한 규칙을 가진 문자를 정한다. 각 요소는 그 요소의 인덱스를 위한 심볼 상수를 갖는다_예를 들어 INTR은 INTR 문자를 정하는 요소의 인덱스이다, 그래서 INTR 문자로 '='을 정하여 termios. c_cc[INTR]에 '='을 저장한다.

다른 시스템에서, 당신은 _POSIX_VDISABLE를 정하여 특별 문자 함수들을 불가능하게 할 수 있다. 이 값은 어느 가능한 문자 코드와 같지 않다. 27. 2절 [Options for Files] 를 참고로, 운영체제가 _POSIX_VDISABLE를 당신에게 지원하는지의 여부를 어떻게 알 수 있는지를 보아라.


12. 4. 9. 1 입력 편집을 위한 문자들

이들 특별 문자들은 오직 정규(canonical) 입력 모드에서만 작동된다. 12. 3절 [Canonical or Not] 참조.

매크로 : int VEOF

이것은 특별 제어 문자 배열에 있는 EOF 문자를 위한 첨자(subscript)이다. termios. c_cc[VEOF] 는 그 문자 자체를 저장하고 있다. EOF 문자는 오직 정규입력 모드에서만 인식된다. 그 문자는 새줄문자와 같은 방법으로 라인의 종료자(terminator)로서 동작하지만, 만일 EOF 문자가 라인의 처음에 존재된다면, 0바이트를 반환하여, 파일의 끝임을 지적한다. EOF 문자 그 자체는 버려진다. 보통, EOF 문자는 C-d 이다.

매크로 : int VEOL

이것은 특별 제어 문자배열에 있는 EOL 문자를 위한 첨자이다. termios. c_cc[VEOL] 은 문자 그 자체를 저장하고 있다. EOL 문자는 오직 정규입력 모드에서만 인식된다. 그것은 새줄문자처럼 라인 종료자(terminator) 로서 동작한다. 입력 라인에서 마지막 문자로 읽혀진 EOL문자는 버려진다. 한 라인의 끝인 RET을 만들기 위해서 EOL 문자를 사용할 필요가 없다. 단지 ICRNL 플래그를 설정하라. 실제로, 이것이 디폴트 상황이다.

매크로 : int VERASE

이것은 특별 제어문자 배열에 있는 ERASE문자를 위한 첨자이다. termios. c_cc[VERASE] 는 그 문자 자체를 저장한다. ERASE문자는 오직 정규입력 모드에서만 인식된다. 사용자가 erase 문자를 입력할 때, 전에 입력된 문자가 버려진다. ( 만일 그 터미널이 다중 바이트 (multibyte)문자열을 발생시킨다면, 이것은 입력에서 버려진 것이 한 바이트보다 더 많을 수도 있다. ) 이것은 텍스트의 현재 라인보다 앞의 것을 지울 수 없다. ERASE 문자 그 자체는 버려진다. 보통 ERASE문자는 DEL 이다.

매크로 : int VKILL

이것은 특별 제어문자 배열에 있는 KILL 문자를 위한 첨자이다. termios. c_cc[VKILL] 은 문자 그 자체를 저장하고 있다. KILL 문자는 오직 정규입력 모드에서만 인식된다. 사용자가 kill 문자를 입력할 때, 입력의 현재라인의 전체의 내용이 버려진다. kill 문자도 버려진다. KILL 문자는 보통 C-u 이다.

12. 4. 9. 2 편집 문자의 BSD 확장

이들 특별 문자들은 오직 정규입력 모드에서만 동작한다. 12. 3절 [Canonical of Not] 참조. 그들은 BSD 확장이다; GNU 라이브러리는 그들을 요청하면, 어느 시스템 상에서든지 그 심볼들을 사용하게 하지만, 그 문자들은 BSD 시스템을 제외하고는 아무 데서도 동작하지 않을 것이다.

매크로 : int VEOL2

이것은 특별 제어문자 배열에 있는 EOL2 문자를 위한 첨자이다. termios. c_cc[VEOL2] 는 문자 그 자체를 저장한다. EOL2문자는 EOL문자처럼 동작하지만( 위를 보라 ) 다른 문자가 될 수 있다. 그래서, 당신이 입력 라인을 끝내기 위해서 두 개의 문자들을 정할 수 있지만, 그들 중 하나를 위해서 EOL 문자를 설정하고 다른 것을 위해서 EOL2 문자를 설정하라.

매크로 : int VWERASE

이것은 특별 제어문자 배열에 있는 WERASE 문자를 위한 첨자이다. termios. c_cc[VWERSE] 는 문자 그 자체를 저장한다. WERASE 문자는 오직 정규입력 모드에서만 동작한다. 그것은 이전 입력의 전체 단어를 지운다.

매크로 : int VREPRINT

이것은 특별 제어문자 배열에 있는 REPRINT문자를 위한 첨자이다. termios. c_cc[BREPRINT]는 문자 그 자체를 저장한다. REPRINT 문자는 오직 정규입력 모드에서만 인식된다. 그것은 현재의 입력라인을 다시 프린트한다.

매크로 : int VLNEXT

이것은 특별 제어문자 배열에 있는 LNEXT 문자를 위한 첨자이다. termios. c_cc[VLNEXT]는 문자 그 자체를 저장한다. LNEXT 문자는 오직 IEXTEN이 설정되었을 때만 인식된다. 사용자가 입력한 다음문자의 편집을 불가능하게 한다. 이것은 Emacs에서 C-q 와 유사하다. "LNEXT"는 "literal next"를 나타낸다. LNEXT문자는 보통 C-v 이다.

12. 4. 9. 3 신호를 발생시키는 문자들

이들 특별문자들은 정규모드나 비정규모드를 상관하지 않고 동작하지만, ISIG 플래그가 설정되었을 때만 동작한다. ( 12. 4. 7절 [Local Modes] 참조. )

매크로 : int VINTR

이것은 특별 제어문자 배열에 있는 INTR 문자를 위한 첨자이다. termios. c_cc[VINTR] 은 문자 그 자체를 저장한다. INTR(interrupt)문자는 터미널과 연관된 전면 작업에 있는 모든 프로세스를 위해 SIGINT 신호를 발생시킨다. INTR 문자 그 자체는 버려진다. 21장 [Signal Handling] 를 참조로, 신호에 대한 정보를 보아라. 특별히, INTR 문자는 C-c 이다.

매크로 : int VQUIT

이것은 특별 제어문자 배열에 있는 QUIT문자를 위한 첨자이다. termios. c_cc[VQUIT] 는 문자 그 자체를 저장한다. QUIT 문자는 터미널과 연관된 전면작업에 있는 모든 프로세스를 위한 SIGQUIT 신호를 발생시킨다. QUIT 문자 그 자체는 버려진다. 21장[Signal Handling] 에서 신호에 대한 더 많은 정보를 참조하라. 특별히, QUIT 문자는 C-\이다.

매크로 : int VSUSP

이것은 특별 제어문자 배열에 있는 SUSP 문자를 위한 첨자이다. termios. c_cc[VSUSP]는 문자 그 자체를 저장한다. SUSP(suspend) 문자는 작업제어를 지원하는 동작에서만 인식된다( 24장 [Job Control] 참조). 그것은 터미널과 연관된 전면 작업에 있는 모든 프로세스들에게 보내기 위한 SIGTSTP 신호를 발생시킨다. SUSP 문자 그 자체는 버려진다. 신호에 대한 자세한 정보는 21장 [Signal Handling] 를 참조하라. 몇몇 응용프로그램에서는 SUSP 문자에 대한 해석을 불가능하게 한다. 만일 당신의 프로그램이 그렇게 한다면, 사용자가 그 작업을 멈출 수 있게 하기 위한 다른 메커니즘을 제공해야 할 것이다. 사용자가 이 메커니즘을 불렀을 때, 프로그램은 단지 그 프로세스 자신이 아닌, 프로세스의 프로세스그룹에게 SIGTSTP 신호를 보낸다. 21. 6. 2절 [Signaling Another Process] 참조.

매크로 : int VDSUSP

이것은 특별 제어문자 배열에 있는 DSUSP 문자를 위한 첨자이다. termios. c_cc[VDSUSP] 는 문자 그 자체를 저장한다. DSUSP(suspend) 문자는 작업 제어를 지원한 는 동작에서만 인식된다(24장 [Job Control] 참조). 그것은 SUSP 문자처럼 SIGTSTP 신호를 보내지만, 프로그램이 입력으로 그것을 읽으려 시도하는, 잘못된 행동을 취할 때 발생한다. 작업제어를 지원하는 모든 시스템에서 DSUSP가 지원되는 것이 아니라 오직 BSD 시스템에서만 지원된다. 신호에 대한 자세한 정보는 21장 [Signal Handling] 를 참조하라. 특별히, DSUSP 문자는 C-y 이다.

12. 4. 9. 4 흐름 제어를 위한 특별 문자들

이들 특별 문자들은 정규입력 모드나 비정규입력모드에 상관없이 동작되지만, 그들의 사용은 IXON과 IXOFF 플래그에 의해 제어된다. ( 12. 4. 4절 [Input Modes] 참조. )

매크로 : int VSTART

이것은 특별 제어문자 배열에서 START 문자를 위한 첨자이다. termios. c_cc[VSTART] 는 문자 그 자체를 저장한다. START 문자는 IXON 과 IXOFF 입력 모드를 지원하기 위해서 사용된다. 만일 IXON이 설정되고, START 문자를 받으면 보류된 출력을 다시 시작한다. 이때 START 문자는 버려진다. 만일 IXOFF 가 설정되면, 시스템은 터미널에 START 문자를 전송할 것이다. START 문자의 보통의 값은 C-q 이다. 당신이 무엇으로 정하든 지에 상관없이 하드웨어에 C-q로 정해져 있다면 당신은 이 값을 변경할 수 없을 것이다.

매크로 : int VSTOP

이것은 특별 제어문자 배열에 있는 STOP 문자를 위한 첨자이다. termios. c_cc[VSTOP] 는 문자 그 자체를 저장한다. STOP 문자는 IXON 과 IXOFF 입력 모드를 지워하기 위해 사용된다. 만일 IXON이 설정되고, STOP 문자를 받으면 출력을 보류시킨다; 이때 STOP 문자 자체는 버려진다. 만일 IXOFF 가 설정되면, 시스템은 입력큐에서 오버플로우가 발생하는 것을 방지하기 위해 터미널에 STOP 문자를 전송할 것이다. STOP를 위해서 보통 사용되는 값은 C-s 이다. 당신은 당신이 무엇으로 이 값을 변경하든 지에 상관없이 하드웨어에서 C-s로 고정되어 있다면, 이 값을 변경할 수 없다.

12. 4. 9. 5 다른 특별 문자들

이곳에서는 BSD 시스템에서 의미가 있는 두 개의 부가적 특별 문자들을 설명한다.

매크로 : int VDISCARD

이것은 특별 제어문자 배열에 있는 DISCARD 문자를 위한 첨자이다. termios. c_cc[VDISCARD] 는 문자 그 자체를 저장한다. DISCART문자는 IEXTEN이 설정되었을 때만 인식된다. 그 영향은 discard-output 플래그를 토글하게된다. 이 플래그가 설정되면, 모든 프로그램 출력은 버려진다. 플래그 설정은 또한 출력 버퍼안에 현재 존재하는 모든 출력을 버린다.

매크로 : int VSTATUS

이것은 특별 제어문자 배열에 있는 STATUS 문자를 위한 첨자이다. termios. c_cc[VSTATUS] 는 문자 그 자체를 저장한다. STATUS 문자의 영향은 현재 프로세스가 어떻게 동작하고 있는지에 대한 상황 메시지를 출력하기 위한 것이다. STATUS 문자는 오직 정규 입력 모드에서만 인식된다.

12. 4. 10 비정규입력

비정규입력 모드에서, ERASE 와 KILL 과 같은 특별 편집 문자들을 무시된다. 입력 편집을 위해 사용자에게 부여된 시스템 도구들이 비정규입력 모드에서는 불가능하게 된다. 그래서 모든 입력 문자들( 만약 그들이 신호나 흐름제어 목적을 가진 것이 아니라면)은 응용 프로그램에 정확히 친 대로(typed) 인식된다. 응용 프로그램에서 사용자에게 입력을 편집하는 방법을 제공하는 것이 좋다.

비정규입력 모드는 유용한 입력이 있을 때까지 기다릴 것인지와 얼마나 기다릴 것인지를 제어하기 위한 MIN 과 TIME 이라 불리는 파라미터가 있다. 당신은 유용한 입력이 있을 때, 또는 없을 때, 즉시 반환되도록 해서 무작정 기다리는 것을 피하는데도 그들을 사용할 수 있다.

MIN과 TIME은 구조체 struct termios의 멤버인 c_cc 배열의 요소로서 저장되어진다. 이 배열의 각 요소는 특별한 규칙을 가지고 있고, 각 요소는 그 요소의 인덱스로 대표되는 심볼 상수를 가지고 있다. VMIN 과 VMAX는 MIN과 TIME 슬롯의 배열의 인덱스들을 위한 이름들이다.

매크로 : int VMIN

이것은 c_cc 배열안의 MIN 슬롯을 위한 첨자이다. 그래서 termios. c_cc[VMIN]은 그 값 자체이다. MIN 슬롯은 비정규입력 모드에서만 유용하다; read가 반환되기 전에 입력큐에서 받아들여야만 하는 유용한 바이트의 최소 개수를 정하는데 사용된다.

매크로 : int VTIME

이것은 c_cc 배열에 있는 TIME 슬롯을 위한 첨자이다. 그래서, termios. c_cc[VTIME] 는 그 값 자체이다. TIME 슬롯은 비정규입력 모드에서만 유용하다; 그것은 0. 1초의 단위로 반환하기 전에 입력을 얼마나 기다릴 것인지를 정한다. MIN 과 TIME값은 read가 반환할 때를 위한 표준을 정하는데 영향을 미친다; 그들의 정확한 의미는 그들이 가진 값에 따른다(0이냐 0이 아니냐. . ) 4개의 가능한 경우가 있다.

MIN 과 TIME 둘다 영일 때. . .

이 경우, read는 큐에 요청된 개수를 넘어서는, 유용한 입력이 있을 때 즉시 반환한다. 만일 아무런 입력이 없어도 즉시 반환하는데, 이때 read는 0의 값을 반환한다.

MIN 은 0이지만 TIME은 0이 아닐 때.

이 경우, read는 유용한 입력이 될 때까지 TIME 시간동안 기다린다; 단 한 개의 바이트도 요청한 read를 만족시키기에 충분하고, read는 반환한다. 그것이 반환할 때 요청된 개수의 유용한 문자를 반환한다. 만일 시간이 다할 때까지 유용한 입력이 없으면 read는 0을 반환한다.

TIME 이 0이지만 MIN은 0이 아닐 때.

이 경우, read는 적어도 큐에 유용한 입력이 MIN 바이트가 될 때까지 기다린다. 그 시간동안, read는 요청된 개수의 유용한 문자들을 반환한다. read는 만일 큐에 MIN보다 더 많은 문자가 발생했다면 MIN 문자보다 더 많은 문자를 반환할 수 있다.

TIME 과 MIN 둘다 영이 아닐 때.

이 경우, TIME은 만일 입력이 도착하면, 그 첫 번째 도착한 입력부터 얼마동안 기다릴 것인가를 정한다. read는 MIN 바이트의 입력이 도착하거나, 또는 TIME이 더 이상 아무런 입력 없이 경과되었을 때까지 기다림을 유지한다. read는 TIME이 첫 번째 입력이 도착하기 전에 경과되면 입력 없이 반환할 수 있다. read는 MIN 보다 더 많은 입력이 큐안에 발생하면 그것을 반환할 수 있다 만일 MIN이 50이고 당신이 단지 10 바이트만 읽기를 요청하면 무슨 일이 발생할 것인가?
보통, read는 버퍼에 50바이트가 찰 때까지 기다린다(또는 더 일반적으로는, 위에 설명된 기다림의 상황이 만족된다. ), 그리고 나서, 그들 중 10개를 읽고, 나머지 버퍼에 있는 40개는 read의 연속적 호출에서 사용하기 위해서 운영체제 안에 남겨둔다.
  이식성 노트: 어떤 시스템에서, MIN 과 TIME 슬롯은 실제로 EOF 와 EOL 슬롯과 같다. MIN 과 TIME은 비정규 입력에서만 사용되고, EOF와 EOL은 정규입력에서만 사용되기 때문에 심각한 문제는 없지만, 완전한 것은 아니다. GNU 라이브러리는 이렇게 사용하기 위해서 슬롯들을 분리해서 할당한다.


12. 5 라인 제어 함수들

이들 함수들은 터미널 디바이스 상에서 갖가지 제어 동작을 수행한다. 터미널 억세스에 관하여, 그들은 출력을 하는 것처럼 취급된다: 만일 그들 함수중 어떤 것이 터미널을 제어중인 배경 프로세스에서 사용된다면, 보통, 프로세스 그룹의 모든 프로세스들은 SIGTTOU 신호를 받는다. 예외적으로 호출한 프로세스 자신이 무시되거나, SIGTTOU 신호에 의해 블록되어 있다면, 그 경우 명령은 수행되고 아무런 신호를 받지 않는다. 24장 [JobControl] 참조.

함수 : int tcsendbreak( int filedes, int duration)

이 함수는 파일 기술자 filedes와 연관된 터미널에 0 비트의 스트림을 전송함으로써 멈춤(break) 상황을 발생시킨다. 멈춤의 존속시간은 duration인수에 의해 제어된다. 만일 duration 이 0이면, 존속시간은 0. 25 와 0. 5초 사이이다. 0이 아닌 값의 의미는 운영체제에 의존된다. 이 함수는 만일 그 터미널이 비동기적 직렬 데이터 포트가 아니면 아무 일도 하지 않는다.
반환 값은 보통 0이고, 에러가 발생하면 -1을 반환한다. 다음의 errno는 이 함수를 위해 정의된 에러 상황이다.

EBADF : filedes 가 유용한 파일 기술자가 아니다.

ENOTTY : filedes 가 터미널 디바이스와 연관이 없다.

함수 : int tcdrain (int filedes)

tcdrain 함수는 큐에 저장된 모든 출력이 터미널 filedes에 모두 전송되어 질 때까지 기다린다.
반환 값은 보통 0이고, 에러가 발생하면 -1을 반환한다. 다음의 errno는 이 함수를 위해 정의된 에러상황이다.

EBADF : filedes 가 유용한 파일 기술자가 아니다.

ENOTTY : filedes는 터미널 디바이스와 연관이 없다.

EINTR : 그 명령은 신호에 의해 인터럽트 되어졌다.

21. 5절 [Interrupter Primitives] 참조.

함수 : int tcflush (int filedes, int queue)

tcflush 함수는 터미널 파일 filedes와 연관된 입력 그리고/또는 출력큐를 소거하기 위해 사용된다. queue 인수는 소거할 큐를 정하고, 다음 값들중 하나를 사용할 수 있다.

TCIFLUSH : 받았지만, 아직 읽지않은 입력 데이터를 소거하라

TCOFLUSH  : 쓰여졌지만, 아직 전송되지 않은 출력데이터를 소거하라.

TCIOFLUSH

큐에 저장된 입력과 출력을 모두 소거하라. 반환 값은 보통 0이고 에러가 발생하면 -1을 반환한다. 다음의 errno는 이 함수를 위해 정의된 에러상황이다.

EBADF : filedes가 유용한 파일 기술자가 아니다.

ENOTTY : filedes는 터미널 디바이스와 연관이 없다.

EINVAL

적당하지 못한 값이 queue인수로써 공급되었다. 이 함수의 이름이 tcflush라고 지어진 것은 유감스러운데, 왜냐하면 한정 "flush"는 보통 모든 출력이 전송되고, 다른 명령을 사용하기 전에 혼동될 입력이나 출력을 버리는데 사용된다. 유감스럽게도, tcflush는 POSIX로부터 유래됐고, 우리는 그것을 변경할 수 없다.

함수 : int tcflow (int filedes, int action)

tcflow 함수는 filedes로 정해진 터미널 파일에서 XON/XOFF 흐름제어에 해당하는 명령을 수행하기 위해 사용된다. action인수는 무슨 명령을 수행할 것인지를 정하고, 다음 값들중 하나를 가질 수 있다.

TCOOFF 출력의 전송을 중단하라.

TCOON 출력의 전송을 다시 시작하라.

TCIOFF STOP 문자를 전송하라.

TCION START 문자를 전송하라.

STOP 와 START에 대한 자세한 정보를 12. 4. 9절 [Special Characters] 를 참조하라.

반환 값은 보통 0이고 에러가 발생하면 -1이 반환된다. 다음의 errno는 이 함수를 위해 정의된 에러상황이다.

EBADF filedes 가 유용한 파일 기술자가 아니다.

ENOTTY filedes 가 터미널 디바이스와 연관이 없다.

EINVAL 적당하지 못한 값이 action인수로 주어졌다.


12. 6 비정규 모드의 예

이곳의 예는 비정규입력모드에서 반향 없이 단일 문자들을 읽기 위해서 터미널 디바이스를 어떻게 맞출 것인지를 보여주고 있다.

#include <unistd. h>
#include <stdio. h>
#include <stdlib. h>
#include <termios. h>
/* 원래의 터미널 속성들을 기억하기 위해서 이 변수를 사용하라 */
struct termios saved_attributes;
void
reset_input_mode (void)
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void
set_input_mode (void)
{
struct termios tattr;
char *name;
/* stdin이 터미널인지 확인하라 */
if (!isatty (STDIN_FILENO)) {
fprintf (stderr, "Not a terminal. \n");
exit (EXIT_FAILURE);
}
/* 그들을 나중에 다시 저장 할 수 있도록 터미널 속성들을 저장하라. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* 재미있는(?) 터미널 모드를 설정하라. */
tcgetattr (STDIN_FILENO, &tattr);
tattr. c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr. c_cc[VMIN] = 1;
tattr. c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
int
main (void)
{
char c;
set_input_mode ();
while (1) {
read (STDIN_FILENO, &c, 1);
if (c == '\004') /* C-d */
break;
else
putchar (c);
}
return EXIT_SUCCESS;
}

이 프로그램은 신호와 함께 빠져나가거나 종료되기 전에 원래의 터미널 모드를 재 저장하도록 주의를 기울여야 한다. 이것을 확실하게 하기 위해서는 atexit 함수를( 22. 3. 3절 [Cleanups on Exit] 참조. ) 사용하라. 쉘은 한 프로세스가 멈추거나 지속될 때 터미널 모드의 재설정에 조심한다. 24장 [Job Control] 참조. 그러나 어떤 쉘들은 실제로 이것을 하지 않는다, 그래서 당신은 터미널 모드를 재설정하는 작업 제어 신호를 위한 핸들러를 만들어야 할 것이다. 위의 예는 그렇게 한다.

SO파일 만들어볼까나(공유라이브러리)

참조http://kldp.org/HOWTO/html/Program-Library-HOWTO/shared-libraries.html

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


아래는 내가 쓴 글...홀홀홀
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
소스 파일을 만든다.

g++ -fPIC -c libfile.cpp // 또는 libfile1.cpp libfile2.cpp

g++ -shared -Wl,-soname,libfile.so.0 -o libfile.so.0.0.0 libfile.o // 또는

libfile1.o libfile2.o

그럼 libfile.so.0.0.0이 생성된다.

이것을 /usr/lib에 복사하고,

  ln -s /usr/lib/libfile.so.0.0.0 /usr/lib/libfile.so

  ln -s /usr/lib/libfile.so.0.0.0 /usr/lib/libfile.so.0

심볼릭 링크파일을 생성한다.

만약 /usr/lib에 만들지 않고, 자신의 프로그램이 설치된 디렉토리에 파일을 두고

싶다면

/etc/ld.so.conf파일안의 내용에 다음을 추가한다.

  /프로그램이 설치된 디렉토리명/lib가 저장된 디렉토리명
 
  ex) /usr/local/myProjects/lib

ld.so.conf파일의 내용을 적용시키기 위해서 다음 명령어를 실행시킨다.

  ldconfig<enter>   --> 이것은 /etc/ld.so.cache파일을 갱신하는 것이다.

이렇게 만들어진 so(공유 라이브러리)를 프로그램에서 사용하기 위해서 다음 옵션

으로 컴파일해라.

  g++ -o test test.cpp -L./ -lfile 

  (-L은 so 파일이 있는 디렉토리, -l은 lib파일이름인데 lib뒤에 나오는 이름)

   ex) libfile -> -lfile,   libMy -> -lMy
/*______________________________________________________________________________________________*/

prev 1 2 next