실리콘 래버러토리스
VoIP용 통신사업자급 듀얼ProSLIC 출시
실리콘 래버러토리스(www.silabs.com)가 듀얼채널 SLIC 및 코덱을 싱글칩에 통합한 Si3226을 출시, 자사의 듀얼 ProSLICSLIC (pro-grammable subscriber line interface) ProSLIC 제품군을 확대한다고 밝혔다.
Si3226 ProSLIC 제품군은 실리콘랩의 특허 디지털 SLIC 아키텍처가 적용되어 전세계에서 사용할 수 있는 통신사업자급 SLIC로, 광범위한 원격 진단 기능과 저전력 소모, 그리고 업계 동종 제품 중 가장 깨끗한 음성재생 기능을 제공한다. Si3226 제품군은 음성 기능의 케이블, DSL 모뎀, FTTP 광네트워킹 장비 및 PBX와 같이 엔터프라이즈 및 컨수머용 장비에 최적화 되어 있다.
아날로그 기반의 SLIC와는 달리, Si3226은 프로그래밍 수준이 높은 디지털 SLIC 아키텍처를 기반으로 설계되어 다양한 고객들의 요구사항을 만족시킬 수 있는 유연성을 제공한다. 또한 단일 하드웨어 설계 및 BOM(bill-of-materials)을 사용함으로써 전세계적으로 사용할 수 있다. Si3226은 멀티모드 링(ring) 제너레이터와 프로그래머블 배터리 트랙킹(tracking) DC/DC 컨트롤러를 결합해 모든 라인 상태에서 저전력 소모 동작을 유지한다.

프로세스마다 인터럽트 처리 방식이 다르기 때문에 리눅스 커널에서는 동일하게 처리하기 위해 IRQ 인터럽트는 모두 do_IRQ() 함수를 호출하여 처리하도록 되어 있다. 이 do_IRQ() 함수는 오직 IRQ 처리만 담당하며, irq_desc라는 전역 변수에 등록된 인터럽트 서비스 함수를 호출하는 구조로 되어 있다.디바이스 드라이버나 커널에서 IRQ 인터럽트 처리가 필요한 경우에는 처리하고자 하는 IRQ 번호에 해당하는 인터럽트 서비스 함수를 이 irq_desc 전역 변수에 등록하면 된다.

IRQ 인터럽트 서비스를 처리하기 위해서는 request_irq() 함수를 이용하여 처리하고자 하는 IRQ 번호와 서비스 함수 주소를 등록한다. 인터럽트가 발생하면 커널은 아키텍처마다 고유의 IRQ 검출 루틴을 이용하여 발생된 인터럽트의 IRQ 번호를 획득하여 do_IRQ() 함수를 호출한다.

더이상의 인터럽트 처리가 필요없는 경우에는 하드웨어에서 인터럽트가 더이상 발생하지 않도록 처리한 후 free_irq() 함수를 이용하여 irq_desc 전역 변수에 등록된 인터럽트 서비스 함수를 제거한다.

 

. 커널 2.4

. #include <linux/sched.h>

. void int_handler(int irq, void* dev_id, struct pt_regs* regs)

{

}

. int request_irq(unsigned int irq, void (*handler)(int, void*, struct pt_regs*), unsigned long frags, const char* device, void* dev_id);

. void free_irq(unsigned int irq, void* dev_id);

 

. 커널 2.6

. #include <linux/interrupt.h>

. irqreturn_t int_hander(int irq, void* dev_id, struct pt_regs* regs)

{

    return IRQ_HANDLED;

}

. int request_irq(unsigned int irq, irqreturn_t(*handler)(int, void* struct pt_regs*), unsigned long frags const char* device, void* dev_id);

 

. frags

    . SA_INTERRUPT             다른 인터럽트를 허가하지 않느다

    . SA_SHIRQ                    동일한 인터럽트 번호를 공유한다

    . SA_SAMPLE_RANDOM  랜덤값 처리에 영향을 준다

. dev_id

    . 인터럽트 ID를 뜻하는 것으로 인터럽트 공유에 사용되거나 인터럽트 서비스 함수를 수행할 때 필요한 정보가 저장된 메모리의 선두 주소를 담고 있다.

    . 인터럽트 서비스 함수를 등록할 때 지정된 값이 그대로 전달된다.

. regs

    . 인터럽트가 발생한 당시의 레지시터 값들

 

* 인터럽트 서비스 함수 내의 메모리 할당

vmalloc() 함수나 vfree() 그리고 ioremap() 같은 함수를 인터럽트에서 사용하면 안 된다. kmalloc() 함수 역시 GFP_ATOMIC 인자를 사용한 방식만 사용해야 한다는 제약이 있다.

 

* 인터럽트 서비스 등록과 해제 시점

왜 open()함수와 close()함수에서 인터럽트 서비스 함수 등록과 해제 루틴을 넣을까?

모듈이 초기화될 때 서비스 함수가 등록되면 해당 디바이스를 사용하는 응용 프로그램이 없다 하더라도 인터럽트가 발생하면 인터럽트 서비스 함수가 항상 호출된다. 이런 호출 방식은 전체 시스템의 처리 속도를 약간 느리게 만드는 원인이 된다. 그래서 open() 함수나 close() 함수에서 인터럽트 서비스 함수의 등록과 해제가 이루어지도록 해야 한다.

PC 같은 범용 시스템에 사용되는 디바이스 드라이버를 만든다면 open() 함수와 close() 함수에서 처리해야 하고, 임베디드와 같은 특정 목적의 시스템에서는 모듈의 등록과 해제 시에 처리하는 것이 좋다.

 

* 인터럽트의 공유

인터럽트 서비스 함수를 등록할 때 같은 인터럽트 번호에 대해 다른 인터럽트 서비스 함수를 등록할 수 있도록 지원한다.

. frags : SA_SHIRQ가 포함되어야 하고

. dev_id : 동일한 인터럽트 서비스 함수를 구별하기 위해 0이 아닌 값을 사용해야 한다.

 

* 인터럽트의 금지와 해제

-. 인터럽트 서비스 함수가 동작중에 다른 인터럽트가 발생하지 못하게 막는 경우

. frags 매개변수에 SA_INTERRUPT를 포함시킨다. 인터럽트가 발생했을 때 SA_INTERRUPT 값이 포함된 인터럽트 서비스 함수는 커널이 프로세스의 인터럽트를 금지시킨 후 호출한다.

-. 일반적인 함수 수행중에 데이터 처리를 보호하기 위해 인터럽트를 강제로 막는 경우

. 특정 인터럽트 번호에 대한 금지와 해제

    . #include <asm/irq.h>

    . void disable_irq(int irq) : 인터럽트 금지

    . void enable_irq(int irq) : 인터럽트 허가

. 전체 인터럽트를 금지하고 해제

    . #include <asm/system.h>

    . 커널 2.4

        . cli(void) : 프로세서의 인터럽트 처리를 금지한다.

        . sti(void) : 프로세서의 인터럽트 처리를 허가한다.

        . save_flags(unsigned long frags) : 현재의 프로세스의 상태를 저장한다.

        . restore_flags(unsigned long frags) : 저장된 프로세스의 상태를 복구한다.

    . 커널 2.6

        . local_irq_disable(void) : 프로세서의 인터럽트 처리를 금지한다.

        . local_irq_enable(void) : 프로세서의 인터럽트 처리를 허가한다.

        . local_save_flags(unsigned long frags) : 현재의 프로세스의 상태를 저장한다.

        . local_restore_flags(unsigned long frags) : 저장된 프로세스의 상태를 복구한다.

 

        unsigned long frags;

        local_save_flags(frags);

        local_irq_disable();

        // 인터럽트 호출에서 보호하고자 하는 루틴

        local_restore_flags(frags);

        . local_irq_enable() 함수를 사용하지 않는데, 그 이유는 local_save_flags() 함수에 의해 저장된 frags 변수에는 이미 인터럽트 허가 상태가 포함되어 있기 때문이다. 그래서 굳이 local_irq_enable() 함수를 사용하지 않아도 local_restore_flags() 함수에 의해 local_irq_disable() 함수를 호출한 효과가 발생한다.

 

* 인터럽트 발생 횟수

인터럽트 발생 횟수는 커널이 알아서 계산한다. /proc/interrupts란 파일을 보면 현재 인터럽트의 발생 횟수를 볼 수 있다.

cat /proc/interrupts


출 처 http://blog.naver.com/jinynet9?Redirect=Log&logNo=40013761955

[출처] 인터럽트|작성자 jinynet9

윤 상배

dreamyun@yahoo.co.kr

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


1절. 소개

유닉스에서 사용되는 proc파일 시스템은 운영체제의 각종 정보를 커널모드가 아닌 유저모드에서 쉽게 접근할 수 있도록 만들어 줌으로 시스템 정보를 일반 프로그래머가 쉽게 접근 할 수 있도록 도와준다.

특히 리눅스에서는 프로세스 정보뿐만 아닌 다른 시스템 정보들까지 광범위 하게 제공해 준다. 이말은 proc파일시스템을 제대로 이해할 경우 리눅스 운영체제를 좀더 깊이 있게 다룰 수 있다는 말이 된다. 실제 ps와 같은 프로세스 상황감시에서 부터, CPU사용율, 인터럽트, 네트워크 패킷전송량, 적재된 모듈, IDE-SCSI와 같은 장치정보, CPU정보등의 데이터를 어렵지 않게 얻어 올 수 있다. 다른 대부분의 유닉스에서 이러한 정보를 얻어올려면 상당한 애로사항을 격게 될것이다.

이제 proc파일시스템에서 데이터를 읽어오는 것을 지나서 proc파일 시스템에 필요한 데이터를 쓰는 방법에 대해서 알아보도록 하겠다.


2절. 왜 proc파일시스템을 이용하는가

우리는 이미 일반 파일 시스템을 이용해서 필요한 데이터를 남기는 방법을 알고 있다. read(2), open(2), write(2) 이 3개의 함수만 사용할 줄 안다면, 필요한 모든 데이터를 읽고 쓰는데 별 부족함이 없다. 그렇다면 왜 굳이 proc파일시스템을 이용해야 하는지에 대해서 알아 보도록 하겠다.


2.1절. 파일 시스템 오버헤드를 줄일 수 있다.

일반적으로 사용되는 파일 시스템은 상당한 오버헤드를 가지고 있다. 각 파일의 inode와 superblocks와 같은 객체를 관리해야 하며 이러한 정보를 필요할때 마다 운영체제에 요청해야 한다. 이들 파일 시스템의 데이터들은 서로 어긋날수도 있으며, 단현화 현상등이 발생할 수도 있다. 운영체제는 이러한 모든 것을 관리해 주어야 하며, 당연히 상당한 오버헤드가 발생하게 된다.

proc파일 시스템은 이러한 일반 파일시스템의 문제점을 없애기 위해서 리눅스 커널에서 직접 파일시스템을 관리하는 방법을 채택하고 있다.

지금 여러분의 리눅스 시스템에서 mount명령을 내리면 다음과 같이 proc 파일 시스템이 자동으로 마운트 되어 있는 것을 확인할 수 있을 것이다.

# mount
/dev/hda7 on / type ext3 (rw)
none on /proc type proc (rw)
/dev/hda5 on /usr type ext3 (rw)
...
			
여러분이 최초에 리눅스 운영체제를 설치할 때 proc파일 시스템을 위해서 별도로 파티션작업을 한적이 없을 것이므로 mount정보에 표시되는게 이상할 수도 있을 것이다. 이유는 앞에서 말했듯이 proc파일 시스템은 리눅스 커널에서 직접 관리하는 것으로 운영체제가 부팅 되었을 때 생성되는 파일 시스템이기 때문이다. mount정보를 보면 알겠지만 어떤 장치에도 마운트되어 있지 않음을 확인할 수 있다. proc파일 시스템은 커널메모리에서 돌아가는 일종의 가상 파일 시스템이다.

메모리에서 그것도 커널이 직접관리를 하게 되니.. 당연히 빠를 수 밖에 없다.


2.2절. 물리적인 파일시스템 장치를 필요로 하지않는다

/proc는 커널메모리에서 유지하는 파일 시스템이다. 때문에 별도의 장치(하드디스크 같은)을 필요로 하지 않는다. 이러한 특징은 임베디드시스템을 설계하고자 할때 중요한 요소가 된다.


2.3절. 최적화된 파일작업 수행

일반적인 파일 시스템 계층은 은 프로그래머를 위해서 POSIX 형식의 인터페이스를 제공한다. open, read, write, close등이 이것이다. 데이터 블럭들은 용이한 확장을 위해서 추상화 되어 있으며 상속가능한 형태로 구성된다.

이러한 일반 파일 시스템은 대용량의 데이터를 다루어야 하는 경우 매우 유용하지만, 고정적이고 처리해야할 데이터의 양이 적은 분야에는 오히려 비효율적이다. proc파일 시스템에서 다루어야 할 정보는 대부분 정해져 있으며, 데이터의 양도 그리 많지 않다. 고로 일반 파일시스템에서 제공하는 인터페이스를 사용하지 않고 필요한 작업에 최적화된 인터페이스를 사용할 수 있다.


3절. proc 파일시스템을 어디에 사용할 수 있을까

2절에서 proc 파일시스템을 사용했을 때 얻을 수 있는 잇점에 대해서 알아보았다. 그럼 어느 용도에 유용하게 사용할 수 있을지에 대해서 알아보자.


3.1절. 커널 모듈 프로그래밍

proc파일 시스템(이하 proc) 자체가 커널과 밀접하게 연관있는 이유로 일반 애플리케이션에서 proc를 사용하는 일은 드물다. 위에서 설명 했듯이 커널메모리에서 proc를 유지하게 되므로 많은 양의 데이터를 처리하는 애플리케이션의 용도와는 맞지 않다는 점도 있다.

그런점에서 proc는 커널모듈과 같이 커널과 밀접하게 관계있는 프로그램에서 유용하게 사용할 수 있다. 커널 모듈 프로그램은 주로 장치를 올리기 위한 용도로 사용되는데, 커널 레벨에서 작동하다 보니 모듈의 작동상황이나 성능등을 알아오기가 그리 쉽지 않다. 그렇다고 해서 일반 파일 시스템을 IPC를 사용하는 것 역시 그리 좋은 생각은 아니다. 이럴 때 proc를 이용하면 문제를 깔끔하게 해결 할 수 있다.


3.2절. 임베디드 프로그래밍

임베디드 시스템은 파일 시스템을 가지지 않는 경우가 많거나 가지고 있다고 하더라도 매우 제한적인 경우가 많다. 이럴 때 proc를 이용해서 관리자 환경이라든지 데이터 입출력 환경을 만들 수 있다.

물론 이경우는 리눅스커널을 기반의 임베디드 환경에 해당된다.


4절. proc 프로그래밍

이번장에서는 커널에서 제공하는 proc API들에 대해서 살펴볼 것이다. 다루어 지는 내용들은 커널 2.4.x를 기준으로 하고 있다.


4.1절. proc 구조체 및 API

4.1.1절. proc_dir_entry 구조체

proc에 있어서 가장 중요한 구조체로써 다음과 같이 정의되어 있다.

struct proc_dir_entry {
    unsigned short low_ino;
    unsigned short namelen;
    const char *name;
    mode_t mode;
    nlink_t nlink;
    uid_t uid;
    gid_t gid;
    unsigned long size;
    struct inode_operations * proc_iops;
    struct file_operations * proc_fops;
    get_info_t *get_info;
    struct module *owner;
    struct proc_dir_entry *next, *parent, *subdir;
    void *data;
    read_proc_t *read_proc;
    write_proc_t *write_proc;
    atomic_t count;     /* use count */
    int deleted;        /* delete flag */
    kdev_t  rdev;
};
				
name은 proc파일의 이름이다. mode는 proc파일의 권한으로 일반파일에 사용되는 권한과 동일하게 사용할 수 있다. mode에 대한 자세한 내용은 stat(2)의 man페이지를 참고하기 바란다.

struct proc_dir_entry *next ...는 proc파일이 위치하는 디렉토리로 일반 파일에서의 디렉토리 권한과 동일하게 사용되며, 링크드 리스트로 관리된다.

data proc에서 읽은 데이터를 리턴하기 위해서 사용된다.

read_proc, write_proc 유저영역의 프로세스는 직접 커널영역에 데이터를 읽거나 쓸수 없다. 때문에 모듈 프로그램등이 중간에서 커널과 유저영역 사이의 데이터전달을 해주어야 한다. 이러한 데이터 전달은 callback함수를 통해서 이루어진다. read_proc는 커널로 부터 읽은 데이터를 유저영역 프로세스로 되돌려주기 위해서 write_proc는 유저역역 프로세스에서 쓴데이터를 커널메모리 영역으로 복사하기 위해서 사용한다.


4.2절. proc API

proc는 사용하기 간단한 몇개의 API만을 제공하는데, 이들 API는 커널의 메이저 버젼에 따라서 차이가 있을 수 있다. 만약 여러분이 2.4.x외의 다른 커널 버젼을 사용하길 원한다면 해당 커널버젼의 커널 문서를 참고해야 할 것이다. 그렇다고 해서 이 문서가 전혀 필요 없지는 않을 것이다. 대부분의 경우 커널의 메이저 버젼이 업그레이드 된다고 하더라도 함수 API가 아주 크게 변하는 경우는 없기 때문이다. 이 문서를 익혀 놓는다면 다른 커널 버젼에도 쉽게 적응할 수 있을 것이다.


4.2.1절. create_proc_entry

struct proc_dir_entry *create_proc_entry
(
    const char *name,
    mode_t     mode,
    struct proc_dir_entry *parent);
);
				
이 함수는 첫번째 인자인 name을 이름으로 하는 proc파일을 생성한다. mode는 생성 될때의 파일 모드로 open(2)에 사용되는 것과 동일하게 사용된다. mode에 대한 자세한 내용은 man페이지를 참고하기 바란다.

마지막 인자인 parent는 name로 만들어진 proc파일이 위치할 디렉토리다. proc파일은 루트디렉토리가 "/"아닌 "/proc"에서 부터 시작하게 된다. 만약 NULL이라면 /proc 디렉토리 밑에 위치하게 된다.


4.2.2절. create_proc_read_entry

static inline
struct proc_dir_entry *create_proc_read_entry
(
    const char *name,
    mode_t mode,
    struct proc_dir_entry *base,
    read_proc_t * read_proc, 
    void *data
);
				
이 함수는 create_proc_entry의 포장(wrapper)함수다.
static inline
struct proc_dir_entry *create_proc_read_entry
(
    const char     *name,
    mode_t         mode,
    struct proc_dir_entry *base,
    read_proc_t  *read_proc, 
    void         *data
)
{
    struct proc_dir_entry *res = create_proc_entry(name, mode, base);
    if (res)
    {
        res->read_proc = read_proc;
        res->data=data;
    }
   return res;
}
				
create_proc_read_entry는 쉽게 읽기용 proc파일을 만들 수 있도록 도와준다.


4.2.3절. create_proc_info_entry

static inline 
proc_dir_entry *create_proc_info_entry
(
    const char *name,
    mode_t     mode,
    get_info_t *get_info
);
				
마찬가지로 create_proc_entry의 포장함수이다.
ststic inline
struct proc_dir_entry *create_proc_info_entry
(
    const char            *name,
    mode_t                mode,
    struct proc_dir_entry *base,
    get_info_t            *get_info
)
{
    struct proc_dir_entry *res = create_proc_entry (name, mode, base);
    if (res) res->get_info = get_info;
    return res;
}
				


4.2.4절. proc_mkdir

proc파일 시스템에 디렉토리를 생성한다. 만들어 지는 디렉토리는 proc파일 시스템의 최상위 디렉토리(/proc)를 기준으로 한다.

extern struct proc_dir_entry *proc_mkdir
(
    const char *dir_name,
    struct proc_dir_entry *parent
) 
				


4.2.5절. proc_symlink

심볼릭 링크를 만들기 위해서 사용된다. 단지 실제 사용자(real user)만이 사용가능하다.

extern struct proc_dir_entry *proc_symlink
(
    const char    *file_name,
    struct proc_dir_entry *parent,
    const char    *symlink_name  
);
				


4.2.6절. remove_proc_entry

커널 모듈 프로그램밍시 celanup_module()함수에서 proc 파일을 지워주지 않을 경우 시스템에 좋지 않은 영향을 미칠 수 있다. 일단 proc를 생성했다면 프로그램 종료시 반드시 이 함수를 호출해서 proc파일을 지워주도록 하자.

extern void *remove_proc_entry
(
    const char            *name,
    struct proc_dir_entry parent
);
				


4.3절. 기타 포장 함수들

모듈 프로그래밍시 proc파일 시스템은 매우 자주 이용된다. 그러므로 이왕이면 쓰기편한 함수들이 준비되면 좋을 것이다. 여기에서 설명하는 함수들은 기존의 proc함수들을 사용하기 편하도록 포장한 함수들이다.


4.3.1절. proc_net_create

이 함수는 create_proc_info_entry의 /proc/net정보에 대한 포장함수이다. 네트워크 서브시스템에 대해서 쉽게 접근하도록 도와준다.

static inline
struct proc_dir_entry *proc_net_create
(
    const char *name,
    mode_t     mode,
    get_info_t *get_info
)
{
    return create_proc_info_entry(name, mode, proc_net, get_info);
}
				


4.3.2절. proc_net_remove

네트워크 서브 시스템에 대한 remove_proc_entry의 포장함수이다.

static inline void proc_net_remove(const char *name)
{
    remove_proc_entry(name, proc_net)
}
				


4.4절. 일반 유저와의 데이터 교환

일반 파일에서 유저와의 데이터 교환은 매우 단순하며, 별로 신경쓸 필요도 없다. 프로그램이 파일에 쓴 내용 그대로를 유저가 보며, 유저가 파일에 쓴 내용그대로를 다시 프로그램이 읽어들인다.

그러나 proc파일 시스템에서의 데이터는 실제 파일에 저장되는 것과는 달리 커널메모리에 저장된다. 알다 시피 커널메모리는 유저레벨 에서 직접 접근할 수 없다. 유저가 cat(혹은 read함수)등을 통해서 파일의 내용을 읽을려고 하면 커널에서 데이터를 유저에게 일정한 포맷으로 뿌려주게 된다. 마찬가지로 유저가 어떤 내용을 proc파일에 쓰게되면 데이터를 받아들인후 가공해서 커널메모리에 적재하게 된다.

이를 위해서 커널과 일반유저 사이에 데이터를 서로에게 전달해 주는 어떤 함수가 필요하고 이 함수가 다룰 수 있는 표준적인 자료구조가 있어야 한다. 유저가 데이터를 읽고 쓰기 위해서는 읽기와 쓰기를 위한 callback함수를 등록시켜서 사용 해야한다.

struct proc_dir_entry *entry;

entry->read_proc = read_proc_foo;
entry->write_proc = write_proc_foo;
			
위에서 처럼 pric_dir_entry에 읽기/쓰기를 위한 콜백함수를 등록하면 된다.


4.4.1절. 데이터 읽기

콜백함수로 등록되는 읽기함수는 유저영역 프로세스의 요청을 받으면 커널로 부터 데이터를 읽어들여서 알맞은 포맷으로 변경한다음 유저영역 프로세스로 되돌려준다. 읽기함수는 다음과 같은 모습을 가진다.

int read_func(char* page, char** start, off_t off, int count, int* eof, void* data);
				
읽기함수는 일어들인 정보를 page에 쓰게 된다.


4.4.2절. 데이터 쓰기

쓰기 콜백함수는 유저영역 프로세스로 부터 데이터를 받은다음 커널이 읽기에 적당한 형식으로 변경후 커널에 넘겨준다. 쓰기함수는 다음과 같은 모습을 가진다.

int write_func(struct file* file, const char *buffer, unsigned long count,
			void *data);
				
쓰기함수는 buffer로 부터 유저가 쓴 데이터를 읽어들인다. buffer는 유저 데이터 이므로 copy_from_user을 이용해서 커널메모리영역으로 데이터를 복사한다.


4.5절. Proc파일 시스템을 이용한 예제

그럼 간단한 예제를 만들어 보도록 하겠다. 예제는 일반 애플리케이션이 아닌 커널 모듈 프로그램이다. 커널 모듈프로그래밍에 대한 내용은커널 모듈 프로그래밍을 참고하기 바란다.

프로그램의 이름은 my_proc.c로 하겠다. 이 모듈은 proc파일 시스템에 myproc라는 디렉토리를 만들고 이 모듈 아래에 foo와 jiffies라는 파일을 만든다. 만약 유저가 cat등을 통해서 이들 파일을 열면 모듈은 모듈 정보를 적당한 포맷으로 만들어서 사용자에게 보여주게 된다. foo파일의 경우에는 파일에 내용을 쓸수도 있도록 되어 있어서 사용자가 내용을 바꾸면 이 내용은 모듈에서 읽어 들이게 된다.

여러분이 커널 모듈 프로그래밍에 대한 이해가 있다는 가정하에 설명은 주석으로 대신하도록 하겠다. 모듈에서 main()함수에 해당하는module_init()함수에서 차근차근 분석해나가면 좀더 쉽게 이해할 수 있을 것이다.

예제 : myproc.c

#include <linux/module.h>
#include <linux/tty.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>

#define MODULE_VERSION	"1.0"

// 모듈의 이름이다. 
// 이 모듈이름으로 /proc밑에 디렉토리가 생성된다. 
#define MODULE_NAME		"myproc"

// 생성된 /proc/MODULE_NAME 밑에 아래 2개의 파일이 
// 생성된다. 
#define FOO_FILE		"foo"
#define JIFFIE_FILE		"jiffies" 

#define FOOBAR_LEN 8

// foo 파일에 저장될 데이터 구조체
struct fb_data_t
{
	char name[FOOBAR_LEN + 1];
	char value[FOOBAR_LEN + 1];
};
struct fb_data_t foo_data;


static struct proc_dir_entry *example_dir, *foo_file, *jiffies_file;

// 사용자가 cat등을 통해서 /proc/[MODULE_NAME]/jiffies파일을 열면 
// 커널은 이 함수를 호출해서 해당 정보를 넘겨준다. 
static int proc_read_jiffies(char *page, char **start, off_t off,
			   int count, int *eof, void *data)
{
	int len;

	MOD_INC_USE_COUNT;
	// 사용자가 이해하기 쉬운 포맷으로 만든다. 
	// cat, vi등을 사용해서 이 파일을 열경우 
	// jiffies = 1234 와 같은 형식으로 보인다. 
	len = sprintf(page, "jiffies = %ld\n", jiffies);
	// 해당 내용을 printk를 통해서 로그로 남긴다.  
	// 이 데이터는 /var/log/message로 출력된다.
	printk("<1> read jiffies = %ld\n", jiffies);
	MOD_DEC_USE_COUNT;

	return len;
}

// 사용자가 /proc/[MODULE_NAME]/foo 파일을 열었을 때 
// 출력해주는 정보 
static int proc_read_foobar(char *page, char **start, off_t off, 
							int count, int *eof, void *data)
{
	int len;
	struct fb_data_t *fb_data = (struct fb_data_t *)data;

	MOD_INC_USE_COUNT;
	// fb_data구조체의 내용을 보시쉽게 만들어서 출력해준다. 
	len = sprintf(page, "%s = %s",
					fb_data->name, fb_data->value);
	MOD_DEC_USE_COUNT;
	return len;
}

// 사용자는 /proc/[MODULE_NAME]/foo에 내용을 쓰기를 원할때도 있을 것이다.
// 이 때 이함수가 호출된다.
static int proc_write_foobar(struct file *foke, const char *buffer,
							unsigned long count, void *data)
{
	int len;
	struct fb_data_t *fb_data = (struct fb_data_t *)data;

	MOD_INC_USE_COUNT;
	if (count > FOOBAR_LEN)
		len = FOOBAR_LEN;
	else
		len = count;

	printk("<1> DATA COPY %d\n", len);
	// echo "xxxxx" > /proc/[MODULE_NAME]/foo 등으로 입력받은 값을
	// fb_data->value에 저장한다. 
	if (copy_from_user(fb_data->value, buffer, len))
	{
		MOD_DEC_USE_COUNT;
		return -EFAULT;
	}

	fb_data->value[len] = 0x00;
	MOD_DEC_USE_COUNT;

	return len;

}

// 커널 모듈 초기화 함수
static int init_myproc(void)
{
	int rv = 0;

	printk("<1> Module Start\n");
	example_dir = proc_mkdir(MODULE_NAME, NULL);
	if (example_dir == NULL)
	{
		rv = -ENOMEM;
		printk("<1> mkdir failure\n");
		goto out;
	}

	// JIFFILE파일의 경우 단지 읽기만 허용한다.
	example_dir->owner = THIS_MODULE;
	jiffies_file = create_proc_read_entry(JIFFIE_FILE, 0444, example_dir,
									proc_read_jiffies, NULL);
	if (jiffies_file == NULL)
	{
		rv = -ENOMEM;
		printk("<1> read entry failure\n");
		goto no_jiffies;
	}
	printk("<1> OK MAKE MODULE\n");
	jiffies_file->owner = THIS_MODULE; 

	// FOO_FILE의 경우 읽기와 쓰기 모두 가능해도록 해야 하며 
	// 각각의 경우에 호출될 함수를 지정해 줘야 한다. 
	foo_file = create_proc_entry(FOO_FILE, 0644, example_dir);
	if (foo_file == NULL)
	{
		rv = -ENOMEM;
		goto no_foo;
	}

	strcpy(foo_data.name, "foo");
	strcpy(foo_data.value, "dream");

	// 커널 자료구조에 저장될 데이터 
	foo_file->data = &foo_data;
	// 읽기를 요청했을 때 호출될 함수
	foo_file->read_proc = proc_read_foobar;
	// 쓰기가 요청되었을 때 호출될 함수
	foo_file->write_proc = proc_write_foobar;
	foo_file->owner = THIS_MODULE;

	printk("<1> %s %s initialised\n", "myproc", "1.0");

	// 제대로 초기화가 이루어졌다면 0을 리턴해야 한다. 
	return 0;
	no_foo:
		remove_proc_entry(FOO_FILE, example_dir);
	no_jiffies:
		remove_proc_entry(MODULE_NAME, NULL);
	out:
		return rv;
}

// cleanup 함수
static void cleanup_myproc(void) 
{
	printk("<1> END\n"); 
	remove_proc_entry(FOO_FILE, example_dir);
	remove_proc_entry(JIFFIE_FILE, example_dir);
}

module_init(init_myproc);
module_exit(cleanup_myproc);

MODULE_AUTHOR("yundream");
MODULE_DESCRIPTION("Sanmpe proc");
			

다음은 위 프로그램을 컴파일 하기 위한 make파일이다.

Makefile

WARN    := -W -Wall -Wstrict-prototypes -Wmissing-prototypes    
INCLUDE := -isystem /lib/modules/`uname -r`/build/include    
CFLAGS  := -O2 -DMODULE -D__KERNEL__ ${WARN} ${INCLUDE}    
CC      := gcc                                 
TARGET  := my_proc 
     
my_proc.o: my_proc.c
	${CC} ${CFLAGS} -c my_proc.c 
                                     
.PHONY: clean                                                      
                                          
clean:                                    
	rm -rf ${TARGET}.o    
			

insmod를 이용해서 모듈을 올린후 cat, echo 등을 통해서 직접 테스트가 가능하다.

# echo "okok" > /proc/myproc/foo
# cat /proc/myproc/foo
foo = okok 
			
작성된 모듈프로그램의 작동상황은 /var/log/message의 내용을 확인하면 된다. 만약 syslog와 klogd가 떠있다면 tail등의 도구를 이용해서 모듈작동상황을 확인할 수 있을 것이다.


4. 모듈 파라미터 처리

모듈은 command line에서 인자들을 받을  있다인자를 모듈로 넘기기 위해서는 module_param() 매크로를 사용하여 변          .     “ insmod

hello_world_parm.ko myarg1=1같이 int형의 하나의 인자를 넘기기 위해서는 커널의 모듈 코드에 다음과 같이 선언해야 한다.

 

int myarg1 = 0

module_param(myarg1, int, 0);

 

module_parm 매크로는 변수의 이름타입, sysfs 관련된 파일의 permission 이렇게 3개의 인자를 갖는다. 인자로 배열을 넘기는 방법은 약간 다르다먼저 매크로는

module_param_array 사용하며 4개의 인자를 갖는다. 3번째 인자로 배열의 갯수를 받을  있는 변수를 추가되었다.

 

int myarg2 [4];

int count;

module_parm_array(myarg2, int, &count , 0);

 

인자의 갯수를 특별히 받고 싶지 않다면 단순히 NULL 사용하면 된다.

다음모듈의 파라미터를 설명하기 위한 MODULE_PARM_DESC() 매크로가 있다이것은 모듈을 사용하는 사람을 위해서 인자를 설명하기 위한 방법이다이것은 변수의 이름과  변수를 설명하는 문자열을 받게 된다.

 

MODULE_PARM_DESC(myarg2, "This is int array forI/O port data");

위의 모든 것을  포함해서 hello_world2 만들어 보자.

 

#include <linux/module.h>

#include <linux/moduleparam.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/stat.h>

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("you");

 

static int argint = 1;

static char *argstring = "This is a string";

static int argarray[2] = { 0, };

static int size_argarray= 0;

 

 

module_param(argint, int, S_IRUSR | S_IWUSR |

S_IRGRP | S_IWGRP);

MODULE_PARM_DESC(argint, "A short integer");

module_param(argstring, charp, 0000);

MODULE_PARM_DESC(argstring, "A character string");

module_param_array(argarray, int, &size_argarray,

0000);

MODULE_PARM_DESC(argarray, "An array of

integers");

 

 

static int __init hello_init(void)

{

int i;

printk(KERN_EMERG "argint : %d ", argint);

printk(KERN_EMERG "argstring : %s ", argstring);

for (i = 0; i < (sizeof (argarray) / sizeof (int)); i++)

{

printk(KERN_INFO "argarray [%d] : %d ", i,

argarray [i]);

}

printk(KERN_INFO "argarrary size : %d ",size_argarray);

return 0;

}

static void __exit hello_exit(void)

{

printk(KERN_EMERG "Goodbye, world ");

}

module_init(hello_init);

module_exit(hello_exit);

 

 

위의 코드를 컴파일   실행하기 전에 modinfo 통해 모듈의 정보를 확인해보자.

 

root@barrios-desktop:~/example/module2# modinfo

hello_world.ko

filename: hello_world.ko

license: GPL

author: you

vermagic: 2.6.18 SMP mod_unload 586 REGPARM gcc-4.1

depends:

srcversion: 300231DCA83E809D0F54644

parm: argarray:An array of integers (array of int)

parm: argstring:A character string (charp)

parm: argint:A short integer (int)

 

 

위와 같이 이전에 비하여 많은 정보들이 출력되는 것을  수 있다 중에는 여러분들이 파라미터를 설명하는문자열도 출력된다이번에는 모듈을 로드해보자무엇이 출력되는가?

 

root@barrios-desktop:~/example/module2# insmod

hello_world.ko argint=100 argstring="hey"

argarray=100,200

여러분들의 예상이 맞는가?


출처 : http://www.linux.co.kr/home2/board/bbs/board.php?bo_table=lecture&wr_id=1640&sca=1&sca2=32

spinlock_t - SpinLock을 위한 자료구조

 

spin_lock(slp) - SpinLock을 획득

 

spin_unlock(slp) - SpinLock을 해제

 

spin_lock_init(slp) - SpinLock을 해제된 상태로 초기화 

 

spin_trylock(slp) - SpinLock을 획득하기위해 시도를 하고, 성공하면 1, 실패하면 0을 돌려줌 

 

spin_unlock_wait(slp) - SpinLock이 풀릴 때까지 기다린다. 

 

spin_lock_irq(slp) - 인터럽트를 금지시키고, SpinLock을 획득

 

spin_unlock_irq(slp) - SpinLock을 해제하고, 인터럽트금지를 해제 

 

spin_lock_irqsave(slp, flags) - 인터럽트플래그를 저장하고, 인터럽트를 금지하고, SpinLock을 획득 

 

spin_unlock_irqrestore(slp, flags) - SpinLock을 해제하고, 인터럽트플래그를 복구하며, 인터럽트금지를 해제 

 

 

어디서 가져온 자료인지 기억이 안나요...ㅜ..ㅜ

죄송합니다..;;

출처 : http://blog.naver.com/wjdguddnr00?Redirect=Log&logNo=20018016177

SpinLock이란?

Spin Lock 은 이름이 뜻하는대로, 만약 다른 스레드가 lock을 소유하고 있다면 그 lock이 반환될 때까지 계속 확인하며 기다리는 것이다.

Spinlock은 다음과 같은 특성을 갖는다.

  1. Lock을 얻을 수 없다면, 계속해서 Lock을 확인하며 얻을 때까지 기다린다. 이른바 바쁘게 기다리는 busy wating이다.
  2. 바쁘게 기다린다는 것은 무한 루프를 돌면서 최대한 다른 스레드에게 CPU를 양보하지 않는 것이다.
  3. Lock이 곧 사용가능해질 경우 컨택스트 스위치를 줄여 CPU의 부담을 덜어준다. 하지만, 만약 어떤 스레드가 Lock을 오랫동안 유지한다면 오히려 CPU 시간을 많이 소모할 가능성이 있다.
  4. 하나의 CPU나 하나의 코어만 있는 경우에는 유용하지 않다. 그 이유는 만약 다른 스레드가 Lock을 가지고 있고 그 스레드가 Lock을 풀어 주려면 싱글 CPU 시스템에서는 어차피 컨택스트 스위치가 일어나야하기 때문이다

 출처 : http://devnote.net/wiki/index.php/Spinlock

즉, 멀티코어CPU 환경에서 Lock을 소유하고 있는 시간이 매우 순간일 경우에 Critical Section이나 Mutex대신에 사용하면 더욱 좋은 성능을 기대 할 수 있다.


SpinLcok 구현

01.class SpinLock
02.{
03.private:
04.static size_t const ms_NonSleepLoopCount = 1000000;//Sleep을 사용하지 않고 Lock을 체크하는 갯수 설정
05.LONG volatile    m_lOwnThreadID;//현재 Lock을 소유한 thread id
06. 
07.public:
08.SpinLock():m_lOwnThreadID(0){}
09.~SpinLock(){}
10. 
11.void Lock()
12.{
13.LONG lThreadID = static_cast<LONG>(::GetCurrentThreadId());
14. 
15.size_t loop = 0;
16.LONG lCompareID = 0;
17.while ( lCompareID != ::InterlockedCompareExchange(&m_lOwnThreadID, lThreadID, lCompareID) )
18.{// Lock을 소유할 수 없는 경우에 Loop를 돌면서 계속해서 체크
19.if ( loop > ms_NonSleepLoopCount )
20.{
21.::Sleep(0);// Sleep
22.}
23.else
24.{
25.++loop;
26.}
27.}
28.}
29. 
30.void UnLock()
31.{
32.LONG lThreadID = static_cast<LONG>(::GetCurrentThreadId());
33.LONG Exchange = 0;
34.LONG lRet = ::InterlockedCompareExchange(&m_lOwnThreadID, Exchange, lThreadID);
35.assert( lThreadID == lRet );
36.}
37.};

Lock을 걸때는 ::InterlockedCompareExchange를 이용하여 현재 소유한 threadid가 없을경우 내threadid로 설정을 시도한다.
내 threadid로 설정을 하지 못하면 계속해서 loop를 돌면서 시도를 한다.
이 때, loop를 너무 오랜시간 동안 돌면 오히려 성능이 나빠질 수 있으니, 적절히 Sleep를 사용할 수 있도록 한다.
(loop 를 돌때마다 Sleep을 사용한다면 컨택스트 스위칭이 일어나 SpinLock이 무용지물이 될 가능성이 많다)

UnLock 에서는 소유한 threadid를 해제시켜주기만 하면 된다.

위의 소스에서는 Recursive Call을 할경우에 DeadLock이 걸린다.
Recursive Call을 지원하기 위해 아래와 같이 수정하였다.

01.class SpinLock
02.{
03.private:
04.static size_t const ms_NonSleepLoopCount = 1000000;//Sleep을 사용하지 않고 Lock을 체크하는 갯수 설정
05.LONG volatile    m_lOwnThreadID;//현재 Lock을 소유한 thread id
06.int m_iRecursiveLockCount;//Recursive Call을 한 횟수 기록
07. 
08.public:
09.SpinLock():m_lOwnThreadID(0), m_iRecursiveLockCount(0){}
10.~SpinLock(){}
11. 
12.void Lock()
13.{
14.LONG lThreadID = static_cast<LONG>(::GetCurrentThreadId());
15. 
16.// 현재 소유 threadID가 호출 threadID와 같으면 Recursive Call Count 만 증가
17.if ( m_lOwnThreadID == lThreadID )
18.{
19.++m_iRecursiveLockCount;
20.assert( m_iRecursiveLockCount > 0 );
21.return;
22.}
23. 
24.size_t loop = 0;
25.LONG lCompareID = 0;
26.while ( lCompareID != ::InterlockedCompareExchange(&m_lOwnThreadID, lThreadID, lCompareID) )
27.{// Lock을 소유할 수 없는 경우에 Loop를 돌면서 계속해서 체크
28.if ( loop > ms_NonSleepLoopCount )
29.{
30.::Sleep(0);// Sleep
31.}
32.else
33.{
34.++loop;
35.}
36.}
37.}
38. 
39.void UnLock()
40.{
41.LONG lThreadID = static_cast<LONG>(::GetCurrentThreadId());
42. 
43.// Recursive Call Count가 있으면 감소만 함
44.if ( m_iRecursiveLockCount )
45.{
46.assert(m_lOwnThreadID == lThreadID);
47.--m_iRecursiveLockCount;
48.return;
49.}
50. 
51.LONG Exchange = 0;
52.LONG lRet = ::InterlockedCompareExchange(&m_lOwnThreadID, Exchange, lThreadID);
53.assert( lThreadID == lRet );
54.}
55.};

성능 테스트

그럼 이제 실제로 Critical Section과 SpinLock의 성능 차이를 테스트 하였다.
(테스트 방법에 따라 결과가 달라질 수 있음)

  - 테스트 환경 : 듀얼코어 CPU
  - 방법 
    1) std::map<int,int>을 하나 생성해서 1~1000000까지 키를 입력
    2) 각 쓰레드는 1~1000000까지 루프를 돌면서 map에서 find실행 이때, find한번당 Lock, UnLock을 호출하며, 루프가 종료되는데 걸린 시간을 측정
          for ( int iFind=0; iFind<1000000; ++iFind)
         {
          // Lock
              std::map<int, int>::const_iterator itr = g_map.find(iFind);
          // UnLock
          }
    3) 쓰레드 3개를 만들어서 결과 측정


측정결과는 예상대로 Critical Section을 사용하는 것 보다 SpinLock을 사용하는 것이 성능향상이 있었으며,
성 능향상율은 평균 20%정도나 되었다.

criticalsection.png
< Critical Section을 사용했을 때>

spinlock.png
< SpinLock을 사용 했을 때 >


출처 : http://yonmy.com/xe/blog/646


1. 개요

  ㅇ IETF(Internet Engineering Task Force)에서 작성한 인터넷 표준

  ㅇ 실시간 응용 데이터 전송을 위한 트랜스포트 프로토콜

  ㅇ RTP와 RTCP는 모두 UDP 상에서 동작

    - 품질 보장이나 신뢰성, 뒤바뀐 순서, 전송방지 등의 기능을 제공하지 못함

    - 실시간 응용에서 필요한 시간 정보와 정보매체의 동기화 기능 제공

    - 최근 인터넷 상에서 실시간 정보를 사용하는 거의 모든 어플리케이션(VOD, AOD, 인터넷 방송, 영상회의 등) 들이 RTP 및 RTCP를 사용

 

2. VoIP 망구조

    - MGCP : Media Gateway Control Protocol

    - TCAP : Transaction Capabilities Application Part

    - RTP/RTCP : Real-time Transport Protocol/RTP Control Protocol

    - LE : Local Exchange

    - SIP : Session Initiation Protocol

    - ISUP : ISDN Usr Part

    - STP : Signaling Transfer Point

    - SCP : Signaling Control Point

 

3. RTP(Real-time Transport Protocol) : 실시간 전송 프로토콜

  ㅇ Application Layer Protocol, 실시간 데이터를 전송하는 응용에 적합

  ㅇ 자원예약, QoS 보장 등의 기능은 제공하지 않음

  ㅇ 트랜스포트의 의미는 실시간 데이터의 특성에 중점을 두어 제정한 표준

  ㅇ RTP 패킷은 UDP를 이용하여 전달됨

 

4. RTCP(Real-time Transport Control Protocol)

  ㅇ RTP의 QoS를 유지하기 위해 함께 쓰이는 프로토콜

  ㅇ RTP는 데이터 전송에만 관계함. RTCP는 데이터 전송을 감시하고 세션 관련 정보를 전송하는데 관여

  ㅇ RTP 노드 들은 네트워크의 상태를 분석하고 주기적으로 네트워크의 정체여부를 보고하기 위해 RTCP 패킷을 서로에게 보냄

 

5. 동작과정



출처 : http://blog.naver.com/blue_button?Redirect=Log&logNo=120038371115

[출처] RTP와 RTCP|작성자 플라워

OpenWrt(오픈더블유알티)는 무선랜 라우터를 위한 비실시간(Non-Real-Time) 리눅스 기반의 오픈 소스 운영 체제이다. 원래는 Linksys사의 가정용 무선랜 라우터 모델인 WRT54G 시리즈의 성능을 강화하기 위한 커스텀 운영 체제로서 개발이 시작되었다가, 이후 점차 다른 무선랜 라우터들을 지원하기 시작하여 지금은 대부분의 라우터 플랫폼을 지원하고 있다. 무선랜 라우터 기능을 지원하는 임베디드 보드들은 대개 제한된 처리 능력과 메모리를 가지기 때문에 일반 PC에서와 같이 리눅스의 모든 기능을 구현하는 것이 불가능하며 라우터로서 반드시 필요한 기능들만 선택적으로 설치되어야 한다. OpenWrt는 무선랜 라우터에 필요한 리눅스의 기능들을 패키지 형태로 제공함으로서 사용자들에게 편의를 제공한다. 초기에 OpenWrt는 배포 버전인 와이트 러시안(White Russian)과 개발 버전인 카미카제(Kamikaze)로 따로 관리되다가 2007년 2월에 카미카제로 통합되었다. 현재DD-WRT 등 많은 무선랜 라우터용 운영 체제들이 OpenWrt를 기반으로 하고 있다.
출처 : 위키백과

세션 개시 프로토콜(Session Initiation Protocol, SIP)은 IETF에서 정의한 시그널링 프로토콜로 음성과 화상 통화 같은 멀티미디어 세션을 제어하기 위해 널리 사용되며,[1] 인터넷 상에서 통신하고자 하는 지능형 단말(전화인터넷 콘퍼런스, 인스턴트 메신저 등)들이 서로를 식별하여 그 위치를 찾고, 그들 상호 간에 멀티미디어 통신 세션을 생성하거나 삭제 또는 수정하기 위한 절차를 명시한 응용 계층의 시그널링 프로토콜이다. 여기서 수정은 포트의 주소 변경, 더 많은 참여자의 초대, 멀티미디어 스트림의 추가 또는 삭제를 의미하며, 화상 회의, 스트리밍 동영상 유통, 메신저가입자 상태 정보파일 전송과 온라인 게임들에 응용되는 것이 가능하다.

SIP는 원래 Henning Schulzrinne과 Mark Handley가 1996년에 처음 고안 했다. 이 규격의 최근 버전은IETF 네트워크 워킹 그룹의 RFC 3261이다.[2] 2000년 11월 SIP는 셀룰러 시스템에서 IP 기반 스트리밍 멀티미디어 서비스를 위한 3GPP 시그널링 프로토콜과 IP 멀티미디어 서브시스템 (IMS) 구조로 체택되었다.

인터넷 기반 회의, 전화, 음성 메일, 이벤트 통지, 인스턴트 메시징 등 멀티미디어 서비스 세션의 생성, 수정, 종료를 제어하는 요구/응답 구조로서 TCP와 UDP에 모두 사용할 수 있으며, 각 사용자들을 구분하기 위해 이메일 주소와 비슷한 SIP URL을 사용함으로써 IP주소에 종속되지 않고 서비스를 제공받는다. HTTP와 SMTP의 많은 부분을 그대로 사용하여 개발된 텍스트 기반이므로 구현이 용이하며, 인터넷에서 사용되는 다른 많은 프로토콜과 결합하여 다양한 서비스들을 만들 수 있는 유연성과 확장성이 있다. ITU-T의 H.323에 대응되는 보다 간편한 프로토콜로, 1999년에 IETF MMUSIC (Multiparty Multimedia Session Control) 워킹 그룹에 의해 RFC 2543으로 제안된 이후 별도로 분리된 ITEF SIP 워킹 그룹에서 개정 작업을 진행하여 2002년 7월 RFC 3261 표준이 제정되었다.

출처 : 위키백과

각 가정이나 소규모의 기업을 위한 다중 서비스 광대역 접속용의 고객 댁내 장치비대칭 디지털 가입자 회선(ADSL), T1, 케이블광섬유와 같은 광대역 접속에서 음성과 데이터 및 비디오 정보 전송이 가능한 것으로, 다수의 전화 회선과 팩스 모뎀개인용 컴퓨터(PC) 및 사설 교환 시스템(PBX)과의 접속이 가능하다. 접속을 원하는 가입자에게 서비스 공급자가 설치하고 사용하는 기간 동안 접속 링크 및 장비를 관리해 준다.

출처 : 네이버 용어 사전

+ Recent posts