SlideShare ist ein Scribd-Unternehmen logo
1 von 51
學士學位論文
임 민 중指導敎授
東國大學校 情報産業大學 情報通信專攻
정 낙 천
년 월2005 11
學士學位論文
정 낙 천
임 민 중指導敎授
이 을 으로 함.論文 學士學位論文 提出
2005 11 18年 月 日
의 을 함鄭樂泉 情報通信學 學士學位論文 認准
2005 11 18年 月 日
委員長
委 員
委 員
東國大學校 情報産業大學 情報通信專攻
요 약- -
리눅스의 네트워킹 디바이스 드라이버의 작성 방법은 규칙처럼 정해져있다 그 이유는 블.
록과 문자 디바이스 드라이버와는 다르게 중간에 스택을 거치기 때문이다TCP/IP . TCP/IP
커널 스택은 리눅스의 가상 파일 시스템 안에 숨겨져 있으며 소켓, BSD , INET,
계층으로 세분화 되어 있으며 각 계층을 대표하는 구조체와 그TCP/UDP, Link, IP, ARP ,
구조체의 연산을 수행하는 구조체로 스택을 구현하고 있다 통신시 데이터의TCP/IP . TCP
전송과 수신은 소프트 라는 인터럽트 서비스를 통해서 이루어지며 각 계층별로 맵핑된IRQ ,
함수들이 호출되어 네트워크 디바이스로 전달되거나 수신된다 네트워크 디바이스 드라이버.
를 작성하려면 구조체의 필드들을 정확히 이해하고 있어야 하며 모듈의 등록net_device ,
및 초기화와 제거 데이터의 송수신 인터럽트 처리 통계처리를 기본적으로 구현해야 한다, , , .
목 차- -
서론I.
연구 배경 설명1.
본론II.
리눅스의 네트워킹1. TCP/IP
리눅스 네트워킹 자료구조1)
리눅스 네트워킹 제어흐름2)
데이터 전송 제어흐름i)
데이터 수신 제어흐름ii)
리눅스의 네트워크 제어2.
1) ifconfig
2) /proc/net/dev
와3) insmod rmmod
네트워크 디바이스 드라이버3.
네트워크 디바이스의 종류1)
네트워크 디바이스 드라이버 구현2)
구조체 의 이해i) net_device
상태나 제어를 나타내는 필드a.
함수 포인터 필드b.
네트워크 디바이스 드라이버의 초기화와 제거ii)
커널에 디바이스 드라이버를 등록 또는 제거a.
하드웨어 검출 및 하드웨어 초기화b.
네트워크 디바이스의 열기iii)
네트워크 디바이스의 닫기iV)
네트워크 디바이스에서의 패킷 전송V)
패킷의 전송처리a.
전송 시간 초과 시의 처리b.
인터럽트 처리c.
네트워크 디바이스에서의 패킷 수신Vi)
인터럽트 처리a.
수신 처리 함수b.
네트워크 디바이스의 통계 처리Vii)
결론III.
분석 결과1.
참고문헌■
도서1.
웹 사이트2.
- 5 -
서론I.
연구 배경 설명1.
리눅스의 네트워킹 디바이스 드라이버의 작성 방법은 규칙처럼 정해져있다 그 이유는 블.
록과 문자 디바이스 드라이버와는 다르게 중간에 스택을 거치기 때문이다 네트워TCP/IP .
크 디바이스 드라이버의 작성원리를 이해하기 위하여 커널 스택의 내부 구조와 동TCP/IP
작 디바이스 드라이버를 작성하기 위한 기법과 도구를 연구한다, .
본론II.
리눅스의 네트워킹1. TCP/IP
그림 은 리눅스의 작동 구조 중에서 네트워킹 부분을 확대하여 표현한 것이다 실제[ 1] .
리눅스 커널은 그림 의 구조와 일치되게 구현되어 있다 리눅스 네트워킹 구조에서 가장[ 1] .
상위에 있는 소켓 계층은 시스템의 인터페이스를 지원하기 위해서 존재한다 사용자BSD .
프로그램은 소켓 라이브러리를 통해서 시스템 함수 호출을 하며 이렇게 발생된 시스템 함,
수 호출은 소켓 계층으로 제어가 넘겨진다 리눅스는 계열의 소켓 인터페이스와BSD . BSD
똑같은 인터페이스를 가지고 있으며 기존에 만들어진 프로그램들에 대한 지원이기도 하다, .
그래서 소켓 계층으로 이름이 지어진 것이다BSD .
소켓 계층은 에 기반을 두는 나 프로토콜의 통신을 관리하는 역할을INET IP TCP UDP
한다 와 계층에서는 사용자로부터 전달 받은 데이터를 실제의 패킷을 나타내는. TCP UDP
특수한 구조체의 형태로 만들어 담고 이를 하위의 계층으로 전달하는 역할을 한다 즉, IP . ,
하위의 계층에는 이 계층에서 만들어진 데이터를 단순히 보내는 역할만 한다 반대로 데이.
터를 받을 때는 반대의 역할을 한다.
계층은 상위에서 만들어진 데이터를 보내는 것과 하위에서 받은 데이터를 상위계층으IP
로 올려주는 역할을 한다 이때 보내기 위한 데이터의 재조합이 일어나며 이때 받은 데이. ,
터의 내용은 건들지 않는다 다만 자신을 위한 헤더나 에러를 검사할 수 있는. CRC(Cyclic
를 더하는 과정이 있다 계층과 계층 사이에 존재하는Redundancy Check) . Link IP ARP
계층은 엄밀히 말해서 계열에 속하는 것으로 계층의 이더넷 프로토콜과 연동하여IP Link
동작할 때에만 계층에서 작동한다IP .
계층은 상위에서 받은 패킷에 헤더와 트레일러를 붙여 완성하여 프레임으로 만들고Link ,
네트워크 디바이스 드라이버와 프로토콜 스택사이에 완성된 프레임을 전송하기 위해 큐와
- 6 -
인터럽트 메커니즘을 제공하고 있다 이때 프레임은 상위에서 이더넷 등의. , PPP, SLIP
계층에서 동작하는 적당한 프로토콜을 정하여 내려와서 만들어진다Data link .
계층이 바로 장치와 디바이스 드라이버가 작용하여 프레임을 전송하는 역할을Physical
한다 이 부분은 장치를 생산하는 회사에서 디바이스 드라이버를 작성하여 제공하는 부분이.
다 그러나 네트워크 디바이스 드라이버는 항상 물리적인 장치만을 의미하진 않는다 일례. .
로 루프백 은 순수하게 소프트웨어로만 작성된 장치이다 또한 네트워크 디바이(loop-back) .
스 드라이버는 로 접근할 수 있는 다른 장치와는 달리 전용 디바이스가 설치되어 장mknod
치를 인식해야 사용할 수 있다 실제로 리눅스에서는 시스템에 설치된 알맞은 디바이스 드.
라이버를 커널에 포함하여 컴파일해야 파일을 볼 수 있다/dev/eth0 .
그림 리눅스의 네트워킹 구현 블록 다이어그램[ 1]
리눅스 네트워킹 자료구조1)
그림 는 리눅스에서 네트워킹을 지원하기 위하여 사용하는 자료구조를 보여준다 이것[ 2] .
은 그림 의 상자 안의 각 레이어별로 구분하여 순서대로 위에서부터 아[ 1] 'Kernel mode'
래로 번호를 매겨 표현하고 있다 또한 소켓 버퍼 라 불리는 각 계층별 데이. (socket buffer)
터를 표현하는 자료구조도 같이 포함 하였다.
리눅스 네트워킹 코드는 소켓 버퍼 라는 큰 메모리 영역에 패킷들을 유지(socket buffer)
- 7 -
한다 각각의 소켓 버퍼에는 디스크립터가 대응한다 디스크립터는 구조체로 구현. . sk_buff
하고 있으며 이에 대한 자료구조를 그림 의 가운데 에 표현하[ 2] Networking Data Buffer
였다.
구조체는 수많은 필드들을 담고 있지만 주요 자료구조로는 소켓버퍼 사용자 데sk_buff ,
이터 데이터 링크 계층 트레일러 소켓 네트워크 장치의 객체 전송 계, , INET , net_device ,
층 헤더의 디스크립터 네트워크 계층 헤더의 디스크립터 데이터 링크 계층 헤더의 디스크, ,
립터 목적지 캐시 엔트리를 담고 있다 이들은 네트워킹을 구현하는데 필요한 모든 정보를, .
담은 구조체의 받은 패킷 보내는 패킷 에러가 발생한 패킷을 담는 큐 안에 이중연결sock , ,
구조로 이루어져있다 즉 구조체는 하나의 패킷을 나타내고 있다. sk_buff_head .
또한 구조체는 구조체 안에서만 작동하는 것이 아니다 일반적으로 커널은sk_buff sock .
데이터 복사를 하지 않으며 간단히 소켓 버퍼 디스크립터 포인터를 각 네트워킹, sk_buff
계층에 차례로 전달한다 일례로 패킷을 보내려고 준비하는 과정에서 전송계층. (Transport
은 페이로드를 사용자 모드 버퍼에서 소켓 버퍼의 뒷부분에 복사한다 전송 계층은Layer) .
페이로드 앞에 나 헤더를 추가한다 다음으로 네트워크 계층으로 넘어가서 소켓TCP UDP . ,
버퍼 디스크립터를 전달받아 헤더를 전송 헤더 앞에 추가한다 끝으로 데이터 링크 계층IP .
이 헤더와 트레일러를 추가하고 패킷을 전송하기 위해 큐에 넣는다.
리눅스는 블록 디바이스 드라이버와 마찬가지로 네트워킹에 대한 연산 또한 가상 파일 시
스템 개념을 적용하고 있다 일례로 네트워크 통신을 위해서 함수로 소켓을 생성. socket()
하면 리눅스 커널은 프로세스가 가지고 있는 가장 큰 파일 디스크립터에 을 더한 정수형1
디스크립터만을 가져온다 이러한 디스크립터는 그림 와 같이 파일 디스크립터를 나타내. [ 2]
는 계층의 자료구조의 배열의 값이며 생성된 소켓 파일은VFS files_struct fd_array , file
구조체에 매핑 되어 하나의 파일처럼 작동하는 것이다 구조체에서는 필드를 두어. file f_op
리눅스가 가상 파일 시스템으로 작동할 수 있게 한다 일례로 사용자가 소켓 파일을 열면.
리눅스 코드는 필드에 구조체를 대입하여 하위의 네트워킹 코드들과f_op sock_files_ops
연결시켜준다 또한 다른 일례로 사용자가 파일을 마운트할 경우에는 리눅스 코드는. ntfs
필드에 구조체를 디렉터리를 마운트할 경우에는 멤버f_op ntfs_file_operations , ntfs f_op
에 구조체를 대입하여 파일시스템을 사용 할 수 있게 한다ntfs_dir_operations ntfs .
리눅스 네트워킹 코드 또한 가상 파일시스템과 비슷하게 각 계층마다 필요한 연산들을 끼
고 뺄 수 있게 연산에 대한 추상화를 하였다 즉 하위 층에서 제공하는 함수를 상위 층에. ,
서 이용할 때 상위층에서 직접 이 함수를 호출하는 것이 아니라 하위 층이 자신이 제공하는
함수의 시작 주소를 특정 자료구조에 등록하고 상위 층에서는 단지 자료구조에 등록된 함,
수를 호출하는 방식으로 하위 층의 함수를 간접 호출하는 방식으로 구현한 것이다.
그림 에서 보듯이 계층에서는 구조체가 하위 계층으로 진입하기위[ 2] INET proto_ops
한 추상화된 연산을 정의하고 있으며 이때 의 연산을 수행하려면 이 구조체에, ipv4 TCP
구조체를 대입하면 된다 계층에서는 상위 계층에서 하위 계층inet_stream_ops . TCP/UDP
- 8 -
으로 데이터가 이동할 때와 하위 계층에서 상위 계층으로 데이터가 이동할 때 사용하는 자
료구조가 틀리다 그것은 계층과 계층에 여러 프로토콜이 존재하기 때문이다. IP TCP/UDP .
상위 계층으로부터의 데이터 이동을 위해서는 구조체를 사용하면 되고 하위 계층으proto ,
로부터의 데이터 이동을 위해서는 구조체를 사용하면 된다 계층에서는inet_protocol . IP
상위 계층으로부터의 데이터 이동을 위해서 구조체를 사용하였으며 하위 계층으tcp_func ,
로 부터의 데이터 이동은 구조체를 사용하였다 여기서 는 사용자가packet_type . tcp_func
소켓을 생성하였을 때 사용되는 구조이다 만약 사용자가 소켓을 사용한다면TCP . RAW
소켓 계층의 구조체의 필드에서 구조체를 바탕으로 계층의BSD sock tp_pinfo tp_raw IP
주요 자료구조가 바뀔 것이다 이러한 구조로 리눅스는 제어 흐름이 다양한 곳으로 분기할.
수 있는 상황을 효과적으로 지원할 수 있는 것이다.
그림 의 번 계층은 모든 상황에 쓰이지는 않지만 현재 대부분의 네트워[ 2] 4.5 IP(ARP) ,
킹이 이더넷으로 이루어져 있기 때문에 포함하였다 여기서는 계층에서 수행하는 라우팅. IP
을 위한 보조적인 역할을 담당하고 있다 계층으로 가면 구조체가 쓰이고. Link net_device
있는데 이 구조체는 네트워크 디바이스 드라이버 코드를 작성하기 위해서도 필요한 구조체
이다 이 구조체는 계층에서 단독으로 사용하는 큐인 자료구조와 네트워크 디바. Link qdisc
이스 드라이버가 사용할 함수를 정의한 포인터를 가지고 있다 사용자 응용 프로그램에서.
호출한 시스템 함수의 콜은 이 자료구조에 등록된 디바이스 드라이버의 함수가 궁극적으로
호출된다.
-9-
그림리눅스의네트워킹자료구조[2]
- 10 -
리눅스 네트워킹 제어흐름2)
그림 은 리눅스의 여러 분기 중에서 통신시의 제어 흐름을 나타낸 블록 다이어[ 3] TCP
그램이다 여기서 왼쪽의 세로로 된 두 줄의 흐름은 데이터를 송신시의 흐름을 꺾인 화살표.
의 흐름으로 오른쪽의 두 줄은 데이터 수신시의 흐름을 색칠한 세모모양의 화살표의 흐름,
으로 나타냈다 가운데는 데이터의 송신과 수신시 커널에 인터럽트 등록하는 메커니즘과 커.
널의 인터럽트 수행 메커니즘을 나타낸 것이다 그림 에서는 호출 과정에 있는 함수의. [ 3]
이름을 연주황의 타원 안에 다음 단계로 넘어가는데 중요한 역할을 하는 함수의 코드를 초,
록색 상자 안에 적어놓았다 또한 함수이름이 없고 하얀 상자에 있는 코드들은 다음 계층으.
로 넘어가기 위해 추상화를 한 부분마다 존재하는데 이들은 다음 계층의 어떤 함수를 찾아,
갈 것인지를 명확하게 해준다 또한 네모 상자에 들어간 함수들은 매크로 함수를 표현하였.
다.
데이터 전송 제어흐름i)
소켓이 만들어지면 사용자 프로그램의 는 커널의 를 이용하여 데이터write() sys_write()
전송을 요청한다 는 의 일부로서 구조체를 가져와서 소켓용 파일 연. sys_write() VFS file
산 구조체에 등록된 함수를 호출한다 소켓 계층은 먼저sock_write() . BSD sock_write()
를 통하여 상위 계층에서 받은 제어 메시지를 처리하고 이상이 없으면 를sock_sendmsg()
호출하여 계층의 연산 함수인 를 구조체에서 찾아서 호출한다 현INET sendmsg() proto .
재의 통신 프로토콜은 이므로 계층은 먼저 을 사용하는 프로토콜 계열TCP , INET stream
의 연산을 위한 구조체에 등록된 가 실행된다 계층은 데이터의 특inet_semdmsg() . INET
별한 처리 없이 바로 프로토콜 연산을 위한 구조체에 등록된 를 호출TCP tcp_sendmsg()
한다.
계층의 주목적은 로 사용자로부터 넘겨받은 데이터를 소켓버TCP/UDP tcp_sendmsg()
퍼 형태로 만들고 를 거쳐 실제 전송을 시작하는 역tcp_push_one(), tcp_transmit_skb()
할을 한다 는 전송할 데이터를 버퍼 큐에 넣을 것인지 아니면 바로 보낼. tcp_push_one()
것인지를 결정한다 는 프로토콜이 사용하는 각종 필드들을 소켓. tcp_transmit_skb() TCP
버퍼에 채워 넣고 계층의 를 호출한다IP queue_xmit() .
계층의 프로토콜 연산을 지원하는 함수는 구조체에 정의되어 있IP TCP ipv4_specific
다 여기서 계층에서 호출한 는 이다 계층은. TCP/UDP queue_xmit() ip_queue_xmit() . IP
넘겨받은 소켓버퍼를 디바이스에 맞게 다듬어서 보내는 역할을 한다 따라서 넘겨받은 소켓.
버퍼를 합치거나 나누는 일이 발생한다 먼저 는 넘겨받은 소켓버퍼의 정. ip_queue_xmit()
보들을 검사하여 함수 호출 흐름을 결정하고 매크로를 거쳐서NF_HOOK()
를 호출하여 실제로 패킷을 자르 거나 조립ip_queue_xmit2() (segmemtation) (assembly)
- 11 -
또는 재조립 을 수행한다(reassembly) .
계층은 앞부분의 계층이 잡은 방향을 토대로 소켓버퍼에 필요한 정보들을IP(ARP) IP
채워 넣는 작업을 한다 그리고 를 거. ip_output(), ip_finish_output() , ip_finish_output2()
치면서 라우팅 테이블을 검색하고 필요에 따라서 의 함수들을 호출하여 계층의ARP Link
이나 를 호출한다output() hh_output() .
계층은 자료구조에 의해서 궁극적으로 를 호출Link arp_direct_ops dev_queue_xmit()
하게 된다 이 함수는 큐를 사용하여 패킷을 전송할 것인지 아니면 바로 패킷을 보낼 것인.
지 결정하고 그에 따라서 적당한 함수를 부르는 일을 수행한다 먼저 패킷을 바로 보낼 것.
인지를 결정하면 는 번 경로를 따라서 실제 네트워크 디바이스 드라이dev_queue_xmit() 2
버의 를 호출하여 패킷을 바로 보내게 된다hard_xtart_xmit() .
의 번 경로는 네트워킹에서 큐 메커니즘을 제공하는 구조체를dev_queue_xmit() 1 qdisc
이용한다 가장 먼저 실행되는 는 여러 조건을 검사하여 패킷을 큐에 넣는다. qdisc_run() .
큐에 들어간 패킷이 큐에서 빠져나와서 전송되려면 를 거쳐야 하는데 먼저qdisc_restart() ,
의 번 경로와 같이 패킷을 큐에서 빼서 디바이스 드라이버로 패킷 전송을qdisc_restart() 1
시도한다 데이터 전송 시에는 패킷을 전송할 준비를 미리 할 수 있기 때문에 인터럽트를.
하지 않는다 그러나 리눅스에서는 만약 디바이스가 바쁘거나 첫 번째 패킷 전송에 실패하.
였을 경우에는 의 번 경로와 같이 와qdisc_restart() 2 netif_schedule() __netif_schedule()
를 이용하여 소프트웨어 인터럽트 요청을 한다.
리눅스에서의 소프트웨어 인터럽트 과정은 크게 두 가지로 나눌 수 있다 먼저 인터럽트.
요청을 하고자 하는 프로세스는 커널내의 인터럽트 벡터 테이블에 등록을 해두는 것이다.
그 다음에 커널의 인터럽트 서비스를 수행하는 스레드가 정기적으로 인터럽트 벡터 테이블
을 살펴보아 등록된 인터럽트 요청이 있으면 그 요청을 수행해주는 방법을 사용한다.
먼저 인터럽트를 요청할 프로세스는 를 호출한다 이 함수는 그림cpu_raise_softirq() . [
에서 보는바와 같이 의 세 매3] __cpu_raise_softirq(), softirq_pending(), __IRQ_STAT()
크로를 통해서 인터럽트 벡터 테이블에 등록한다 그리고 로 불irq_stat[] . skoftirqd_CPUn
리는 소프트웨어 인터럽트 서비스를 수행하는 커널 스레드는 실행되면 의 무한ksoftirqd()
문 을 통하여 인터럽트 테이블을 검사한다 는 안에서 호출for . skoftirqd_CPUn ksoftirqd()
하는 로 인터럽트 서비스 요청을 감지하면 바로 를 수행하는softirq_pending() do_softirq()
데 이 함수는 해당 인터럽트 요청에 등록된 인터럽트를 수행할 함수를 수행해주는 역할을,
한다 는 와 관련된 함수를 호출할 것이다 패킷을. do_softirq() NET_TX_SOFTIRQ action .
받을 때 호출되는 함수는 이다action net_tx_action() .
데이터의 네트워킹 전송에 처음 네트워크 디바이스 드라이버가 커널에 등록되면
가 호출이 되는데 이 함수에서 전송과 수신용의 소프트웨어 인터럽트를 수net_dev_init() ,
행할 의 커널함수 개를 에net_tx_action(), net_rx_action() 2 open_softirq()
와 플래그를 사용하여 등록하고 있다 참고로NET_TX_SOFTIRQ NET_RX_SOFTIRQ .
- 12 -
는 네트워크 디바이스를 통해 패킷을 전송함을 의미하며 인덱스는 을NET_TX_SOFTIRQ 1 ,
는 네트워크 디바이스에서 패킷을 수신함을 의미하며 인덱스는 이다NET_RX_SOFTIRQ 2 .
인덱스는 우선순위를 의미하며 값이 적을수록 우선순위가 높다는 것을 의미한다.
결론적으로 이러한 인터럽트 메커니즘에 의해 수행되는 는 다시net_tx_action()
함수를 호출하여 다시 네트워크 디바이스로 부터의 패킷전송을 시도한다qdisc_run() .
데이터 수신 제어흐름ii)
데이터 수신시의 네트워킹 제어 흐름은 가장 최상위의 계층과 최하위의VFS Physical
계층의 두 곳에서 시작하여 계층과 계층 두 곳에서 만난다 그림 의TCP/UDP , INET . [ 3]
오른쪽 그림들에서 음영 처리되어 계층을 표현하는 상자가 있다 상위 계층에서 시작함과.
하위계층에서 시작함을 구분하기 위해서 표시하였다 음영 처리된 계층은 상위 계층에서 시.
작하는 것을 나타내고 음영 처리가 되지 않은 계층은 하위 계층에서 시작함을 의미한다, .
네트워크 디바이스 드라이버는 인터럽트를 커널에 등록함으로써 데이터를 받을 수 있는
준비가 완료된다 이 과정은 그림 의 데이터 수신 제어 흐름에서 계층에 나타. [ 3] Physical
나 있다 네트워크 디바이스 드라이버는 안에서 계층에 속하는. interrupt() Link netif_rx()
를 호출하고 는 를 호출하는 일련의 호출 단계를 거쳐서 인, netif_rx() cpu_raise_softirq()
터럽트를 등록한다 그러면 후에 소프트웨어 인터럽트를 처리하는 가 호출될. do_softirq()
때 와 관련된 함수를 호출할 것이다 패킷을 받을 때 호출되는, NET_RX_SOFTIRQ action .
함수는 이다 은 받은 패킷의 데이터를 읽어서 패킷action net_rx_action() . net_rx_action()
타입을 결정하여 계층의 적당한 함수로 전달하는 역할을 수행한다 이 부분 또한 추상화IP .
되어 있어서 계층의 연산을 지원하는 자료구조인 에 정의된 를IP ip_packet_type ip_rcv()
호출한다.
계층의 받은 데이터에 대한 주요 역할은 다음과 같다 함수로 받은 패킷의IP . ip_rcv()
체크섬 버전정보 데이터 길이 등의 필드들을 검사하여 자신의 것이 아니거(checksum), IP ,
나 에러가 있는 패킷은 버리는 등의 적당한 행동을 취한다 그 다음 자신의 패킷이거나 에.
러가 없는 패킷은 새로운 소켓 버퍼를 할당하여 프로토콜에 알맞은 필드들의 내용을 채우
고 로 이동하여 라우팅 테이블을 갱신하는 등의 작업을 수행한다 이후에, ip_rcv_finish() .
자신의 패킷만을 받아들여 를 통해서 된 패킷의 재조합ip_local_deliver() fragment IP
을 수행한다 마지막으로 계층은 에서 넘겨받은(reassembly) . IP ip_local_deliver_finish()
소켓 버퍼에서 헤더를 벗겨내어 상위 프로토콜인 을 찾아낸 후 상위 프로IP IP datagram ,
토콜정보를 바탕으로 상위 프로토콜에 을 전달한다 이때 데이터 전송을IP datagram . TCP
로 하였기 때문에 여기서도 데이터를 받은 것으로 가정한다 여기서도 프로토콜TCP . TCP
의 연산을 지원하는 함수들이 등록된 자료구조로 계층에서 먼저tcp_protocol TCP/UDP
호출될 함수는 임을 알 수 있다tcp_v4_rcv() .
- 13 -
계층의 는 하위 계층으로부터 받은 소켓 버퍼 객체를 분석하여TCP/UDP tcp_v4_rcv()
및 와 에 대한 처리를 한다 그 외의 일반적인 경우에는SYN ACK RST . tcp_v4_do_rcv()
를 호출하는데 이 함수는 소켓의 상태에 따라 달리 처리한다 만약 소켓의 상, INET . INET
태가 이면 소켓의 와 관련된 타이머를 갱신하고TCP_EXTABLISHED , INET TCP
를 호출한 후 복귀한다 만약 소켓의 상태가 이tcp_rcv_establish() . INET TCP_LISTEN
면 를 호출해서 처리를 넘겨주는 등의 상태제어 역할을 하고 있다 현재, tcp_v4_hnd_req() .
의 경우에는 소켓이 이미 연결 상태로 되어 있는 상태이므로 는tcp_v4_do_rcv()
를 호출한다 는 에 대한 처리와 상위 프로토tcp_rcv_establish() . tcp_rcv_establish() ACK
콜 계층에 패킷이 도착했음을 알리는 일을 한다 이를 위해서 사용하는 함수는.
이다 는 계층의 추상화된 를 호출tcp_data_queue() . tcp_data_queue() INET data_ready()
하여 상위 계층으로 제어 흐름을 넘긴다 여기서 받은 소켓 버퍼는 계층으로 전달되. INET
는 것이 아니고 계층에 남아 있는 것이다 그렇기 때문에 사용자가 를 호TCP/UDP . read()
출한 경우에 계층까지 제어 흐름이 내려가는 것이다TCP/UDP .
계층의 추상화된 함수인 는 다른 계층으로의 전환 방법이 프로토콜INET data_ready()
연산을 정의해 놓은 자료구조가 아닌 를 통해서 이루어지고 있다는 것이sock_init_data()
특징적이다 안에서 추상적인 는 실제로. sock_init_data() data_ready()
이다 는 다른 추가적인 작업이 없고 바로sock_def_readable() . sock_def_readable()
를 호출하는데 함수는 바로 소켓 버퍼wake_up_interruptible() , wake_up_interruptible()
를 기다리는 모든 프로세스를 깨운다.
만약 프로세스가 데이터 읽기를 원한다면 소켓 계층의 를 호출했을 것이며, BSD read() ,
이것은 다시 를 거쳐서 호출하게 될 것이다 버sys_read(), sock_read(), inet_rcvmsg() .
퍼가 비어있고 프로세스가 블로킹 모드 를 사용하고 있다면 계층의, (blocking mode) , INET
큐에서 잠들어 있게 된다 따라서 이젠 원하는 데이터가 도착했으므로 다시 깨어나게sleep .
될 것이며 이후에는 계층의 에 들어있는 소켓 버퍼 데이터들을 처리, INET receive_queue
하게 될 것이다.
사용자가 를 호출하여 시스템 콜인 를 거쳐서 계층의read() VFS sys_read() TCP/UDP
를 수행하고 계층의 까지의 과정은 데이터 전송 시tcp_recvmsg() INET add_wait_queue()
의 제어흐름과 똑같은 메커니즘을 사용하여 흐름을 따라간다 다만 눈여겨 볼 것은 데이터.
를 받기 위해 잠들어 있다가 데이터 수신시 커널 스택에 쌓여 있던 함수들이 복귀 하면서
받은 데이터를 처리하는 과정을 알아볼 필요가 있다 와. add_wait_queue()
는 단순히 계층에의 큐에 잠드는 프로세스를 추가하는 기능을tcp_data_wait() INET sleep
수행한다.
신호를 받은 프로세스는 다시 를 통해서 계층의wake-up tcp_recvmsg() INET
에 있는 데이터가 완전히 소진될 때까지 사용자 주소공간으로 데이터를 복receive_queue
사하는 일을 수행한다 이때 는 데이터를 그냥 복사하지 않고 긴급. tcp_recvmsg() (urgent)
- 14 -
데이터가 있는지 먼저 검색하여 사용자 공간으로 복사한다 부가적인 작업으로는 당. CPU
네트워크 통계정보 등을 업데이트하기도 한다 그 이후에는. inet_recvmsg(),
순으로 곧바로 복귀 한다 즉 계층에서 곧바로sock_recvmsg(), sock_read() . , TCP/UDP
사용자 공간으로 데이터를 복사하기 때문에 함수는 잠들어 있던 가read() tcp_recvmsg()
동작함과 동시에 데이터를 읽을 수 있게 된다.
-15-
그림리눅스의네트워킹제어흐름블록다이어그램[3]
- 16 -
리눅스의 네트워크 제어2.
리눅스는 네트워킹을 제어하기 위해 프로토콜 스택과 만을 제공하는 것이 아TCP/IP API
니다 리눅스 관리자가 네트워크를 간단하게 제어하게 하기위하여 쉘 환경에서 제공하는 여.
러 가지 메커니즘이 있다 리눅스는 수많은 네트워킹 도구들을 제공하고 있다 리눅스에서. .
네트워크 디바이스를 제어하기 위한 도구들은 명령과 파일 그리고ifconfig /proc/net/dev ,
와 명령이 있다insmod rmmod .
1) ifconfig
커널 부팅이 끝나고 시스템 초기화 스크립트 과정을 거치면 커널에 포함되었거나 모듈로,
작성된 대부분의 네트워크 디바이스 드라이버가 커널에 올라가 동작대기 상태가 된다 그러.
나 아직 네트워크 디바이스를 검출하고 이를 초기화한 후에 해당하는 네트워크 디바이스를,
나타내는 구조체를 커널에 등록한 상태일 뿐으로 커널과 연동되어 동작하는 것net_device
은 아니다 이것은 장에서 다룬 블록 디바이스 드라이버에서 블록 디바이스가 마운트되어. 7
야만 커널에서 사용할 수 있는 것과 같은 맥락이다.
네트워크 디바이스도 블록 디바이스처럼 마운트할 수 있다 그러나 블록 디바이스를 마운.
트하는 명령어와 다른 명령어를 사용하여 네트워크 디바이스를 활성화시켜mount ifconfig
커널과 연동하게 하거나 비활성 시켜 커널과의 연동을 끊는다.
명령어의 사용은 간단하다 자주 쓰이는 파라미터들만 모아놓은 명령의ifconfig . ifconfig
간단한 형식은 다음과 같다 굵게 나타낸 것들은 명령어 사용 시 그대로 써야하는 문자열이.
고 그 외의 것은 적당한 값을 넣어주면 된다.
ifconfig [interface-name] [ip-address] [netmask subnetmask] [up|down]
다음은 주소를 로 서브넷 마스크를 으로 하여 활성화IP 192.168.0.11 255.255.255.0
시키는 명령이다.
설정파일에 기본 설정을 해놓았으면 다음과 같이 명령어 옵션 과 만'interface-name' 'up'
주어도 된다 이 경우에 는 설정파일의. ifconfig /sys/config/network-scripts/ifcfg-eth*
내용을 읽어 와서 그 내용에 맞게 네트워크 카드를 활성화시킨다.
$ ifconfig eth0:0 192.168.0.11 netmask 255.255.255.0 up
- 17 -
$ ifconfig eth0 up
동작중인 네트워크 디바이스의 동작은 다음과 같이 간단하게 중단시킬 수 있다.
$ ifconfig eth0 down
마지막으로 현재 활성화되어 있는 네트워크 디바이스는 다음과 같은 명령으로 확인할 수
있다 이 명령을 사용하면 네트워크 디바이스의 이름 타입. (eth0), (Link encap:Ethernet),
하드웨어 주소 네트워크 주소(HWaddr 00:0D:60:5F:6A:31), (inet addr:210.94.178.168
상태 브로드 캐스트인Bcast:210.94.178.255 Mask:255.255.255.0), (UP, RUNNING),
지 멀티캐스트인지의 여부 데이터 처리능력(BROADCAST, MULTICAST), (MTU:1500
송신 및 수신 통계 정보Metric:1), (RX packets:7132616 errors:0 dropped:0
overruns:0 frame:0, TX packets:219956 errors:0 dropped:0 overruns:0 carrier:0,
collisions:0 txqueuelen:100, RX bytes:548724167 (523.3 Mb) TX bytes:34737277
하드웨어에 접근하기 위한 주소와 인터럽트 주소 등의 커널 내의 하드웨(33.1 Mb)), I/O
어 정보 를 확인할(Interrupt:11 Base address:0x8000 Memory:c0220000-c0240000)
수 있다 참고로 이들은 모두 네트워크 디바이스 드라이버 작성자가 만들어 주어야 하는 부.
분이기도 하다.
[root@150 /]$ ifconfig -a
eth0 Link encap:Ethernet HWaddr 00:0D:60:5F:6A:31
inet addr:210.94.178.168 Bcast:210.94.178.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:7132616 errors:0 dropped:0 overruns:0 frame:0
TX packets:219956 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
RX bytes:548724167 (523.3 Mb) TX bytes:34737277 (33.1 Mb)
Interrupt:11 Base address:0x8000 Memory:c0220000-c0240000
2) /proc/net/dev
파일은 이 커널과 연동되어 동작하는 네트워크 디바이스의 시스템 리소스/proc/net/dev
와 관련된 통계정보를 출력하는 역할을 한다 이 파일의 내용을 보기 위해서는 나. vi
같은 편집기를 사용하면 안 되고 명령어를 이용하여 사용해야 한다 이것은emacs cat .
- 18 -
라는 파일 시스템의 특징 때문인데 이 안의 파일들은 커널이 커널작동과 관련/proc /proc
된 모든 정보를 실시간으로 파일로 저장하는 곳이기 때문이다 이는 마치 윈도우의 제어판.
과 같다.
네트워크 디바이스 드라이버에 대한 통계정보를 출력한 것을 보여준다 이것은 보는 바와.
같이 각 디바이스 별로 송신시와 수신시의 통계를 보여주고 있다 이 통계들은 네트워크 디.
바이스 드라이버 작성자가 작성해야 하는 부분이 아니고 프로토콜 스택이 패킷의TCP/IP
송수신을 처리하면서 통계정보를 수시로 모아 출력한 결과물이다.
$ cat /proc/net/dev
Inter-| Receive
face |bytes packets errs drop fifo frame compressed multicast
lo: 344042 571 0 0 0 0 0 0
eth0:548755206 7133018 0 0 0 0 0 0
| Transmit
|bytes packets errs drop fifo colls carrier compressed
344042 571 0 0 0 0 0 0
34740843 219983 0 0 0 0 0 0
와3) insmod rmmod
네트워크 디바이스 드라이버를 커널에 등록하는 방법은 커널 컴파일시 삽입하는 방법과
모듈로 작성하여 런타임시 등록하는 방법이 있는데 이 두 명령어는 후자의 방법으로 디바,
이스 드라이버를 작성할 때 사용한다 전자의 방법은 다음절인 네트워크 디바이스 드라이버.
작성에서 자세히 설명한다.
명령어는 네트워크 디바이스를 검출하고 이를 초기화 한 후에 해당 디바이스를insmod ,
다룰 수 있는 자료구조인 구조체를 커널에 등록하는 작업을 수행한다 이렇게net_device .
가 수행하는 세 개의 작업은 네트워크 디바이스 드라이버에 구현되어 있어야 하는insmod
부분으로 눈여겨 볼 필요가 있다.
명령어의 사용은 문자나 블록 디바이스 드라이버와 같이 컴파일 된 모듈을 인자insmod
로 주면 된다.
네트워크 디바이스 드라이버 모듈이름$ insmod _ _ _ .o
는 사용시 등록된 구조체를 커널에서 제거하는 역할을 한다rmmod insmod net_device .
- 19 -
이 역할을 하는 함수 또한 네트워크 디바이스 드라이버에 구현되어 있어야 하는 부분이다.
의 사용은 와 비슷하나 모듈이름에서 확장자를 뺀 문자열을 인자로 주면rmmod insmod
된다.
네트워크 디바이스 드라이버 모듈이름$ rmmod _ _ _
- 20 -
네트워크 디바이스 드라이버3.
네트워크 디바이스 드라이버는 그림 과 그림 그리고 그림 에서 보았듯이 실[ 1] [ 2], [ 3]
제적인 하드웨어인 네트워크 디바이스의 작동을 제어하는 역할을 담당하고 있다 그러나 리.
눅스에서는 네트워크 디바이스를 항상 실제의 하드웨어를 다루는 데에만 작성되지 않는다.
루프백 처럼 논리적인 디바이스를 구현하기도 한다(loop-back) .
또한 네트워크 디바이스와 연결되는 위의 계층들은 문자나 블록 디바이스와 다르게
프로토콜 스택과 연결되어 있다 이러한 이유로 네트워크 디바이스 드라이버는 문TCP/IP .
자나 블록 디바이스와는 다르게 구현된다 네트워크 디바이스 드라이버의 구현은 좀 더 복.
잡한 경향이 있다 일례로 그림 의 제어 흐름은 네트워크 디바이스가 인터럽트의 처리를. [ 3]
해야 제대로 된 송수신을 수행 할 수 있음을 알 수 있다 또한 명령어의 출력. 'ifconfig -a'
물은 네트워크 디바이스 드라이버의 여러 가지 초기화와 통계 정보 등을 구현해야 함을 알
수 있다.
네트워크 디바이스의 종류1)
네트워크 디바이스 드라이버는 패킷의 전송과 수신을 담당하는 실제 하드웨어의 작동을
제어하는 드라이버가 있으며 특수한 목적으로 구현된 논리적 디바이스 드라이버가 있다, .
리눅스에서 구현해놓은 논리적 디바이스 드라이버로는 루프백 과 더미(loop-back)
디바이스 드라이버가 있다(dummy) .
루프백 디바이스 드라이버는 리눅스에서 라는 이름으로 구현되어 있다 는 루프백lo . lo
네트워크 디바이스 로 이 이름만 사용할 수 있다 디바이스는 논리적 네트워(127.0.0.1) . lo
크 디바이스이고 네트워크 시스템이 동작하기 위한 최소한의 네트워크 디바이스 이다.
에서 자세한 정보를 얻을 수 있다linux/drivers/net/loopback.c .
디바이스는 일을 하지 않는 명목상의 네트워크 디바이스로 네트워크 디바이스dummy0
가 없을 경우에 포함되는 디바이스이다 주로 라우팅 디바이스 포인터로 사용되고.
에서 선언 내용을 볼 수 있다 문자열 뒤에 붙은 은linux/drivers/net/dummy.c . dummy 0
여러 개의 디바이스를 만들 수 있기 때문에 순서를 붙여놓은 것이다 이 숫자는 커dummy .
널에서 제공하는 루틴으로 자동으로 주어지게 할 수 있다.
여기서 논리 디바이스 드라이버는 목적에 따라서 이름이 정해져있다 물리 디바이스 드라.
이버의 이름은 드라이버 초기화 함수에서 자유자재로 붙일 수 있으나 보통 로 시작하고eth
디바이스마다 과 같은 식으로 뒤에 번호를 붙인다 번호를 붙이는 방법 또한0,1,2,3 .
디바이스 드라이버에서 번호를 붙이는 것과 같은 방식으로 한다 는 많이 되는dummy . eth
것으로 랜이라고도 불리는 계층의 이더넷 프로토콜을 의미한다 그러므로Link (ehternet) .
- 21 -
나 등의 다른 프로토콜을 사용하는 네트워크 디바이스를 사용할 경우에는 이름을PPP SLIP
달리하여 구현해야 할 것이다.
네트워크 디바이스 드라이버 구현2)
커널 스택과 네트워크 디바이스 드라이버의 상관관계를 구체적으로 알아보기 위TCP/IP
하여 이더넷 제어 칩의 코드를 분석한다 일단 네트워크 디바이스 드라이버에서CS9800 .
구현해야 할 내용은 네트워크 디바이스 드라이버 가 모듈 또는 포함되어 컴파일 된 상태에
서 커널에 적재될 때의 초기화 처리 모듈의 커널에서 제거될 때의 종료 처리 네트워크 디, ,
바이스의 검출 네트워크 디바이스의 초기화 및 등록 네트워크 디바이스의 열기와 닫기 네, , ,
트워크 디바이스에서의 데이터 전송과 수신 인터럽트의 처리 네트워크 디바이스를 제어할, ,
함수의 구현 멀티캐스트 와 브로드캐스트 의 처리 등이 있다ioctl() , (multicast) (braadcast) .
이들의 구현을 하나하나 살펴볼 것이다 먼저 초기화와 관련된 커다란 자료구조인.
구조체에 대하여 알아보자net_device .
구조체 의 이해i) net_device
네트워크 디바이스 드라이버 또한 다른 디바이스 드라이버와 마찬가지로 초기화루틴들이
필요하다 그러나 네트워크 디바이스 드라이버는 디바이스 파일형태로 존재하는 문자나 블.
록 디바이스와는 다르게 디바이스 파일로 접근하지 않고 다른 방법으로 구현된다 또한 이, .
것을 자세하게 표현하면 문자나 블록 디바이스 드라이버는 그림 의 계층에 속하는[ 2] VFS
구조체와 연계하여 구현되지만 네트워크 디바이스 드라이버는files_operations ,
와 연계하는 부분을 프로토콜 스택에 맡기고 있으며 프로files_operations TCP/IP TCP/IP
토콜 스택의 최하위 계층인 계층과 연계하여 구현된다 그러므로 네트워크 디바이스Link .
드라이버는 계층에 속하는 구조체의 필드들의 내용을 초기화 함수들을 통Link net_device
하여 채워서 프로토콜과 연계해야 한다TCP/IP .
는 네트워크 디바이스 드라이버의 중요 자료구조로net_device include/linux/netdevice.h
에 선언되어 있다 커널 버전 에서는 이 구조체에 상태필드 제어필드 제어 함수 주소. 2.4 , ,
필드 등 모두 무려 개의 필드가 정의 되어 있다 그래서 네트워크 디바이스 드라이버에75 .
서 사용하는 필드만을 골라서 상태필드와 제어필드에 대한 설명과 제어 함수 주소 필드에,
대한 설명을 분리하여 설명한다.
- 22 -
상태나 제어를 나타내는 필드a.
char name[16];
네트워크 디바이스의 인터페이스 이름을 지정한다 이 필드는 주로 초기화 루틴인. init()
함수에서 지정한다 문자열은 의 포맷과 비슷하게 로 넣어주면 자동으로. printf() "eth%d"
와 같은 형식으로 처리된다 숫자를 붙일 필요가 없다면 과 같"eth0", "eth1", "eth2" . "net"
이 사용하면 된다.
unsigned long base_addr;
하드웨어를 제어하기 위한 주소를 지정한다 여기에는 이더넷 제어 칩의 선두 주소를 지.
정하면 된다 와 같이 가 지원도록 만들어진 디바이스에서는 강제. PCI PnP(Plug and Play)
로 지정하면 안 되고 공란으로 두어야 한다.
unsigned char irq;
인터럽트 서비스 번호이다 이 필드도 과 같이 가 지원도록 만들어진 디바. base_addr PnP
이스에서는 강제로 지정하면 안 되고 공란으로 두어야 한다.
unsigned char if_port;
디바이스 드라이버 물리적인 동작방식을 지정한다 일례로 로 작동시키기를 원. 10Mbps
하면 이 필드에 를 로 작동시키기를 원하면 이 필드에IF_PORT_10BASET , 100Mbps
를 대입하면 된다IF_PORT_100BASET .
커널에는 에 값이 인 부터 시작하여include/linux/netdevice.h 0 IF_PORT_UNKNOWN
IF_PORT_10BASE2, IF_PORT_10BASET, IF_PORT_AUI, IF_PORT_100BASET,
의 순서로 개가 열거형 키워드인IF_PORT_100BASETX, IF_PORT_100BASFX 7 enum
으로 정의되어 있다 그 외의 다른 동작방식을 정의하기 위해서는 네트워크 디바이스 드라.
이버에서 자체적으로 정의해 사용하면 된다.
unsigned char dev_addr[8];
디바이스와 연결된 네트워크의 주소를 지정할 때 사용된다.
unsigned short flags;
디바이스의 상태와 사용가능한 기능 등을 나타내는 필드이다 로 이루어져 있어서. 12bit
각 비트마다 디바이스의 기능을 끄고 키는 역할을 한다 이 필드에 설정할 수 있는 플래그.
들은 에 선언되어 있다 이들 중 일부는 디바이스의 행동을 정의하므로include/linux/if.h .
중요하다 그림 에 일부 플래그의 의미를 정리하였다. [ 4] .
- 23 -
상태 이름 값 설명
IFF_UP 0x1
디바이스가 활성화되어 있음을 나타낸다 디바이스 드.
라이버에서는 이 플래그를 설정하면 안 되고 참조만
해야 한다.
IFF_BROADCAST 0x2
디바이스가 브로드캐스트를 처리할 수 있음을 나타낸
다.
IFF_LOOPBACK 0x8 디바이스가 디바이스임을 나타낸다loop-bacck .
IFF_POINTOPOINT 0x10
나 와 같은 연결을 갖는 디바이스임을PPP SLIP 1:1
나타낸다.
IFF_NOARP 0x80
디바이스가 를 지원하지 않음을 나타낸다 보통ARP .
연결을 갖는 디바이스나 같은 논리적1:1 lo, dummy
인 디바이스에서 사용한다.
IFF_PROMISC 0x100
네트워크에 발생한 모든 데이터를 수신함을 나타낸다.
보통 와 같은 유틸리티에 의해 설정된다tcpdump .
IFF_ALLMULTI 0x200
모든 멀티캐스트 데이터를 수신하도록 하기위해 설정
한다 이 플래그는 가 지정되어 있. IFF_MULTICAST
을 때만 설정할 수 있다.
IFF_MULTICAST 0x1000 디바이스가 멀티캐스트를 처리할 수 있음을 나타낸다.
IFF_AUTOMEDIA 0x4000
여러 네트워크의 접속이 가능할 때 자동으로 사용가능
한 네트워크를 검출할 수 있음을 나타낸다.
IFF_DYNAMIC 0x8000
하드웨어 주소가 고정되지 않고 바뀔 수 있음을 나타
낸다 이는 보통 디바이스에 하드웨어 주소를 고정하기.
위해 사용하는 이 없을 경우에 이 플래그를eeprom
설정하여 수동으로 설정해야 함을 알 수 있다.
그림 디바이스 상태 및 기능을 나타내는 플래그[ 4]
unsigned long trans_start;
데이터 전송이 시작된 시간을 저장하는 필드이다 그러므로 네트워크 디바이스 드라이버.
는 데이터 전송이 시작되었을 때마다 이 필드 값을 로 설정해야 한다jiffies .
unsigned long last_rx;
데이터가 수신된 시간을 저장하는 필드이다 그러므로 네트워크 디바이스 드라이버는 데.
이터가 수신되었을 때마다 이 필드 값을 로 설정해야 한다jiffies .
int watchdog_timeo;
전송이 시작되고 나서의 시간을 설정하는 필드이다 단위는 이고 이 값을timeout . jiffies ,
- 24 -
초과해 전송이 되면 커널은 필드에 지정된 함수를 호출한다tx_timeout .
void *priv;
디바이스마다 처리해야 하는 정보를 저장할 메모리가 필요하면 이 필드를 이용해야 한다.
와 같은 함수를 사용할 경우에는 따로 할당할 필요가 없다alloc_ether() .
struct dev_mc_list *mc_list;
int mc_count
멀티캐스트를 처리할 때 사용되는 필드이다 는 멀티캐스트 주소 정보를 관리하는. mc_list
구조체의 선두 주소이고 는 에 포함된 멀티캐스트 정보의 목록수를 가지, mc_count mc_list
고 있다.
함수 포인터 필드b.
int (*init)(struct net_device *dev);
디바이스 드라이버를 커널에 등록하는 역할을 하는 함수이다 이 함수는 명령어. insmod
를 이용하여 모듈을 커널에 삽입할 때 수행되고 커널에서 딱 한번만 실행되는 특징을 가지
고 있다 구조체의 대부분의 필드를 이 함수에서 채운다. net_device .
int (*open)(struct net_device *dev);
커널에 등록된 디바이스를 활성화시키는 역할을 하는 함수이다 이 함수는 명령. ifconfig
어를 이용해 디바이스를 활성화시킬 때 수행된다 디바이스가 동작할 수 있도록 디바이스의.
각종 제어 레지스터들을 초기화하고 인터럽트 서비스 함수를 등록하는 일을 한다, .
int (*stop)(struct net_device *dev);
디바이스를 비활성화 시키는 역할을 한다 이 함수 또한 명령어로 디바이스를 활. ifconfig
성화시킬 때 수행된다 디바이스의 동작을 정지시키고 등록된 인터럽트 서비스 함수를 제. ,
거하는 등의 함수와 반대의 일을 한다open() .
int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev);
그림 의 데이터 송신 흐름에서 살펴보았듯이 커널의 프로토콜 스택의8.8 TCP/IP Link
계층에서 이 함수를 호출하여 데이터를 송신한다 즉 데이터 송신 처리 루틴은 이 필드에.
등록된 함수에 구현해야 한다.
void (*set_multicast_list)(struct net_device *dev);
네트워크 인터페이스가 처리하는 멀티캐스트 목록이 바뀌거나 멀티캐스트와 관련된 상태
가 바뀔 때 호출된다.
void (*tx_timeout) (struct net_device *dev);
- 25 -
데이터 송신 시 하나의 패킷을 전송한 후에 필드에 설정해 놓은 시간제watchdog_timeo
한 안에 다음 패킷을 전송을 하지 못해서 전송을 지속적으로 처리할 수 없게 될 때 커널에
서 호출되는 함수이다 이때 디바이스가 적절한 조취를 취할 수 있게 구현하면 된다. .
struct net_device_stats* (*get_stats)(struct net_device *dev);
명령어와 같은 응용 프로그램에 네트워크 디바이스 드라이버의 통계 정보가 필ifconfig
요할 때 호출된다.
네트워크 디바이스 드라이버의 초기화와 제거ii)
네트워크 디바이스 드라이버의 초기화 단계는 크게 세 단계로 나눌 수 있다 첫 단계는.
디바이스 드라이버를 커널에 등록하는 단계이며 등록한 다음엔 바로 프로토콜 스, TCP/IP
택이 디바이스 드라이버를 사용할 수 있게 그 연결통로 역할을 하는 구조체를, net_device
초기화하고 하드웨어를 검출한다 마지막으로 네트워크 디바이스의 제어 레지스터들을 초기.
화시켜 장치를 활성화시키는 것이다 첫 번째 단계는 함수에서 등록한 함수. module_init()
가 그 일을 수행하며 두 번째 단계는 구조체의 함수 포인터 필드가 마지, net_device init ,
막인 세 번째 단계는 구조체의 함수 포인터 필드가 수행한다net_device open .
커널에 디바이스 드라이버를 등록 또는 제거a.
코드 은 이더넷 제어 칩 의 코드 중 초기화의 첫 번째 단계인 디바이스 드[ 1-1] CS8900
라이버를 커널에 등록 초기화 제거 루틴을을 보여준다, , .
디바이스 드라이버 고유의 관리 구조체 선언/* */①
typedef struct {
struct net_device_stats stats;
u16 txlen;
int char_devnum;
spinlock_t lock;
} cs8900_1_t;
커널에 등록할 구조체 선언/* net_device */②
static struct net_device cs8900_1_dev = {
init: cs8900_1_probe
};
- 26 -
번 코드는 디바이스 드라이버가 드라이버를 관리하는 자기 자신의 구조체를 선언한 부1
분이다 이 구조체의 주소는 나중에 구조체의 필드의 내용이 된다. net_device priv .
번 코드에서는 커널에 등록할 구조체를 전역 변수로 선언하였다 여기서2 net_device .
구조체 전역 변수는 함수 포인터 필드만을 초기화 하였는데 이것은 번net_device init , 3
코드에서 구조체를 함수로 커널에 등록하기 전에 초기화 함net_device register_netdev()
수 주소를 지정해주어야 하기 때문이다 이렇게 하면 가 실행된 후에. cs8900_1_init() init
함수 포인터에 등록된 함수가 바로 실행되어 구조체의 나cs8900_1_probe() net_device
머지 필드들을 스택에서 사용할 수 있도록 초기화 할 수 있게 된다TCP/IP .
다른 필드들은 커널에서 제공하는 초기화 함수로 초기화 하거나 필드들의 목적에 맞게 적
당한 장소에서 초기화된다 일례로 우리가 작성하는 디바이스 드라이버는 이더넷 환경에서.
작동하는 드라이버를 작성할 것이기 때문에 구조체에서 이더넷 프로토콜과 관, net_device
련된 필드들은 함수로 쉽게 초기화할 수 있다 이 함수를 사용하는 코드를ether_setup() .
뒤에서 살펴 볼 것이다.
구조체 변수는 전역 변수로 선언하거나 함수를 이용해서net_device alloc_etherdev()
메모리 할당을 하는 방법이 있는데 여기서는 전자를 이용하였다 함수는. alloc_etherdev()
커널에 디바이스 모듈과 디바이스를 등록/* */③
static int __init cs8900_1_init (void) {
strcpy(cs8900_1_dev.name, "eth%d");
return (register_netdev (&cs8900_1_dev));
}
커널에서 디바이스 모듈과 디바이스를 제거/* */④
static void __exit cs8900_1_cleanup (void) {
cs8900_1_t *priv = (cs8900_1_t *) cs8900_1_dev.priv;
unregister_chrdev(priv->char_devnum,"cs8900_1_eeprom");
release_region (cs8900_1_dev.base_addr,16);
unregister_netdev (&cs8900_1_dev);
}
모듈을 초기화하거나 제거할 함수를 지정/* , */⑤
module_init (cs8900_1_init);
module_exit (cs8900_1_cleanup);
코드 모듈 초기화와 제거[ 1-1]
- 27 -
파일에서 선언되어 파일에서 구현된 것include/linux/etherdevice.h drivers/net/net_init.c
으로 이 함수의 원형은 코드 와 같다 이 함수의 인자에는 꼭 디바이스 드라이버에서 자[ 2] .
체 선언한 관리용 구조체가 사용할 메모리 크기를 지정해야 한다 물론 관리용 구조체에 메.
모리를 따로 할당할 필요가 없다면 코드 과 같이 이 함수를 사용하지 않거나 인자를[ 1-1]
으로 주면 된다0 .
#include <linux/etherdevice.h>
struct net_device *alloc_etherdev(int sizeof_priv);
코드 함수의 원형[ 2] alloc_etherdev()
는 디바이스 자체의 관리용 구조체와 구조체에 메모리를 할alloc_etherdev() net_device
당받아 구조체의 필드에 관리용 구조체의 선두주소를 대입한다 또한net_device priv . ,
구조체의 필드의 값을 자동으로 로 설정하여 메모리가 할당된net_device name "eth%d"
구조체의 선두주소를 반환한다 그러므로 이 함수를 사용할 때는 코드net_device . [ 1-1]
의 번과 번 그리고 번 코드가 달라진다 를 사용하여 달라진 코드2 3 4 . alloc_etherdev() [
의 번과 번 코드는 코드 과 같다 번 코드에서 전역으로 선언했던1-1] 2 3 [ 3] . 2 net_device
구조체에 값을 대입하였고 등록 함수인 번 코드에서 의 반환 값NULL , 3 alloc_etherdev()
을 전역으로 선언했던 구조체에 대입하였으며 의 등록도 그net_device cs8900_1_probe()
다음 코드에 행해진다 또한 구조체에 메모리를 할당했으므로 모듈이 커널에서. net_device
제거될 때 를 사용한 후에 로 할당된 메모리를 제거하는 코드unregister_netdev() kfree()
가 추가되어야 한다.
커널에 등록할 구조체 선언/* net_device */
static struct net_device *cs8900_1_dev = NULL;
커널에 디바이스 모듈과 디바이스를 등록/* */
static int __init cs8900_1_init (void) {
cs8900_1_dev = alloc_etherdev(sizeof(cs8900_1_t));
if(cs8900_1_dev == NULL) return -ENOMEM;
cs8900_1_dev->init = cs8900_1_probe;
return (register_netdev (&cs8900_1_dev));
}
커널에서 디바이스 모듈과 디바이스를 제거/* */
- 28 -
는 파일에서 선언되어 파일에서 구현된 것으로 이free() include/linux/slab.h mm/slab.c
함수의 원형은 코드 와 같다 이 함수는 메모리에 할당된 객체를 메모리에서 해제하여[ 4] .
준다.
#include <linux/slab.h>
void kfree (const void *objp);
코드 함수의 원형[ 4] kfree()
번 코드는 커널에 디바이스 모듈과 디바이스를 등록하고 있다 이 함수는 모듈이 커널에3 .
적재될 때 가장 먼저 실행되는 것으로 번 코드의 함수에서 이 함수를 등록, 5 module_init()
하였기 때문이다 이 함수는 가장먼저 구조체의 필드를 지정하고 있다. net_device name .
네트워크 디바이스는 디바이스 드라이버가 정하므로 고유한 이름을 지정해야 한다 여기에.
서는 를 사용하는데 이는 동일한 네트워크 디바이스 드라이버를 사용하는 하드웨"eth%d" ,
어가 등록될 때마다 와 같은 식으로 디바이스 이름이 다르게 등록된eth0, eth1, eth2, ...
다 만약 이름을 하나만 지원하고 디바이스도 하나만 지원한다면 고 같은 형식으로도. "net"
지정할 수 있다.
코드 의 번 코드의 는 번에 걸쳐 설정한 구조[ 1-1] 3 register_netdev() 1~2 net_device
체를 커널에 등록해주는 중요한 함수이다 이 함수는 파일에서. include/linux/netdevice.h
선언되어 에 구현된 것으로 이 함수의 원형은 코드 와 같다 디바drivers/net/net_init.c [ 5] .
이스 드라이버를 등록해주는 문자나 블록 디바이스와는 다르게 이 함수는 file_operations
구조체를 등록하지 않고 구조체를 등록한다 이것은 위의 를 설명할net_device . net_device
때 언급했던 것처럼 디바이스 드라이버를 응용 프로그램이 바로 사용하는 것이 아니라 응,
용 프로그램이 를 거쳐서 사용하기 때문인 것이다TCP/IP .
#include <linux/netdevice.h>
int register_netdev(struct net_device *dev);
코드 함수의 원형[ 5] register_netdev()
static void __exit cs8900_1_cleanup (void) {
위와 상동 단지 마지막 줄에 함수를 추가/* , kfree() */
kfree(cs8900_1_dev);
}
코드 함수를 사용한 디바이스 초기화와 제거 코드[ 3] alloc_etherdev()
- 29 -
코드 의 번 는 모듈이 커널에서 제거될 때 실행되는 함수이다 이것 또[ 1-1] 4 cleanup() .
한 번의 초기화 함수처럼 번 코드의 에서 이 함수를 등록하였기 때문인3 5 module_exit()
것이다 이 함수에서는 디바이스 드라이버에서 할당한 메모리들을 제거하고 있다 중간에. .
수행하는 는 네트워크 디바이스가 가지고 있는 을 제거하기unregister_chrdev() EEPROM
위해서인 것이다 보통 네트워크 디바이스는 을 가지고 있어서 안에 디바이스의. ROM ROM
주소와 다른 필요한 정보들을 하드 코딩해 놓고 있다 만약 디바이스에 이 없다MAC . ROM
면 주소를 디바이스 드라이버 작성자가 드라이버에 주소를 설정하는 루틴을 직MAC MAC
접 작성해야 한다.
의 세 번째 줄에서는 를 사용하여 구조체의cleanup() release_region() net_device
필드에 할당된 메모리를 제거 해주고 있다 필드와base_addr . base_addr
은 두 번째와 세 번째 단계의 초기화를 수행하도록 등록된release_region()
와 연결된 설명이 필요하므로 뒤로 미루도록 하겠다cs8900_1_probe() .
에서 가장 중요한 것은 로 에서 등록한cleanup() unregister_netdev() register_netdev()
구조체를 커널에서 제거하는 함수이다 이 함수 또한 와 마net_device . register_netdev()
찬가지로 을 하고 에서 사용했던linux/netdevice.h include register_netdev() net_device
구조체 변수의 포인터를 매개변수로 호출하면 된다.
참고로 번 번 코드에서의 와 함수 와 키워드는 커널의2 3 init() cleanup() , __init __exit
효율적인 메모리 관리를 위해 만들어놓은 기법이다 먼저 와 함께 쓰인 함수는 커널에. __init
운영체제의 초기화 과정에만 사용된다는 것을 알려주게 된다 커널의 입장에서는 초기화 루.
틴들을 초기를 마친 후에 속해서 메모리에 남겨두고 있을 필요가 없다 그래서 커널은 이런.
초기화에만 필요한 함수나 변수를 컴파일 시 미리 별도의 영역에 따로 모아두었다가 초기,
화를 마친 후 이 영역의 메모리를 해제한다 그렇게 하면 필요 없는 메모리를 제거하여 커.
널이 차지하는 메모리의 양을 줄일 수 있게 되어 효율적인 운영체제가 되는 것이다. __exit
도 와 비슷한 맥락이다 와 함께 쓰인 함수들 또한 컴파일 시 미리 별도의 영역__init . __init
에 따로 모아두어 있다가 커널이 부팅할 때 메모리에 바로 올라오지 않는다 이들은 이들, .
함수가 호출될 때에 메모리에 적재되어 사용되고 바로 메모리에서 삭제된다.
하드웨어 검출 및 하드웨어 초기화b.
위의 모듈 초기화의 두 번째와 세 번째 단계는 프로토콜 스택이 디바이스 드라TCP/IP
이버를 사용할 수 있게 구조체를 초기화하고 하드웨어를 검출하여 하드웨어 장net_device
치를 활성화시키는 단계라 하였다.
이더넷 디바이스는 마지막 초기화 단계를 에서 수행하고 있CS9800 cs8900_1_probe()
다 의 코드는 코드 이다 실제로 는 하드웨어. cs8900_1_probe() [ 1-2] . cs8900_1_probe()
를 검출하고 활성화 시키는 코드들 중에서 상당양의 에러 제어 코드들이 있으나 이들은 네,
- 30 -
트워크 디바이스 드라이버를 작성하는 데에는 큰 도움을 주지 못하므로 코드를 읽기 쉽게
삭제하였다.
또한 하드웨어를 제어하는 코드들 또한 상당양이 있지만 삭제하지 않았는데 이는 디바이,
스 드라이버의 코드가 실제 디바이스다운 모습을 잃지 않게 하기 위한 것이며 의, CS8900
하드웨어는 대부분의 디바이스 드라이버가 갖추어야 할 기본적인 하드웨어 스펙만을 갖추고
있기 때문에 어떠한 방식으로 하드웨어를 제어하는지 쉽게 파악할 수 있다 그렇지만 완전.
히 디바이스의 하드웨어의 내부를 이해하여야만 정확한 동작을 이해할 수 있다CS8900 .
/* Driver initialization routines */
int __init cs8900_1_probe (struct net_device *dev) {
static cs8900_1_t priv;
int i,result;
u16 value;
/* initialize device fields */①
u16 MAC_addr[3] = {0, 0, 0};
memset (&priv,0,sizeof (cs8900_1_t));
result = check_region(dev->base_addr,16);
request_region (dev->base_addr,16,dev->name);
dev->if_port = IF_PORT_10BASET;
dev->priv = (void *) &priv;
/* use ethernet */②
ether_setup (dev);
/* register service routines */③
dev->open = cs8900_1_start;
dev->stop = cs8900_1_stop;
dev->hard_start_xmit = cs8900_1_send_start;
dev->get_stats = cs8900_1_get_stats;
dev->set_multicast_list = cs8900_1_set_receive_mode;
dev->tx_timeout = cs8900_1_transmit_timeout;
dev->watchdog_timeo = HZ;
/* probe ethernet hardware */④
#ifdef CONFIG_ARCH_PXA_LDS2000
- 31 -
dev->base_addr = 0xf0000000 + 0x300;
dev->irq = IRQ_GPIO(0);
for (i = 0; i < ETH_ALEN / 2; i++) {
MAC_addr[i]= cs8900_1_read(dev,PP_IA+i*2);
dev->dev_addr[i*2] = MAC_addr[i] & 0xff;
dev->dev_addr[i*2+1] = (MAC_addr[i] >> 8) & 0xff;
}
#endif
/* verify EISA registration number for Cirrus Logic */⑤
value = cs8900_1_read (dev,PP_ProductID);
/* verify chip version */
value = cs8900_1_read (dev,PP_ProductID + 2);
/* setup interrupt number */
cs8900_1_write (dev,PP_IntNum,0);
/* If an EEPROM is present, use it's MAC address. */
/* A valid EEPROM will initialize the registers automatically. */
result = cs8900_1_eeprom (dev);
/* prints initialization results */⑥
printk (KERN_INFO "%s: CS8900A rev %c at %#lx irq=%d",
dev->name,
'B' + REVISION (value) - REV_B,
dev->base_addr,
dev->irq);
printk (", eeprom ok");
printk (", addr:");
for (i = 0; i < ETH_ALEN; i += 2) {
u16 mac = cs8900_1_read (dev,PP_IA + i);
printk ("%c%02X:%2X", (i==0)?' ':':', mac & 0xff, (mac >> 8));
- 32 -
는 먼저 하드웨어와 연관이 없는 필드부터 채운다 번 코드에서 일반cs8900_1_probe() . 1
적인 필드를 채우고 번 코드에서 이더넷 설정을 한다 마지막으로 번 코드에서 디바이스, 2 . 3
의 작동을 제어하는 함수들을 구조체에 등록을 한다 그 다음부터는 하드웨어와net_device .
관련된 초기화를 진행하는데 번 코드에서 네트워크 디바이스를 검출하고 검출이 성공하면4 ,
번 코드를 거쳐서 네트워크 디바이스 내의 세부 하드웨어들을 찾아내고 초기화하여 최종5
적으로 디바이스 드라이버를 활성화시킨다 번 코드는 초기화를 끝내고 그 결과를 커널 메. 6
시지로 출력을 한 것뿐이다.
번 코드의 의 원형은 코드 과 같다 파일에서 선언되1 memset [ 6] . include/linux/string.h
었으며 파일에서 구현되었다 는 원하는 자료구조에 값을 채워주는, lib/string.c . memset()
함수이다 즉 는 새로 선언한 필드에. , “memset (&priv,0,sizeof (cs8900_1_t));” priv 0
을 채워 넣어 구조체의 필드에 그의 주소를 대입할 준비를 하는 것이다net_device priv .
#include <linux/string.h>
void * memset(void * s, int c, size_t count);
코드 함수의 원형[ 6] memset()
코드 의 번 는 앞서 보았던 디바이스의[ 1-2] 1 check_region(), request_region()
의 와 세트를 이룬다 이들은 매크로 함수로cleanup() release_region() .
에 선언되어 있으며 이 함수들의 원형은 코드 과 같다include/linux/ioport.h [ 7] .
는 세 번째 인자로 주어진 이름으로 커널 메모리를 두 번째 인자의 크기request_region()
만큼 첫 번째 인자에 할당한다 는 그 반대의 기능을 수행한다 마지막으. release_region() .
로 은 첫 번째 인자에 두 번째 인자의 크기만큼 시험 삼아 커널 메모리를check_region()
할당해 보고 그 결과를 반환한다.
}
printk ("n");
return (0);
}
코드 의 마지막 초기화 단계 함수[ 1-2] CS9800 cs8900_1_probe()
- 33 -
#include <linux/ioport.h>
struct resource *
request_region(unsigned long start, unsigned long n, const char *name);
int check_region(unsigned long start, unsigned long n);
void release_region(unsigned long start, unsigned long n);
코드 함수들의 원형[ 6] xxx_region()
즉 의 디바이스 드라이버는 위 세 개의 함수를 이용하여 구조체의, CS8900 net_device
필드에 커널 메모리를 할당받는 것이다 의 필드는 네트base_addr . net_device base_addr
워크 디바이스의 제어 레지스터를 찾아가기 위하여 메모리 맵핑된 주소를 가지고 있는 필드
로써 디바이스 드라이버에서 하드웨어를 제어하는데 유용하게 쓰인다.
코드 의 번 은 이더넷 프로토콜과 관련된 구조체의 필[ 1-2] 2 ether_setup() net_device
드들의 세팅을 해준다 보통 이더넷과 토큰링과 같이 이미 정형화 되어 있는 프로토콜들은.
세팅해주는 함수들을 두고 있다 이 함수들은 파일에 선언되어. include/linux/netdevice.h
있으며 파일에 구현되어 있다 코드 의 계열 함수drivers/net/net_init.c . [ 7] xxx_setup()
의 인자는 구조체 변수를 넣어주며 된다net_device .
#include <linux/netdevice.h>
ether_setup(); 이더넷 관련 필드 초기화 함수/* */
fc_setup(); 광채널 관련 필드 초기화 함수/* */
fddi_setup(); 광통신 관련 필드 초기화 함수/* */
tr_setup(); 토큰링 관련 필드 초기화 함수/* */
코드 계열 함수들의 원형[ 7] xxx_setup()
코드 의 역시 계열 함수들과 마찬가지로 여러 프로토[ 2] alloc_etherdev() xxx_setup()
콜을 지원한다 이 할당과 셋업 함수는 보통 네트워크 디바이스 구현 시 같이 사용되므로.
알아두는 것이 좋다 코드 은 할당 함수들의 원형을 보여주고 있다. [ 8] .
- 34 -
#include <linux/etherdevice.h> 이더넷/* */
struct net_device *alloc_etherdev(int sizeof_priv);
#include <linux/fcdevice.h> 광채널/* */
struct net_device *init_fcdev(struct net_device *dev, int sizeof_priv);
#include <linux/fddidevice.h> 광통신/* */
struct net_device *alloc_fddidev(int sizeof_priv);
#include <linux/hippidevice.h> 고성능 병렬/* */
struct net_device *alloc_hippi_dev(int sizeof_priv);
#include <linux/trdevice.h> 토큰링/* */
struct net_device *alloc_trdev(int sizeof_priv)
코드 함수들의 원형[ 8] alloc_xxx()
코드 의 번에서는 디바이스 제어 루틴을 등록하고 있다 이들의 함수들을 하나하[ 1-2] 3 .
나 살펴볼 것이다 여기서 구조체의 필드에 등록되는 는. net_device watchdog_timeo HZ
파일에 으로 정의되어 있다 또한 이 필드의 값은 항상 정include/asm-arm/param.h 100 .
의되어 있어야 디바이스 바르게 작동하므로 꼭 설정해주어야 한다.
코드 의 번에서 디바이스를 제어할 준비를 한다 여기서 와 필드에[ 1-2] 4 . base_ddr irq
하드웨어 정보를 대입한다 이 정보들을 바탕으로 하드웨어를 제어하게 되어 있다 보통. .
나 로 작동하는 디바이스일 경우에는 하드웨어를 검출하는 작업을 해주어야 하고PCI ISA ,
임베디드 시스템에서는 보통 네트워크 디바이스가 와 메모리 매핑으로 연결되어 있으CPU
므로 해당필드에 하드웨어 제작자가 할당한 주소와 인터럽트 번호를 필드에 대입하여주면
된다.
코드 의 번에서 디바이스는 칩의 버전이 맞는 것인지 다시 확인하고 할당받은 인[ 1-2] 5 ,
터럽트 번호를 하드웨어에 세팅하여 인터럽트가 발생하면 할당받은 인터럽트 번호로 커널에
전달하게 한다 마지막으로 하드웨어의 주소를 가지고 있는 에서 주. MAC EEPROM MAC
소를 읽어오고 있다 의 제어는 디바이스에서 따로 로 하고. EEPROM cs8900_1_eeprom()
있다.
네트워크 디바이스의 열기iii)
응용 프로그램으로 네트워크 디바이스를 활성화 시킬 때 수행되는 루틴이다ifconfig .
구조체의 함수 포인터 필드에 등록된 함수로 디바이스 드라이버net_device open CS8900
에서는 이라는 이름을 사용하였다 네트워크 디바이스 드라이버에서cs8900_1_start . open
필드에 등록된 함수는 크게 세 단계의 동작을 수행해야 한다 에 구현된. CS8900
- 35 -
는 앞으로 언급할 세 단계를 그대로 수행하고 있으므로 이 함수를 기준으cs8900_1_start()
로 설명한다 는 코드 과 같다. cs8900_1_start() [ 1-3] .
첫 번째로 로 인터럽트 서비스 함수를 등록한다 인터럽트 서비스 함수는request_irq() .
송신과 수신시 항상 동작되는 함수로 여기서는 를 등록하였다 두 번cs8900_1_interrupt() .
째로는 디바이스가 동작할 수 있도록 내부 레지스터들에 값을 설정하여 네트워크와 디바이
스를 연결시킨다 마지막으로 구조체내에 정의된 송신 큐가 동작하도록. net_device
를 호출해야 한다 코드 에 각 단계별로 번호를 붙여 놓았다netif_start_queue() . [ 1-3] . 2
번 코드는 하드웨어 의존적인 코드들이 있지만 여기서는 한 줄만 남겨 놓았다.
마지막으로 모듈이 사용되고 있음을 나타내기 위해서 디바이스 드라이버의CS8900
는 매크로를 사용한다cs8900_1_start() MOD_INC_USE_COUNT .
네트워크 디바이스의 닫기iV)
응용 프로그램으로 네트워크 디바이스를 비활성화 시킬 때 수행되는 루틴이다ifconfig .
구조체의 함수 포인터 필드에 등록된 함수로 디바이스 드라이버net_device stop CS8900
에서는 이라는 이름을 사용하였다 네트워크 디바이스 드라이버에서cs8900_1_stop . stop
필드에 등록된 함수는 필드에 등록된 함수와 반대의 동작을 수행하면 된다open .
첫 번째로 로 등록된 인터럽트 서비스 함수를 제거한다 그 다음 디바이스가free_irq() .
동작할 수 있도록 내부 레지스터들에 값을 설정하여 네트워크와 디바이스와의 연결을 끊는
static int cs8900_1_start (struct net_device *dev) {
/* install interrupt handler */①
request_irq (dev->irq,&cs8900_1_interrupt,0,dev->name,dev);
/* enable the ethernet controller */②
cs8900_1_set (dev,PP_RxCFG,RxOKiE | BufferCRC
| CRCerroriE | RuntiE | ExtradataiE);
/* start the queue */③
netif_start_queue (dev);
MOD_INC_USE_COUNT;
return (0);
}
코드 필드에 등록된[ 1-3] open cs8900_1_start()
- 36 -
다 마지막으로 를 호출하여 커널에 등록된 구조체 내에서. netif_stop_queue() net_device
동작하고 있는 송신 큐를 정지시킨다 디바이스 드라이버의 는. CS8900 cs8900_1_stop()
이 순서들을 그대로 수행하고 있지는 않지만 순서는 달라도 상관없다 또한 이 함수에서도.
번 코드와 같은 하드웨어 의존적인 코드들이 여러 줄이 있지만 첫 줄만 남겨 놓았다1 .
네트워크 디바이스에서의 패킷 전송V)
패킷의 전송은 디바이스에서 패킷을 전송하여 성공하면 금상첨화일 것이다 그러100% .
나 패킷을 전송하기까지의 과정이 프로토콜 스택을 거쳐서 진행되기 때문에 패킷TCP/IP
전송처리 뿐만 아니라 여러 요인으로 인하여 소켓 버퍼의 데이터가 전송되지 않을 수 있,
다 또한 리눅스 네트워킹 제어 흐름에서 살펴보았듯이 계층에서 직접 전송이 진행되. Link
지 않으면 나중에 데이터를 전송하기 위해 커널의 인터럽트 서비스 루틴에 패킷을 등록한
다 이러한 이유로 디바이스 드라이버에서의 전송은 세 개의 함수로 구현된다 가장 먼저. .
패킷전송을 수행하는 함수 전송시간이 초과되었을 경우에 수행되는 함수 인터럽트를 처리, ,
하는 함수이다.
패킷의 전송처리a.
패킷전송은 리눅스의 네트워킹에서 살펴봤듯이 사용자가 를 호출하면 궁극적으로read()
코드 와 같은 함수가 호출된다 이 함수는 구조체의 함[ 1-5] . net_device hard_start_xmit
static int cs8900_1_stop (struct net_device *dev) {
/* disable ethernet controller */①
cs8900_1_write (dev,PP_BusCTL,0);
/* uninstall interrupt handler */②
free_irq (dev->irq,dev);
/* stop the queue */③
netif_stop_queue (dev);
MOD_DEC_USE_COUNT;
return (0);
}
코드 필드에 등록된[ 1-4] stop cs8900_1_stop()
- 37 -
수 포인터 필드에 등록된 함수로 디바이스 드라이버에서는CS8900 cs8900_1_send_start
이라는 이름을 사용하였다.
static int cs8900_1_send_start (struct sk_buff *skb,struct net_device *dev) {
cs8900_1_t *priv = (cs8900_1_t *) dev->priv;
u16 status;
spin_lock_irq(&priv->lock);
상위 계층의 송신 큐를 중단/* */①
netif_stop_queue (dev);
디바이스에서 데이터를 전송하도록 설정/* */②
cs8900_1_write (dev,PP_TxCMD,TxStart (After5));
cs8900_1_write (dev,PP_TxLength,skb->len);
디바이스의 상태 검사/* */③
status = cs8900_1_read (dev,PP_BusST);
if ((status & TxBidErr)) {
spin_unlock_irq(&priv->lock);
printk (KERN_WARNING "%s: Invalid frame size %d!n",
dev->name,skb->len);
priv->stats.tx_errors++;
priv->stats.tx_aborted_errors++;
priv->txlen = 0;
return (1);
}
if (!(status & Rdy4TxNOW)) {
spin_unlock_irq(&priv->lock);
printk (KERN_WARNING "%s: Transmit buffer not free!n",dev->name);
priv->stats.tx_errors++;
priv->txlen = 0;
/* FIXME: store skb and send it in interrupt handler */
return (1);
- 38 -
데이터의 전송을 처리하는 의 설계는 일반적으로 여섯 단계를 거친다hard_start_xmit() .
먼저 인자로 넘겨받은 소켓버퍼 구조체에서 전송할 데이터와 그 길이 등의 정보를 추출하여
보낼 준비를 한다 디바이스 드라이버에서는 이 과정이 생략되어 다음 단계를 실. CS8900
행할 때 소켓 버퍼 구조체에서 필요한 값을 바로 추출해서 인자로 넣어주고 있다 다음 단.
계는 디바이스가 패킷을 전송할 동안 상위 계층인 계층에서 패킷 전송요구를 할 수Link
없도록 번 코드와 같이 를 호출하여 송신 큐를 중단한다1 netif_stop_queue() .
그 다음에는 번 코드와 같이 디바이스를 설정하여 데이터를 전송하도록 하는데 상식적2 ,
인 개념으로는 이 단계는 가장 마지막에 있어야할 단계처럼 보인다 그렇지만 디바이스안의.
버퍼가 비어있는 상태에서 디바이스를 활성화 시키면 디바이스는 데이터가 버퍼에 들어오,
자마자 바로 데이터를 보낼 수 있기 때문에 미리 전송시작을 하는 것이다 이는 디바이스의.
전송 회로를 활성화시킨 것으로도 볼 수 있다.
디바이스의 전송회로를 활성화시킨 후 디바이스 드라이버는 번 코드와 같이 디CS8900 3
바이스 자체 함수인 로 디바이스의 상태를 읽고 데이터 프레임 길이와cs8900_1_read() ,
전송 버퍼의 사용여부를 검사하여 다음 단계로 진행할지 검사한다.
번 코드가 실제로 데이터를 전송하는 부분으로 디바이스 내부의 버퍼에 소켓버퍼의 데4
이터를 집어넣는 과정이다 실제 전송하는 과정은 하드웨어에서 회로로 구성되어 있기 때문.
에 디바이스 드라이버의 작성자는 하드웨어 내의 버퍼에 데이터를 넣어주기만 하면 되는 것
이다 디바이스 드라이버에서는 자체 함수인 에서 수행. CS8900 cs8900_1_frame_write()
하는데 코드 에서 보듯이 계열의 함수를 이용하여 데이터를 하드웨어에 출력[ 1-6] out()
하고 있음을 볼 수 있다.
}
디바이스 내의 버퍼에 전송할 데이터인 소켓 버퍼를 넣는다/* FIFO . */④
cs8900_1_frame_write (dev,skb);
spin_unlock_irq(&priv->lock);
전송 시간을 기록/* */⑤
dev->trans_start = jiffies;
dev_kfree_skb (skb);
priv->txlen = skb->len;
return (0);
}
코드 필드에 등록된[ 1-5] hard_start_xmit cs8900_1_send_start()
- 39 -
static
inline void cs8900_1_frame_write (struct net_device *dev,struct sk_buff *skb) {
outsw (dev->base_addr,skb->data,(skb->len + 1) / 2);
}
코드 디바이스 버퍼에 데이터를 넣는[ 1-6] cs8900_1_frame_write()
실제 전송이 끝났지만 네트워크 디바이스 드라이버는 전송이 시작된 시간을 상위 계층에,
알려주어야 한다 코드 의 번에서 상위 계층에 알려주는 작업을 수행하고 있다 참. [ 1-5] 5 .
고로 는 현재 시각을 나타내는 커널전역변수로써 커널은 이 변수의 값을 항상 업데이jiffies
트 한다 그러므로 현재시각을 기록하려면 이 변수의 값을 그대로 대입하면 된다 여기까지. .
여섯 단계의 전송 처리 과정을 살펴보았다 이들의 순서는 어느 정도 바뀔 수 있어도 단계.
를 생략하면 디바이스에서 데이터를 전송하지 않으므로 준수하여야 한다.
코드 의 번 뒤에 나오는 코드들은 전송함수에서 사용한 자료구조들을 정리만을[ 1-5] 5
수행하고 있다 또한 디바이스 자체의 관리용 구조체에 전송정보를 기록하고 있음을 볼 수.
있다.
전송 시간 초과 시의 처리b.
커널은 소켓 버퍼의 데이터가 전송되지 않을 경우 마지막으로 전송된 시간을 참조하여 시
간 초과가 발생하면 네트워크 디바이스 드라이버에게 적절한 처리를 요구한다 즉 커널은, . ,
구조체의 필드에 등록된 함수를 호출하는데 여기서는net_device tx_timeout ,
가 해당된다 이 함수는 코드 과 같다cs8900_1_transmit_timeout() . [ 1-7] .
이 함수에서 구현해야 하는 기능은 간단하다 마지막으로 전송이 요구된 데이터가 디바이.
스에서 무시되도록 처리하고 관련된 에러 통계를 갱신하고 데이터를 전송할 수 있도록 하, ,
드웨어를 재설정한다 마지막으로 를 호출하여 커널에서 패킷을 다시. netif_wake_queue()
전송할 수 있도록 알린다 에서는 데이터를 무시하고 데이터를 재전송 할 수 있도. CS8900 ,
록 설정하는 하드웨어 처리과정은 생략되었으며 번 코드에서 자체 관리용 구조체에 에러, 1
통계를 갱신하고 있다.
필드에 등록된 함수는 번과 같이 커널이 패킷을 재전송 하도록 반드시tx_timeout 2
를 호출하여야 한다netif_wake_queue() .
- 40 -
static void cs8900_1_transmit_timeout (struct net_device *dev) {
디바이스 내에서의 에러에 대한 처리 수행/* */①
cs8900_1_t *priv = (cs8900_1_t *) dev->priv;
priv->stats.tx_errors++;
priv->stats.tx_heartbeat_errors++;
priv->txlen = 0;
커널내의 송신 큐를 깨운다/* . */②
netif_wake_queue (dev);
}
코드 필드에 등록된[ 1-7] tx_timeout cs8900_1_transmit_timeout()
인터럽트 처리c.
그림 의 네트워킹 제어흐름에서 살펴본 바와 같이 인터럽트 서비스를 처리하는 것이[ 3]
필요하다 또한 대부분의 네트워크 디바이스 드라이버는 기본적으로 인터럽트를 처리하지.
않으면 송수신을 할 수 없기 때문에 인터럽트 처리는 디바이스 드라이버에서 중요한 부분을
차지한다 코드 은 인터럽트 서비스 함수에서 송신과 관련된 코드만을 정리한 것이. [ 1-8]
다.
네트워크 디바이스는 번 코드에 나온 것과 같이 다섯 개의 상태에 의해 인터럽CS8900 1
트가 발생한다 다섯 가지 인터럽트 상태는 파일에 정의되어 있으며. drivers/net/cs8900.h ,
이들 상태는 문으로 구현되어 있다 인터럽트 상태와 구현방법은 일반적인 네switch-case .
트워크 디바이스 드라이버에서 거의 공식과 같이 적용되고 있다.
첫 번째 상태인 는 수신된 데이터가 있을 때 디바이스에서 발생한다 네트워크RxEvent , .
디바이스 드라이버는 데이터의 수신을 인터럽트에서 처리해야 하므로 수신된 데이터가 있으
면 수신한 데이터를 소켓 버퍼 구조체로 변환하여 커널내의 프로토콜 스택에 전달TCP/IP
한다 수신된 데이터를 처리하는 방식은 을 사용하는 방식과 단순히 수신처리만 하는. poll ,
방식이 있는데 방식은 커널 에서는 지원하지 않는 방식이므로 후자만 설명한다, poll 2.4 .
참고로 방식은 커널 에서부터 지원되는 방식이다poll 2.6 .
두 번째 상태인 는 이전에 디바이스가 가지고 있던 데이터 송신을 완료하였을TxEvent
경우에 발생한다 이때 를 호출하여 커널내의 프로토콜 스택. netif_wake_queue() TCP/IP
에 알려주어야 한다 자세한 구현방법은 코드 을 설명할 때 살펴본다. [ 1-8] .
세 번째 상태인 는 디바이스 안의 버퍼가 비어서 송신이 가능해졌을 경우BufEvent FIFO
에 발생한다 이 경우 또한 를 호출하여 커널내의 프로토콜. netif_wake_queue() TCP/IP
스택에 알려주어야 한다.
- 41 -
네 번째 상태인 는 수신에러가 발생했을 경우 발생한다 이때 네트워크 디바이스RxMISS .
드라이버에서 수생해야 하는 것은 수신 에러에 대한 통계 정보를 갱신하고 네트워크 하드,
웨어의 에러 상태를 없애 주어야 한다.
마지막 상태인 는 송신에러가 발생했을 경우 발생한다 이때는 수신에러와 발생했TxCOL .
을 경우와 마찬가지로 네트워크 디바이스 드라이버에서는 송신 에러에 대한 통계 정보를 갱
신하고 네트워크 하드웨어의 에러 상태를 클리어 해야 한다, .
네트워크 디바이스 드라이버의 인터럽트 서비스는 코드 의 번과 같이 가장 먼저[ 1-8] 1
구조체를 얻으면서 시작한다 나머지는 상태별 인터럽트 구현만이 인터럽트 서net_device .
비스 함수를 구성하고 있다.
코드 의 번에서 번까지는 송수신 상태별로 인터럽트를 구현하고 있다 번 코드[ 1-8] 3 5 . 3
에서 디바이스에서 송신이 끝나서 데이터 전송이 가능해졌을 경우를 처리한다 먼저 디바이.
스의 정확한 상태를 알기위해 디바이스 내의 레지스터에서 값을 읽어서 판단한다 그 후에.
는 디바이스에 에러가 나서 송신이 가능해진 것인지 정상적으로 동작한 것인지를 판단하여,
통계 처리를 한다 의 디바이스 드라이버에서는 지금까지 살펴보았듯이 변수. CS8900 priv
를 이용해서 통계를 구하고 있다 통계처리가 끝나면 디바이스는 를 호. netif_wake_queue()
출해서 커널내의 송신 큐에 재전송 또는 새로운 패킷 전송을 시작하라고 알려주며 작업을
끝낸다.
번 코드는 버퍼가 비어있을 경우에 수행되는 코드이다 여기서는 디바이스에 에러가 발4 .
생했는지 검사한 후 에러가 발생했으면 통계 처리를 하고 마지막으로, netif_wake_queue()
를 호출해서 커널내의 송신 큐에 재전송을 시작하라고 알려주며 작업을 끝낸다 정상적인.
경우에는 통계 처리를 하지 않는데 이는 디바이스가 패킷을 보내고 있거나 받고 있지 않았,
기 때문이다 번 코드는 송신 에러 시 수행되는 코드인데 특별한 처리 없이 통계 작업만. 5 ,
을 수행한다.
/* drivers/net/cs8900.h PP_ISQ */①
#define PP_ISQ 0x0120 /* Interrupt Status Queue */
#define RxEvent 0x0004
#define TxEvent 0x0008
#define BufEvent 0x000c
#define RxMISS 0x0010
#define TxCOL 0x0012
/* drivers/net/lds2000_cs8900_1.c */
static void cs8900_1_interrupt (int irq,void *id,struct pt_regs *regs) {
구조체를 가장 먼저 얻어야 한다/* net_device . */②
- 42 -
struct net_device *dev = (struct net_device *) id;
cs8900_1_t *priv = (cs8900_1_t *) dev->priv;
u16 status;
while ((status = cs8900_1_read (dev,PP_ISQ))) {
switch (RegNum (status)) {
송신이 끝났을 때/* */③
case TxEvent:
디바이스의 상태를 읽음/* */
priv->stats.collisions +=
ColCount (cs8900_1_read (dev,PP_TxCOL));
디바이스에 에러가 있을 경우의 통계 처리/* */
if (!(RegContent (status) & TxOK)) {
priv->stats.tx_errors++;
if ((RegContent (status) & Out_of_window))
priv->stats.tx_window_errors++;
if ((RegContent (status) & Jabber))
priv->stats.tx_aborted_errors++;
break;
정상적으로 동작하였을 경우의 통계 처리/* */
} else if (priv->txlen) {
priv->stats.tx_packets++;
priv->stats.tx_bytes += priv->txlen;
}
priv->txlen = 0;
커널에 알려 송신 큐를 재시작/* */
netif_wake_queue (dev);
break;
버퍼가 비었을 때/* */④
case BufEvent:
에러 시 통계처리/* */
if ((RegContent (status) & TxUnderrun)) {
priv->stats.tx_errors++;
- 43 -
네트워크 디바이스에서의 패킷 수신Vi)
수신 처리는 가 데이터 수신을 위해서 계속 디바이스 드라이버를 검사할 수 없기 때CPU
문에 보통 인터럽트 처리부터 수신이 시작된다 일반적인 네트워크 디바이스 드라이버는 인.
터럽트가 발생하면 인터럽트 서비스 함수에서 수신 상태를 확인하고 수신 처리를 담당하는,
함수를 호출한다 수신 처리용 함수는 패킷 송신의 인터럽트를 설명할 때 수신한 데이터를.
소켓 버퍼 구조체로 변환하여 커널내의 프로토콜 스택에 전달한다고 설명하였다TCP/IP .
인터럽트 처리a.
코드 의 번은 데이터가 수신되었을 때 디바이스가 이 인터럽트를 발생시킨다 여[ 1-9] 1 .
기서는 추가의 작업 없이 바로 수신된 데이터를 처리하는 함수인 를cs8900_1_receive()
호출하고 있다.
코드 의 번은 송신시 인터럽트 처리부분에서 설명하였듯이 버퍼가 비었을 경우에[ 1-9] 2
발생한다 이 부분 또한 수신에러가 발생하였는지 검사한 다음 에러가 발생했으면 통계처리.
를 하고 아니면 아무 일도 하지 않는다 즉 송신시의 인터럽트 처리 부분과 합하면 코드. , [
의 알고리즘과 같은데 버퍼가 비었을 경우에는 송수신 에러가 발생하였는지 검사하고 에9] ,
러가 발생하였으면 알맞은 통계처리를 해야 한다는 것을 알 수 있다.
priv->stats.tx_fifo_errors++;
priv->txlen = 0;
netif_wake_queue (dev);
}
정상일 경우 아무 일도 하지 않음/* */
break;
송신 에러가 발생했을 때/* */⑤
case TxCOL:
priv->stats.collisions +=
ColCount (cs8900_1_read (dev,PP_TxCOL));
break;
}
}
}
코드 필드에 등록된 함수의 송신처리 코드[ 1-8] irq cs8900_1_interrupt()
- 44 -
버퍼가 비었음case ( ):
송신 에러 발생if ( ) 송신 에러에 맞는 통계 처리( );
수신 에러 발생else if ( ) 수신 에러에 맞는 통계 처리( );
else 아무 일도 하지 않음( );
break;
코드 디바이스 버퍼가 비었을 경우의 인터럽트 처리 알고리즘[ 9]
코드 의 번은 수신 에러 시 수행되는 코드인데 여기서도 송신 에러 시와 마찬가[ 1-9] 3 ,
지로 단순히 통계 처리만을 수행함을 알 수 있다.
static void cs8900_1_interrupt (int irq,void *id,struct pt_regs *regs) {
struct net_device *dev = (struct net_device *) id;
cs8900_1_t *priv = (cs8900_1_t *) dev->priv;
u16 status;
while ((status = cs8900_1_read (dev,PP_ISQ))) {
switch (RegNum (status)) {
수신된 데이터가 있을 때/* */①
case RxEvent:
cs8900_1_receive (dev);
break;
버퍼가 비었을 때/* */②
case BufEvent:
에러 시 통계처리/* */
if ((RegContent (status) & RxMiss)) {
u16 missed = MissCount (cs8900_1_read (dev,PP_RxMISS));
priv->stats.rx_errors += missed;
priv->stats.rx_missed_errors += missed;
}
정상일 경우 아무 일도 하지 않음/* */
break;
송신 에러가 발생했을 때/* */③
- 45 -
수신 처리 함수b.
대부분의 네트워크 디바이스가 수신 처리 함수를 코드 과 거의 동일하게 구현한[ 1-10]
다 그것은 프로토콜 스택에서 제공하는 함수들을 사용하여 받은 데이터를. TCP/IP TCP/IP
프로토콜 스택이 이해할 수 있는 소켓 버퍼의 형태로 변환해야 하기 때문이다 디바이스마.
다 다른 부분은 코드 의 번 번 번 코드와 같이 디바이스에서 값을 얻어오거나[ 1-10] 1 , 2 , 6 ,
통계처리를 하는 부분뿐이다 그러나 번 코드의 경우에는 번 코드에서 에러인지 판단하. 1 2
기 위한 상태 정보와 소켓 버퍼를 할당받기 위한 수신 데이터의 크기를 디바이스에서 받아,
와 한다 번 코드의 경우에는 에러일 경우 통계처리를 하고 바로 복귀한다. 2 .
case RxMISS:
status = MissCount (cs8900_1_read (dev,PP_RxMISS));
priv->stats.rx_errors += status;
priv->stats.rx_missed_errors += status;
break;
}
}
}
코드 필드에 등록된 의 수신처리 코드[ 1-9] irq cs8900_1_interrupt()
static void cs8900_1_receive (struct net_device *dev) {
cs8900_1_t *priv = (cs8900_1_t *) dev->priv;
struct sk_buff *skb;
u16 status,length;
디바이스의 수신 상태와 데이터의 크기를 구함/* */①
status = cs8900_1_read (dev,PP_RxStatus);
length = cs8900_1_read (dev,PP_RxLength);
수신 상태가 에러이면 통계처리를 하고 복귀/* */②
if (!(status & RxOK)) {
priv->stats.rx_errors++;
if ((status & (Runt | Extradata))) priv->stats.rx_length_errors++;
if ((status & CRCerror)) priv->stats.rx_crc_errors++;
return;
}
- 46 -
코드 의 번에서 번까지 변환할 소켓버퍼에 관한 연산을 수행하게 된다 가장[ 1-10] 3 6 .
먼저 변환 받을 소켓 버퍼를 할당받아야 한다 소켓 버퍼를 할당받기 위해서는 번 코드에. 3
서처럼 를 호출해야 한다 는 에 구dev_alloc_skb() . dev_alloc_skb() include/linux/skbuff.h
현되어 있으며 의 원형은 코드 과 같다 이 함수는 프로토콜, dev_alloc_skb() [ 10] . TCP/IP
스택 내의 소켓 버퍼 풀에서 빈 소켓 버퍼를 받아오는 역할을 한다.
#include <linux/skbuff.h>
static inline struct sk_buff *dev_alloc_skb(unsigned int length);
코드 의 원형[ 10] dev_alloc_skb()
소켓 버퍼 할당/* */③
if ((skb = dev_alloc_skb (length + 4)) == NULL) {
priv->stats.rx_dropped++;
return;
}
skb_reserve (skb,2);
할당받은 소켓 버퍼에 받은 데이터를 채움/* */④
skb->dev = dev;
cs8900_1_frame_read (dev,skb,length);
소켓 버퍼의 프로토콜 타입 설정/* */⑤
skb->protocol = eth_type_trans (skb,dev);
소켓 버퍼를 커널에 전달/* */⑥
netif_rx (skb);
수신 데이터에 대한 통계 처리/* */⑦
dev->last_rx = jiffies;
priv->stats.rx_packets++;
priv->stats.rx_bytes += length;
}
코드 디바이스 드라이버의 수신 처리 함수[ 1-10] cs8900_1_receive()
- 47 -
그 다음에는 소켓 버퍼에 대한 연산을 수행하는 를 호출한다 이 함수의skb_reserve() .
원형은 코드 과 같다 이 함수는 소켓 버퍼의 데이터 공간을 앞쪽과 뒤쪽으로 두 번째[ 11] .
인자의 크기만큼 확장한다 이 함수는 메모리를 할당받는 것이 아니라 단순히 소켓 버퍼의.
필드만을 갱신한다data, tail, len .
#include <linux/skbuff.h>
static inline void skb_reserve(struct sk_buff *skb, unsigned int len);
코드 의 원형[ 11] dev_alloc_skb()
여기서 의 호출에서 데이터의 크기보다 약간 더 큰 데이터를 할당하고dev_alloc_skb() ,
로 소켓버퍼의 크기를 조절하는 것은 디바이스 내에 있는 버퍼가 링 구조로skb_reserve()
이루어져 있기 때문에 소켓 버퍼의 앞뒤에 여유 공간을 두어서 디바이스 내부 버퍼로부터
원활한 복사를 하기 위한 것이다.
번 코드에서는 현재 할당받은 소켓 버퍼 데이터를 사용하는 디바이스가 무엇인지 알려4
주기 위하여 소켓 버퍼의 필드에 디바이스의 구조체의 선두주소를 대입하dev net_device
였다 그 다음에 나오는 코드는 소켓 버퍼에 받은 데이터를 복사하는 코드이다. .
소켓 버퍼에 수신된 데이터를 복사해서 넣은 후에는 코드 의 번과 같이[ 1-10] 5
를 사용하여 소켓 버퍼의 필드들을 이더넷 타입으로 설정한다 이 함수는eth_type_trans() .
구조체의 프로토콜 의존적인 필드들을 자동으로 세팅해주는 와net_device ether_setup()
비슷한 역할을 하는 함수이다 이 함수의 원형은 코드 와 같다. [ 12] .
#include <linux/etherdevice.h>
unsigned short eth_type_trans(struct sk_buff *skb, struct net_device *dev);
코드 함수의 원형[ 12] eth_type_trans()
코드 의 번까지 오면 소켓 버퍼에 대한 모든 처리가 끝난 것이다 여기서는[ 1-10] 6 .
를 호출하여 소켓 버퍼를 프로토콜 스택의 계층으로 보낸다 그림netif_rx() TCP/IP Link . [
의 제어흐름에서 보았듯이 는 커널내의 인터럽트 요청 큐에 소켓 버퍼를 등록3] , netif_rx()
하여 준다 그러면 커널 인터럽스 서비스를 수행하는 커널 스레드 가 이를 발견하. ksoftirqd
여 상위 계층으로 계속 전달을 하는 것이다 데이터가 수신된 시간은 이 시점에서.
를 호출하고 난 바로 후임을 번 코드를 통해 알 수 있다netif_rx() 7 .
- 48 -
네트워크 디바이스의 통계 처리Vii)
네트워크 디바이스 드라이버는 패킷의 송수신에 관련된 통계 정보를 관리해야 한다 일반.
적인 네트워크 디바이스 드라이버는 디바이스 드라이버의 전역 구조체 변수CS8900 priv
와 같이 디바이스 자체 관리용 구조체로 관리하고 드라이버 내의 루틴들의 곳곳에서 통계,
처리를 수행한다 그러나 디바이스에서 처리하는 통계는 커널 내의 프로토콜 스택. TCP/IP
또한 사용해야 하므로 미리 약속되어진 통계에 관한 자료구조가 필요하다.
그래서 커널에는 네트워크 디바이스가 처리해야할 네트워킹 통계에 관한 구조체가 존재한
다 그 구조체는 파일에 선언되어 있는 구조. include/linux/netdevice.h net_device_stats
체이다 이 구조체는 총 개의 필드를 가지고 있으며 이에 대한 상세한 설명은 코드. 23 [ 13]
과 같다 또한 이 구조체는 일반적으로 디바이스 자체 관리용 구조체 안에 멤버 변수로 선.
언되어 사용된다.
struct net_device_stats
{
수신된 총 패킷 수unsigned long rx_packets; /* */
송신한 총 패킷 수unsigned long tx_packets; /* */
수신된 패킷의 총 바이트 수unsigned long rx_bytes; /* */
송신한 패킷의 총 바이트 수unsigned long tx_bytes; /* */
수신시 에러난 패킷의 수unsigned long rx_errors; /* */
송신시 에러난 패킷의 수unsigned long tx_errors; /* */
수신버퍼에 공간이 없어 버려진 패킷 수unsigned long rx_dropped; /* */
송신버퍼에 공간이 없어 버려진 패킷 수unsigned long tx_dropped; /* */
수신된 멀티캐스트 패킷 수unsigned long multicast; /* */
패킷 충돌이 발생한 횟수unsigned long collisions; /* */
수신에러 필드에 대한 자세한 정보/* rx_errors */
패킷길이 에러의 횟수unsigned long rx_length_errors; /* */
수신 버퍼 오버플로우 에러의 횟수unsigned long rx_over_errors; /* */
에러의 횟수unsigned long rx_crc_errors; /* CRC */
프레임 에러의 횟수unsigned long rx_frame_errors; /* */
디바이스 버퍼 오버플로우 에러의 횟수unsigned long rx_fifo_errors; /* */
수신되지 않은 패킷의 수unsigned long rx_missed_errors; /* */
송신에러 필드에 대한 자세한 정보/* tx_errors */
송신 취소가 되어 발생한 에러의 횟수unsigned long tx_aborted_errors; /* */
- 49 -
앞서 살펴보았듯이 구조체에는 통계를 관리하는 함수를 등록하는net_device get_stats
함수 포인터 필드가 있다 이 필드에는 에서 가. cs8900_1_probe() cs8900_1_get_stats()
등록되어 있다 이 함수에 대한 코드는 코드 과 같다 이 함수는 단순히 디바이스. [ 1-11] .
자체 관리용 구조체인 변수에서 구조체 변수인 필드를 반환한priv net_device_stats stats
다.
static struct net_device_stats *cs8900_1_get_stats (struct net_device *dev) {
cs8900_1_t *priv = (cs8900_1_t *) dev->priv;
return (&priv->stats);
}
코드 디바이스 드라이버의 통계 처리 함수[ 1-11] cs8900_1_get_stats()
unsigned long tx_carrier_errors; 캐리어가 감지되지 않아 발생한 에러의 횟수/* */
디바이스 버퍼 오버플로우 에러의 횟수unsigned long tx_fifo_errors; /* */
unsigned long tx_heartbeat_errors;
unsigned long tx_window_errors;
/* for cslip etc */
unsigned long rx_compressed;
unsigned long tx_compressed;
};
코드 통계 처리 관련 구조체[ 13] net_device_stats
- 50 -
결론III.
분석 결과1.
여러 통계적 자료를 통해 의 시장의 성장률이 크게 증가하고 있음을 확인할 수 있으PDA
며 그에 맞는 의 개발의 중요성도 높아지고 있다 현재는 대 운영체제간의 경쟁PDA OS . 3
에서 팜 가 선두를 유지하고 있으며 그 가운데 리눅스 운영체제는 공개성과 안정성을OS ,
바탕으로 제 의 운영체제로 자리 잡을 것으로 예상된다 리눅스가 아직 용 표준이 마3 . PDA
련되지 않았다는 문제점을 가지고 있지만 시장조사회사 가트너의 한 분석가는 소비“PDA
자는 기능만 충족되면 어떤 가 탑재돼있든 관심을 보이지 않는다 면서 세계적으로OS .” “
모바일기기용 리눅스프로그램 개발자들의 모임이 수천 개에 달할 정도로 용 리눅스PDA
는 프로그램 숫자만으로 및 의 와 겨룰 수 있을 것 으로 전망했다OS Palm MS OS ” .
디바이스 드라이버와 커널에 관하여 여러 이름 있는 웹사이트 저서들을 대부분 커널 또,
는 디바이스 드라이버만을 다룬다 그러나 디바이스 드라이버와 커널은 서로 뗄 수 없는 관.
계로 디바이스 드라이버의 작성방법은 리눅스 커널의 구현과 밀접한 관계가 있다 본 논문.
은 네트워크 디바이스 드라이버의 작성방법에 한 하여 커널과 밀접한 관계를 논하려고 노력
하였다 따라서 리눅스의 심화적인 이해를 통해 디바이스 드라이버의 제작 기술을 습득하고.
더불어 의 코드를 읽어 이해할 수 있는 능력을 배양한다OS .
- 51 -
참 고 문 헌■
도서1.
이상근 역 임베디드 개발자를 위한 리눅스 커널 심층 분석 에이콘출판사[1] , “ ”, , 2004
심마로 역 리눅스 커널의 이해 한빛미디어[2] , “ ”, , 2003
유영창 리눅스 디바이스 드라이버 한및미디어[3] , “ ”, , 2004
박재호 임베디드 리눅스 한빛미디어[4] , “IT EXPERT ”, , 2003
조유근 외 리눅스 매니아를 위한 커널 프로그래밍 교학사[5] , “ ”, , 2002
외[6] Jonathan , “Linux Device Driver 3rd”, O'REILLY, 2005
웹 사이트1.
코어벨[1] , (www.corebell.co.kr)
[2] Korea Embedded Linux Project, (kelp.or.kr/korweblog)
[3] Cross-Referencing Linux, (http://lxr.linux.no/)
[4] Korea Linux Documents Project, (kldp.org)
[5] KorOne.net, (www.korone.net)
월간 임베디드 월드[6] , (www.embeddedworld.co.kr)
하이버스[7] , (www.hybus.net)
한백전자[8] , (www.hanback.co.kr)
휴인스[9] , (www.huins.com)
[10] FA. Linux (www.falinux.com)
[11] Red Hat, (www.redhat.com)
[12] GNU official homepage, (www.gnu.org)

Weitere ähnliche Inhalte

Was ist angesagt?

파일시스템 관련 명령어
파일시스템 관련 명령어파일시스템 관련 명령어
파일시스템 관련 명령어Chulgyu Shin
 
07 스레드스케줄링,우선순위,그리고선호도
07 스레드스케줄링,우선순위,그리고선호도07 스레드스케줄링,우선순위,그리고선호도
07 스레드스케줄링,우선순위,그리고선호도ssuser3fb17c
 
데이터베이스 설계 및 구현 2014-2018년 기말시험 기출문제
데이터베이스 설계 및 구현 2014-2018년 기말시험 기출문제데이터베이스 설계 및 구현 2014-2018년 기말시험 기출문제
데이터베이스 설계 및 구현 2014-2018년 기말시험 기출문제Lee Sang-Ho
 
10 동기및비동기장치io
10 동기및비동기장치io10 동기및비동기장치io
10 동기및비동기장치iossuser3fb17c
 
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은jieun kim
 
우분투에 시스템콜 추가하기
우분투에 시스템콜 추가하기우분투에 시스템콜 추가하기
우분투에 시스템콜 추가하기Hoyoung Jung
 
ITs 2주차_기본명령어(발표)
ITs 2주차_기본명령어(발표)ITs 2주차_기본명령어(발표)
ITs 2주차_기본명령어(발표)Chulgyu Shin
 
Free rtos seminar
Free rtos seminarFree rtos seminar
Free rtos seminarCho Daniel
 

Was ist angesagt? (9)

파일시스템 관련 명령어
파일시스템 관련 명령어파일시스템 관련 명령어
파일시스템 관련 명령어
 
07 스레드스케줄링,우선순위,그리고선호도
07 스레드스케줄링,우선순위,그리고선호도07 스레드스케줄링,우선순위,그리고선호도
07 스레드스케줄링,우선순위,그리고선호도
 
데이터베이스 설계 및 구현 2014-2018년 기말시험 기출문제
데이터베이스 설계 및 구현 2014-2018년 기말시험 기출문제데이터베이스 설계 및 구현 2014-2018년 기말시험 기출문제
데이터베이스 설계 및 구현 2014-2018년 기말시험 기출문제
 
10 동기및비동기장치io
10 동기및비동기장치io10 동기및비동기장치io
10 동기및비동기장치io
 
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
 
우분투에 시스템콜 추가하기
우분투에 시스템콜 추가하기우분투에 시스템콜 추가하기
우분투에 시스템콜 추가하기
 
ITs 2주차_기본명령어(발표)
ITs 2주차_기본명령어(발표)ITs 2주차_기본명령어(발표)
ITs 2주차_기본명령어(발표)
 
Free rtos seminar
Free rtos seminarFree rtos seminar
Free rtos seminar
 
linux1
linux1linux1
linux1
 

Ähnlich wie ice_grad

Hyperledger farbric build your first network install and analysis
Hyperledger farbric   build your first network install and analysisHyperledger farbric   build your first network install and analysis
Hyperledger farbric build your first network install and analysis병준 김
 
소켓프로그래밍 기초요약
소켓프로그래밍 기초요약소켓프로그래밍 기초요약
소켓프로그래밍 기초요약세빈 정
 
UNIX 시스템 2014-2018년 기말시험 기출문제
UNIX 시스템 2014-2018년 기말시험 기출문제UNIX 시스템 2014-2018년 기말시험 기출문제
UNIX 시스템 2014-2018년 기말시험 기출문제Lee Sang-Ho
 
윈도우 커널 익스플로잇
윈도우 커널 익스플로잇윈도우 커널 익스플로잇
윈도우 커널 익스플로잇Seungyong Lee
 
파이썬 병렬프로그래밍
파이썬 병렬프로그래밍파이썬 병렬프로그래밍
파이썬 병렬프로그래밍Yong Joon Moon
 
Java rmi 개발 가이드
Java rmi 개발 가이드Java rmi 개발 가이드
Java rmi 개발 가이드중선 곽
 
Ch01 네트워크와+소켓+프로그래밍+[호환+모드]
Ch01 네트워크와+소켓+프로그래밍+[호환+모드]Ch01 네트워크와+소켓+프로그래밍+[호환+모드]
Ch01 네트워크와+소켓+프로그래밍+[호환+모드]지환 김
 
리눅스 소켓 프로그래밍 기초
리눅스 소켓 프로그래밍 기초리눅스 소켓 프로그래밍 기초
리눅스 소켓 프로그래밍 기초Yu Yongwoo
 
Python으로 채팅 구현하기
Python으로 채팅 구현하기Python으로 채팅 구현하기
Python으로 채팅 구현하기Tae Young Lee
 
Interface and Protocol
Interface and ProtocolInterface and Protocol
Interface and ProtocolWonjun Hwang
 
손쉬운 데이터 연결 방법(라이브바인딩 활용)
손쉬운 데이터 연결 방법(라이브바인딩 활용)손쉬운 데이터 연결 방법(라이브바인딩 활용)
손쉬운 데이터 연결 방법(라이브바인딩 활용)Devgear
 
Implementing remote procedure calls rev2
Implementing remote procedure calls rev2Implementing remote procedure calls rev2
Implementing remote procedure calls rev2Sung-jae Park
 
세션1. block chain as a platform
세션1. block chain as a platform세션1. block chain as a platform
세션1. block chain as a platformJay JH Park
 
C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발흥배 최
 
2006 03 15_pe & api hook
2006 03 15_pe & api hook2006 03 15_pe & api hook
2006 03 15_pe & api hook용환 노
 
Windows via c++ chapter6
Windows via c++   chapter6Windows via c++   chapter6
Windows via c++ chapter6Shin heemin
 
ROS SERIAL and OpenCR
ROS SERIAL and OpenCRROS SERIAL and OpenCR
ROS SERIAL and OpenCRYoonseok Pyo
 
Multithread pattern 소개
Multithread pattern 소개Multithread pattern 소개
Multithread pattern 소개Sunghyouk Bae
 
caanoo Device driver
caanoo Device drivercaanoo Device driver
caanoo Device driverjumiss
 
Introduction to Parallel Programming
Introduction to Parallel ProgrammingIntroduction to Parallel Programming
Introduction to Parallel ProgrammingUNIST
 

Ähnlich wie ice_grad (20)

Hyperledger farbric build your first network install and analysis
Hyperledger farbric   build your first network install and analysisHyperledger farbric   build your first network install and analysis
Hyperledger farbric build your first network install and analysis
 
소켓프로그래밍 기초요약
소켓프로그래밍 기초요약소켓프로그래밍 기초요약
소켓프로그래밍 기초요약
 
UNIX 시스템 2014-2018년 기말시험 기출문제
UNIX 시스템 2014-2018년 기말시험 기출문제UNIX 시스템 2014-2018년 기말시험 기출문제
UNIX 시스템 2014-2018년 기말시험 기출문제
 
윈도우 커널 익스플로잇
윈도우 커널 익스플로잇윈도우 커널 익스플로잇
윈도우 커널 익스플로잇
 
파이썬 병렬프로그래밍
파이썬 병렬프로그래밍파이썬 병렬프로그래밍
파이썬 병렬프로그래밍
 
Java rmi 개발 가이드
Java rmi 개발 가이드Java rmi 개발 가이드
Java rmi 개발 가이드
 
Ch01 네트워크와+소켓+프로그래밍+[호환+모드]
Ch01 네트워크와+소켓+프로그래밍+[호환+모드]Ch01 네트워크와+소켓+프로그래밍+[호환+모드]
Ch01 네트워크와+소켓+프로그래밍+[호환+모드]
 
리눅스 소켓 프로그래밍 기초
리눅스 소켓 프로그래밍 기초리눅스 소켓 프로그래밍 기초
리눅스 소켓 프로그래밍 기초
 
Python으로 채팅 구현하기
Python으로 채팅 구현하기Python으로 채팅 구현하기
Python으로 채팅 구현하기
 
Interface and Protocol
Interface and ProtocolInterface and Protocol
Interface and Protocol
 
손쉬운 데이터 연결 방법(라이브바인딩 활용)
손쉬운 데이터 연결 방법(라이브바인딩 활용)손쉬운 데이터 연결 방법(라이브바인딩 활용)
손쉬운 데이터 연결 방법(라이브바인딩 활용)
 
Implementing remote procedure calls rev2
Implementing remote procedure calls rev2Implementing remote procedure calls rev2
Implementing remote procedure calls rev2
 
세션1. block chain as a platform
세션1. block chain as a platform세션1. block chain as a platform
세션1. block chain as a platform
 
C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발
 
2006 03 15_pe & api hook
2006 03 15_pe & api hook2006 03 15_pe & api hook
2006 03 15_pe & api hook
 
Windows via c++ chapter6
Windows via c++   chapter6Windows via c++   chapter6
Windows via c++ chapter6
 
ROS SERIAL and OpenCR
ROS SERIAL and OpenCRROS SERIAL and OpenCR
ROS SERIAL and OpenCR
 
Multithread pattern 소개
Multithread pattern 소개Multithread pattern 소개
Multithread pattern 소개
 
caanoo Device driver
caanoo Device drivercaanoo Device driver
caanoo Device driver
 
Introduction to Parallel Programming
Introduction to Parallel ProgrammingIntroduction to Parallel Programming
Introduction to Parallel Programming
 

Mehr von NakCheon Jung

Mehr von NakCheon Jung (7)

technical report
technical reporttechnical report
technical report
 
2005_Structures and functions of Makefile
2005_Structures and functions of Makefile2005_Structures and functions of Makefile
2005_Structures and functions of Makefile
 
2004_P2P chatting program
2004_P2P chatting program2004_P2P chatting program
2004_P2P chatting program
 
programming with GDB
programming with GDBprogramming with GDB
programming with GDB
 
KCC2007_VoIP
KCC2007_VoIPKCC2007_VoIP
KCC2007_VoIP
 
master_thesis
master_thesismaster_thesis
master_thesis
 
ce_grad
ce_gradce_grad
ce_grad
 

ice_grad

  • 1. 學士學位論文 임 민 중指導敎授 東國大學校 情報産業大學 情報通信專攻 정 낙 천 년 월2005 11
  • 2. 學士學位論文 정 낙 천 임 민 중指導敎授 이 을 으로 함.論文 學士學位論文 提出 2005 11 18年 月 日 의 을 함鄭樂泉 情報通信學 學士學位論文 認准 2005 11 18年 月 日 委員長 委 員 委 員 東國大學校 情報産業大學 情報通信專攻
  • 3. 요 약- - 리눅스의 네트워킹 디바이스 드라이버의 작성 방법은 규칙처럼 정해져있다 그 이유는 블. 록과 문자 디바이스 드라이버와는 다르게 중간에 스택을 거치기 때문이다TCP/IP . TCP/IP 커널 스택은 리눅스의 가상 파일 시스템 안에 숨겨져 있으며 소켓, BSD , INET, 계층으로 세분화 되어 있으며 각 계층을 대표하는 구조체와 그TCP/UDP, Link, IP, ARP , 구조체의 연산을 수행하는 구조체로 스택을 구현하고 있다 통신시 데이터의TCP/IP . TCP 전송과 수신은 소프트 라는 인터럽트 서비스를 통해서 이루어지며 각 계층별로 맵핑된IRQ , 함수들이 호출되어 네트워크 디바이스로 전달되거나 수신된다 네트워크 디바이스 드라이버. 를 작성하려면 구조체의 필드들을 정확히 이해하고 있어야 하며 모듈의 등록net_device , 및 초기화와 제거 데이터의 송수신 인터럽트 처리 통계처리를 기본적으로 구현해야 한다, , , . 목 차- - 서론I. 연구 배경 설명1. 본론II. 리눅스의 네트워킹1. TCP/IP 리눅스 네트워킹 자료구조1) 리눅스 네트워킹 제어흐름2) 데이터 전송 제어흐름i) 데이터 수신 제어흐름ii) 리눅스의 네트워크 제어2. 1) ifconfig 2) /proc/net/dev 와3) insmod rmmod 네트워크 디바이스 드라이버3. 네트워크 디바이스의 종류1) 네트워크 디바이스 드라이버 구현2)
  • 4. 구조체 의 이해i) net_device 상태나 제어를 나타내는 필드a. 함수 포인터 필드b. 네트워크 디바이스 드라이버의 초기화와 제거ii) 커널에 디바이스 드라이버를 등록 또는 제거a. 하드웨어 검출 및 하드웨어 초기화b. 네트워크 디바이스의 열기iii) 네트워크 디바이스의 닫기iV) 네트워크 디바이스에서의 패킷 전송V) 패킷의 전송처리a. 전송 시간 초과 시의 처리b. 인터럽트 처리c. 네트워크 디바이스에서의 패킷 수신Vi) 인터럽트 처리a. 수신 처리 함수b. 네트워크 디바이스의 통계 처리Vii) 결론III. 분석 결과1. 참고문헌■ 도서1. 웹 사이트2.
  • 5. - 5 - 서론I. 연구 배경 설명1. 리눅스의 네트워킹 디바이스 드라이버의 작성 방법은 규칙처럼 정해져있다 그 이유는 블. 록과 문자 디바이스 드라이버와는 다르게 중간에 스택을 거치기 때문이다 네트워TCP/IP . 크 디바이스 드라이버의 작성원리를 이해하기 위하여 커널 스택의 내부 구조와 동TCP/IP 작 디바이스 드라이버를 작성하기 위한 기법과 도구를 연구한다, . 본론II. 리눅스의 네트워킹1. TCP/IP 그림 은 리눅스의 작동 구조 중에서 네트워킹 부분을 확대하여 표현한 것이다 실제[ 1] . 리눅스 커널은 그림 의 구조와 일치되게 구현되어 있다 리눅스 네트워킹 구조에서 가장[ 1] . 상위에 있는 소켓 계층은 시스템의 인터페이스를 지원하기 위해서 존재한다 사용자BSD . 프로그램은 소켓 라이브러리를 통해서 시스템 함수 호출을 하며 이렇게 발생된 시스템 함, 수 호출은 소켓 계층으로 제어가 넘겨진다 리눅스는 계열의 소켓 인터페이스와BSD . BSD 똑같은 인터페이스를 가지고 있으며 기존에 만들어진 프로그램들에 대한 지원이기도 하다, . 그래서 소켓 계층으로 이름이 지어진 것이다BSD . 소켓 계층은 에 기반을 두는 나 프로토콜의 통신을 관리하는 역할을INET IP TCP UDP 한다 와 계층에서는 사용자로부터 전달 받은 데이터를 실제의 패킷을 나타내는. TCP UDP 특수한 구조체의 형태로 만들어 담고 이를 하위의 계층으로 전달하는 역할을 한다 즉, IP . , 하위의 계층에는 이 계층에서 만들어진 데이터를 단순히 보내는 역할만 한다 반대로 데이. 터를 받을 때는 반대의 역할을 한다. 계층은 상위에서 만들어진 데이터를 보내는 것과 하위에서 받은 데이터를 상위계층으IP 로 올려주는 역할을 한다 이때 보내기 위한 데이터의 재조합이 일어나며 이때 받은 데이. , 터의 내용은 건들지 않는다 다만 자신을 위한 헤더나 에러를 검사할 수 있는. CRC(Cyclic 를 더하는 과정이 있다 계층과 계층 사이에 존재하는Redundancy Check) . Link IP ARP 계층은 엄밀히 말해서 계열에 속하는 것으로 계층의 이더넷 프로토콜과 연동하여IP Link 동작할 때에만 계층에서 작동한다IP . 계층은 상위에서 받은 패킷에 헤더와 트레일러를 붙여 완성하여 프레임으로 만들고Link , 네트워크 디바이스 드라이버와 프로토콜 스택사이에 완성된 프레임을 전송하기 위해 큐와
  • 6. - 6 - 인터럽트 메커니즘을 제공하고 있다 이때 프레임은 상위에서 이더넷 등의. , PPP, SLIP 계층에서 동작하는 적당한 프로토콜을 정하여 내려와서 만들어진다Data link . 계층이 바로 장치와 디바이스 드라이버가 작용하여 프레임을 전송하는 역할을Physical 한다 이 부분은 장치를 생산하는 회사에서 디바이스 드라이버를 작성하여 제공하는 부분이. 다 그러나 네트워크 디바이스 드라이버는 항상 물리적인 장치만을 의미하진 않는다 일례. . 로 루프백 은 순수하게 소프트웨어로만 작성된 장치이다 또한 네트워크 디바이(loop-back) . 스 드라이버는 로 접근할 수 있는 다른 장치와는 달리 전용 디바이스가 설치되어 장mknod 치를 인식해야 사용할 수 있다 실제로 리눅스에서는 시스템에 설치된 알맞은 디바이스 드. 라이버를 커널에 포함하여 컴파일해야 파일을 볼 수 있다/dev/eth0 . 그림 리눅스의 네트워킹 구현 블록 다이어그램[ 1] 리눅스 네트워킹 자료구조1) 그림 는 리눅스에서 네트워킹을 지원하기 위하여 사용하는 자료구조를 보여준다 이것[ 2] . 은 그림 의 상자 안의 각 레이어별로 구분하여 순서대로 위에서부터 아[ 1] 'Kernel mode' 래로 번호를 매겨 표현하고 있다 또한 소켓 버퍼 라 불리는 각 계층별 데이. (socket buffer) 터를 표현하는 자료구조도 같이 포함 하였다. 리눅스 네트워킹 코드는 소켓 버퍼 라는 큰 메모리 영역에 패킷들을 유지(socket buffer)
  • 7. - 7 - 한다 각각의 소켓 버퍼에는 디스크립터가 대응한다 디스크립터는 구조체로 구현. . sk_buff 하고 있으며 이에 대한 자료구조를 그림 의 가운데 에 표현하[ 2] Networking Data Buffer 였다. 구조체는 수많은 필드들을 담고 있지만 주요 자료구조로는 소켓버퍼 사용자 데sk_buff , 이터 데이터 링크 계층 트레일러 소켓 네트워크 장치의 객체 전송 계, , INET , net_device , 층 헤더의 디스크립터 네트워크 계층 헤더의 디스크립터 데이터 링크 계층 헤더의 디스크, , 립터 목적지 캐시 엔트리를 담고 있다 이들은 네트워킹을 구현하는데 필요한 모든 정보를, . 담은 구조체의 받은 패킷 보내는 패킷 에러가 발생한 패킷을 담는 큐 안에 이중연결sock , , 구조로 이루어져있다 즉 구조체는 하나의 패킷을 나타내고 있다. sk_buff_head . 또한 구조체는 구조체 안에서만 작동하는 것이 아니다 일반적으로 커널은sk_buff sock . 데이터 복사를 하지 않으며 간단히 소켓 버퍼 디스크립터 포인터를 각 네트워킹, sk_buff 계층에 차례로 전달한다 일례로 패킷을 보내려고 준비하는 과정에서 전송계층. (Transport 은 페이로드를 사용자 모드 버퍼에서 소켓 버퍼의 뒷부분에 복사한다 전송 계층은Layer) . 페이로드 앞에 나 헤더를 추가한다 다음으로 네트워크 계층으로 넘어가서 소켓TCP UDP . , 버퍼 디스크립터를 전달받아 헤더를 전송 헤더 앞에 추가한다 끝으로 데이터 링크 계층IP . 이 헤더와 트레일러를 추가하고 패킷을 전송하기 위해 큐에 넣는다. 리눅스는 블록 디바이스 드라이버와 마찬가지로 네트워킹에 대한 연산 또한 가상 파일 시 스템 개념을 적용하고 있다 일례로 네트워크 통신을 위해서 함수로 소켓을 생성. socket() 하면 리눅스 커널은 프로세스가 가지고 있는 가장 큰 파일 디스크립터에 을 더한 정수형1 디스크립터만을 가져온다 이러한 디스크립터는 그림 와 같이 파일 디스크립터를 나타내. [ 2] 는 계층의 자료구조의 배열의 값이며 생성된 소켓 파일은VFS files_struct fd_array , file 구조체에 매핑 되어 하나의 파일처럼 작동하는 것이다 구조체에서는 필드를 두어. file f_op 리눅스가 가상 파일 시스템으로 작동할 수 있게 한다 일례로 사용자가 소켓 파일을 열면. 리눅스 코드는 필드에 구조체를 대입하여 하위의 네트워킹 코드들과f_op sock_files_ops 연결시켜준다 또한 다른 일례로 사용자가 파일을 마운트할 경우에는 리눅스 코드는. ntfs 필드에 구조체를 디렉터리를 마운트할 경우에는 멤버f_op ntfs_file_operations , ntfs f_op 에 구조체를 대입하여 파일시스템을 사용 할 수 있게 한다ntfs_dir_operations ntfs . 리눅스 네트워킹 코드 또한 가상 파일시스템과 비슷하게 각 계층마다 필요한 연산들을 끼 고 뺄 수 있게 연산에 대한 추상화를 하였다 즉 하위 층에서 제공하는 함수를 상위 층에. , 서 이용할 때 상위층에서 직접 이 함수를 호출하는 것이 아니라 하위 층이 자신이 제공하는 함수의 시작 주소를 특정 자료구조에 등록하고 상위 층에서는 단지 자료구조에 등록된 함, 수를 호출하는 방식으로 하위 층의 함수를 간접 호출하는 방식으로 구현한 것이다. 그림 에서 보듯이 계층에서는 구조체가 하위 계층으로 진입하기위[ 2] INET proto_ops 한 추상화된 연산을 정의하고 있으며 이때 의 연산을 수행하려면 이 구조체에, ipv4 TCP 구조체를 대입하면 된다 계층에서는 상위 계층에서 하위 계층inet_stream_ops . TCP/UDP
  • 8. - 8 - 으로 데이터가 이동할 때와 하위 계층에서 상위 계층으로 데이터가 이동할 때 사용하는 자 료구조가 틀리다 그것은 계층과 계층에 여러 프로토콜이 존재하기 때문이다. IP TCP/UDP . 상위 계층으로부터의 데이터 이동을 위해서는 구조체를 사용하면 되고 하위 계층으proto , 로부터의 데이터 이동을 위해서는 구조체를 사용하면 된다 계층에서는inet_protocol . IP 상위 계층으로부터의 데이터 이동을 위해서 구조체를 사용하였으며 하위 계층으tcp_func , 로 부터의 데이터 이동은 구조체를 사용하였다 여기서 는 사용자가packet_type . tcp_func 소켓을 생성하였을 때 사용되는 구조이다 만약 사용자가 소켓을 사용한다면TCP . RAW 소켓 계층의 구조체의 필드에서 구조체를 바탕으로 계층의BSD sock tp_pinfo tp_raw IP 주요 자료구조가 바뀔 것이다 이러한 구조로 리눅스는 제어 흐름이 다양한 곳으로 분기할. 수 있는 상황을 효과적으로 지원할 수 있는 것이다. 그림 의 번 계층은 모든 상황에 쓰이지는 않지만 현재 대부분의 네트워[ 2] 4.5 IP(ARP) , 킹이 이더넷으로 이루어져 있기 때문에 포함하였다 여기서는 계층에서 수행하는 라우팅. IP 을 위한 보조적인 역할을 담당하고 있다 계층으로 가면 구조체가 쓰이고. Link net_device 있는데 이 구조체는 네트워크 디바이스 드라이버 코드를 작성하기 위해서도 필요한 구조체 이다 이 구조체는 계층에서 단독으로 사용하는 큐인 자료구조와 네트워크 디바. Link qdisc 이스 드라이버가 사용할 함수를 정의한 포인터를 가지고 있다 사용자 응용 프로그램에서. 호출한 시스템 함수의 콜은 이 자료구조에 등록된 디바이스 드라이버의 함수가 궁극적으로 호출된다.
  • 10. - 10 - 리눅스 네트워킹 제어흐름2) 그림 은 리눅스의 여러 분기 중에서 통신시의 제어 흐름을 나타낸 블록 다이어[ 3] TCP 그램이다 여기서 왼쪽의 세로로 된 두 줄의 흐름은 데이터를 송신시의 흐름을 꺾인 화살표. 의 흐름으로 오른쪽의 두 줄은 데이터 수신시의 흐름을 색칠한 세모모양의 화살표의 흐름, 으로 나타냈다 가운데는 데이터의 송신과 수신시 커널에 인터럽트 등록하는 메커니즘과 커. 널의 인터럽트 수행 메커니즘을 나타낸 것이다 그림 에서는 호출 과정에 있는 함수의. [ 3] 이름을 연주황의 타원 안에 다음 단계로 넘어가는데 중요한 역할을 하는 함수의 코드를 초, 록색 상자 안에 적어놓았다 또한 함수이름이 없고 하얀 상자에 있는 코드들은 다음 계층으. 로 넘어가기 위해 추상화를 한 부분마다 존재하는데 이들은 다음 계층의 어떤 함수를 찾아, 갈 것인지를 명확하게 해준다 또한 네모 상자에 들어간 함수들은 매크로 함수를 표현하였. 다. 데이터 전송 제어흐름i) 소켓이 만들어지면 사용자 프로그램의 는 커널의 를 이용하여 데이터write() sys_write() 전송을 요청한다 는 의 일부로서 구조체를 가져와서 소켓용 파일 연. sys_write() VFS file 산 구조체에 등록된 함수를 호출한다 소켓 계층은 먼저sock_write() . BSD sock_write() 를 통하여 상위 계층에서 받은 제어 메시지를 처리하고 이상이 없으면 를sock_sendmsg() 호출하여 계층의 연산 함수인 를 구조체에서 찾아서 호출한다 현INET sendmsg() proto . 재의 통신 프로토콜은 이므로 계층은 먼저 을 사용하는 프로토콜 계열TCP , INET stream 의 연산을 위한 구조체에 등록된 가 실행된다 계층은 데이터의 특inet_semdmsg() . INET 별한 처리 없이 바로 프로토콜 연산을 위한 구조체에 등록된 를 호출TCP tcp_sendmsg() 한다. 계층의 주목적은 로 사용자로부터 넘겨받은 데이터를 소켓버TCP/UDP tcp_sendmsg() 퍼 형태로 만들고 를 거쳐 실제 전송을 시작하는 역tcp_push_one(), tcp_transmit_skb() 할을 한다 는 전송할 데이터를 버퍼 큐에 넣을 것인지 아니면 바로 보낼. tcp_push_one() 것인지를 결정한다 는 프로토콜이 사용하는 각종 필드들을 소켓. tcp_transmit_skb() TCP 버퍼에 채워 넣고 계층의 를 호출한다IP queue_xmit() . 계층의 프로토콜 연산을 지원하는 함수는 구조체에 정의되어 있IP TCP ipv4_specific 다 여기서 계층에서 호출한 는 이다 계층은. TCP/UDP queue_xmit() ip_queue_xmit() . IP 넘겨받은 소켓버퍼를 디바이스에 맞게 다듬어서 보내는 역할을 한다 따라서 넘겨받은 소켓. 버퍼를 합치거나 나누는 일이 발생한다 먼저 는 넘겨받은 소켓버퍼의 정. ip_queue_xmit() 보들을 검사하여 함수 호출 흐름을 결정하고 매크로를 거쳐서NF_HOOK() 를 호출하여 실제로 패킷을 자르 거나 조립ip_queue_xmit2() (segmemtation) (assembly)
  • 11. - 11 - 또는 재조립 을 수행한다(reassembly) . 계층은 앞부분의 계층이 잡은 방향을 토대로 소켓버퍼에 필요한 정보들을IP(ARP) IP 채워 넣는 작업을 한다 그리고 를 거. ip_output(), ip_finish_output() , ip_finish_output2() 치면서 라우팅 테이블을 검색하고 필요에 따라서 의 함수들을 호출하여 계층의ARP Link 이나 를 호출한다output() hh_output() . 계층은 자료구조에 의해서 궁극적으로 를 호출Link arp_direct_ops dev_queue_xmit() 하게 된다 이 함수는 큐를 사용하여 패킷을 전송할 것인지 아니면 바로 패킷을 보낼 것인. 지 결정하고 그에 따라서 적당한 함수를 부르는 일을 수행한다 먼저 패킷을 바로 보낼 것. 인지를 결정하면 는 번 경로를 따라서 실제 네트워크 디바이스 드라이dev_queue_xmit() 2 버의 를 호출하여 패킷을 바로 보내게 된다hard_xtart_xmit() . 의 번 경로는 네트워킹에서 큐 메커니즘을 제공하는 구조체를dev_queue_xmit() 1 qdisc 이용한다 가장 먼저 실행되는 는 여러 조건을 검사하여 패킷을 큐에 넣는다. qdisc_run() . 큐에 들어간 패킷이 큐에서 빠져나와서 전송되려면 를 거쳐야 하는데 먼저qdisc_restart() , 의 번 경로와 같이 패킷을 큐에서 빼서 디바이스 드라이버로 패킷 전송을qdisc_restart() 1 시도한다 데이터 전송 시에는 패킷을 전송할 준비를 미리 할 수 있기 때문에 인터럽트를. 하지 않는다 그러나 리눅스에서는 만약 디바이스가 바쁘거나 첫 번째 패킷 전송에 실패하. 였을 경우에는 의 번 경로와 같이 와qdisc_restart() 2 netif_schedule() __netif_schedule() 를 이용하여 소프트웨어 인터럽트 요청을 한다. 리눅스에서의 소프트웨어 인터럽트 과정은 크게 두 가지로 나눌 수 있다 먼저 인터럽트. 요청을 하고자 하는 프로세스는 커널내의 인터럽트 벡터 테이블에 등록을 해두는 것이다. 그 다음에 커널의 인터럽트 서비스를 수행하는 스레드가 정기적으로 인터럽트 벡터 테이블 을 살펴보아 등록된 인터럽트 요청이 있으면 그 요청을 수행해주는 방법을 사용한다. 먼저 인터럽트를 요청할 프로세스는 를 호출한다 이 함수는 그림cpu_raise_softirq() . [ 에서 보는바와 같이 의 세 매3] __cpu_raise_softirq(), softirq_pending(), __IRQ_STAT() 크로를 통해서 인터럽트 벡터 테이블에 등록한다 그리고 로 불irq_stat[] . skoftirqd_CPUn 리는 소프트웨어 인터럽트 서비스를 수행하는 커널 스레드는 실행되면 의 무한ksoftirqd() 문 을 통하여 인터럽트 테이블을 검사한다 는 안에서 호출for . skoftirqd_CPUn ksoftirqd() 하는 로 인터럽트 서비스 요청을 감지하면 바로 를 수행하는softirq_pending() do_softirq() 데 이 함수는 해당 인터럽트 요청에 등록된 인터럽트를 수행할 함수를 수행해주는 역할을, 한다 는 와 관련된 함수를 호출할 것이다 패킷을. do_softirq() NET_TX_SOFTIRQ action . 받을 때 호출되는 함수는 이다action net_tx_action() . 데이터의 네트워킹 전송에 처음 네트워크 디바이스 드라이버가 커널에 등록되면 가 호출이 되는데 이 함수에서 전송과 수신용의 소프트웨어 인터럽트를 수net_dev_init() , 행할 의 커널함수 개를 에net_tx_action(), net_rx_action() 2 open_softirq() 와 플래그를 사용하여 등록하고 있다 참고로NET_TX_SOFTIRQ NET_RX_SOFTIRQ .
  • 12. - 12 - 는 네트워크 디바이스를 통해 패킷을 전송함을 의미하며 인덱스는 을NET_TX_SOFTIRQ 1 , 는 네트워크 디바이스에서 패킷을 수신함을 의미하며 인덱스는 이다NET_RX_SOFTIRQ 2 . 인덱스는 우선순위를 의미하며 값이 적을수록 우선순위가 높다는 것을 의미한다. 결론적으로 이러한 인터럽트 메커니즘에 의해 수행되는 는 다시net_tx_action() 함수를 호출하여 다시 네트워크 디바이스로 부터의 패킷전송을 시도한다qdisc_run() . 데이터 수신 제어흐름ii) 데이터 수신시의 네트워킹 제어 흐름은 가장 최상위의 계층과 최하위의VFS Physical 계층의 두 곳에서 시작하여 계층과 계층 두 곳에서 만난다 그림 의TCP/UDP , INET . [ 3] 오른쪽 그림들에서 음영 처리되어 계층을 표현하는 상자가 있다 상위 계층에서 시작함과. 하위계층에서 시작함을 구분하기 위해서 표시하였다 음영 처리된 계층은 상위 계층에서 시. 작하는 것을 나타내고 음영 처리가 되지 않은 계층은 하위 계층에서 시작함을 의미한다, . 네트워크 디바이스 드라이버는 인터럽트를 커널에 등록함으로써 데이터를 받을 수 있는 준비가 완료된다 이 과정은 그림 의 데이터 수신 제어 흐름에서 계층에 나타. [ 3] Physical 나 있다 네트워크 디바이스 드라이버는 안에서 계층에 속하는. interrupt() Link netif_rx() 를 호출하고 는 를 호출하는 일련의 호출 단계를 거쳐서 인, netif_rx() cpu_raise_softirq() 터럽트를 등록한다 그러면 후에 소프트웨어 인터럽트를 처리하는 가 호출될. do_softirq() 때 와 관련된 함수를 호출할 것이다 패킷을 받을 때 호출되는, NET_RX_SOFTIRQ action . 함수는 이다 은 받은 패킷의 데이터를 읽어서 패킷action net_rx_action() . net_rx_action() 타입을 결정하여 계층의 적당한 함수로 전달하는 역할을 수행한다 이 부분 또한 추상화IP . 되어 있어서 계층의 연산을 지원하는 자료구조인 에 정의된 를IP ip_packet_type ip_rcv() 호출한다. 계층의 받은 데이터에 대한 주요 역할은 다음과 같다 함수로 받은 패킷의IP . ip_rcv() 체크섬 버전정보 데이터 길이 등의 필드들을 검사하여 자신의 것이 아니거(checksum), IP , 나 에러가 있는 패킷은 버리는 등의 적당한 행동을 취한다 그 다음 자신의 패킷이거나 에. 러가 없는 패킷은 새로운 소켓 버퍼를 할당하여 프로토콜에 알맞은 필드들의 내용을 채우 고 로 이동하여 라우팅 테이블을 갱신하는 등의 작업을 수행한다 이후에, ip_rcv_finish() . 자신의 패킷만을 받아들여 를 통해서 된 패킷의 재조합ip_local_deliver() fragment IP 을 수행한다 마지막으로 계층은 에서 넘겨받은(reassembly) . IP ip_local_deliver_finish() 소켓 버퍼에서 헤더를 벗겨내어 상위 프로토콜인 을 찾아낸 후 상위 프로IP IP datagram , 토콜정보를 바탕으로 상위 프로토콜에 을 전달한다 이때 데이터 전송을IP datagram . TCP 로 하였기 때문에 여기서도 데이터를 받은 것으로 가정한다 여기서도 프로토콜TCP . TCP 의 연산을 지원하는 함수들이 등록된 자료구조로 계층에서 먼저tcp_protocol TCP/UDP 호출될 함수는 임을 알 수 있다tcp_v4_rcv() .
  • 13. - 13 - 계층의 는 하위 계층으로부터 받은 소켓 버퍼 객체를 분석하여TCP/UDP tcp_v4_rcv() 및 와 에 대한 처리를 한다 그 외의 일반적인 경우에는SYN ACK RST . tcp_v4_do_rcv() 를 호출하는데 이 함수는 소켓의 상태에 따라 달리 처리한다 만약 소켓의 상, INET . INET 태가 이면 소켓의 와 관련된 타이머를 갱신하고TCP_EXTABLISHED , INET TCP 를 호출한 후 복귀한다 만약 소켓의 상태가 이tcp_rcv_establish() . INET TCP_LISTEN 면 를 호출해서 처리를 넘겨주는 등의 상태제어 역할을 하고 있다 현재, tcp_v4_hnd_req() . 의 경우에는 소켓이 이미 연결 상태로 되어 있는 상태이므로 는tcp_v4_do_rcv() 를 호출한다 는 에 대한 처리와 상위 프로토tcp_rcv_establish() . tcp_rcv_establish() ACK 콜 계층에 패킷이 도착했음을 알리는 일을 한다 이를 위해서 사용하는 함수는. 이다 는 계층의 추상화된 를 호출tcp_data_queue() . tcp_data_queue() INET data_ready() 하여 상위 계층으로 제어 흐름을 넘긴다 여기서 받은 소켓 버퍼는 계층으로 전달되. INET 는 것이 아니고 계층에 남아 있는 것이다 그렇기 때문에 사용자가 를 호TCP/UDP . read() 출한 경우에 계층까지 제어 흐름이 내려가는 것이다TCP/UDP . 계층의 추상화된 함수인 는 다른 계층으로의 전환 방법이 프로토콜INET data_ready() 연산을 정의해 놓은 자료구조가 아닌 를 통해서 이루어지고 있다는 것이sock_init_data() 특징적이다 안에서 추상적인 는 실제로. sock_init_data() data_ready() 이다 는 다른 추가적인 작업이 없고 바로sock_def_readable() . sock_def_readable() 를 호출하는데 함수는 바로 소켓 버퍼wake_up_interruptible() , wake_up_interruptible() 를 기다리는 모든 프로세스를 깨운다. 만약 프로세스가 데이터 읽기를 원한다면 소켓 계층의 를 호출했을 것이며, BSD read() , 이것은 다시 를 거쳐서 호출하게 될 것이다 버sys_read(), sock_read(), inet_rcvmsg() . 퍼가 비어있고 프로세스가 블로킹 모드 를 사용하고 있다면 계층의, (blocking mode) , INET 큐에서 잠들어 있게 된다 따라서 이젠 원하는 데이터가 도착했으므로 다시 깨어나게sleep . 될 것이며 이후에는 계층의 에 들어있는 소켓 버퍼 데이터들을 처리, INET receive_queue 하게 될 것이다. 사용자가 를 호출하여 시스템 콜인 를 거쳐서 계층의read() VFS sys_read() TCP/UDP 를 수행하고 계층의 까지의 과정은 데이터 전송 시tcp_recvmsg() INET add_wait_queue() 의 제어흐름과 똑같은 메커니즘을 사용하여 흐름을 따라간다 다만 눈여겨 볼 것은 데이터. 를 받기 위해 잠들어 있다가 데이터 수신시 커널 스택에 쌓여 있던 함수들이 복귀 하면서 받은 데이터를 처리하는 과정을 알아볼 필요가 있다 와. add_wait_queue() 는 단순히 계층에의 큐에 잠드는 프로세스를 추가하는 기능을tcp_data_wait() INET sleep 수행한다. 신호를 받은 프로세스는 다시 를 통해서 계층의wake-up tcp_recvmsg() INET 에 있는 데이터가 완전히 소진될 때까지 사용자 주소공간으로 데이터를 복receive_queue 사하는 일을 수행한다 이때 는 데이터를 그냥 복사하지 않고 긴급. tcp_recvmsg() (urgent)
  • 14. - 14 - 데이터가 있는지 먼저 검색하여 사용자 공간으로 복사한다 부가적인 작업으로는 당. CPU 네트워크 통계정보 등을 업데이트하기도 한다 그 이후에는. inet_recvmsg(), 순으로 곧바로 복귀 한다 즉 계층에서 곧바로sock_recvmsg(), sock_read() . , TCP/UDP 사용자 공간으로 데이터를 복사하기 때문에 함수는 잠들어 있던 가read() tcp_recvmsg() 동작함과 동시에 데이터를 읽을 수 있게 된다.
  • 16. - 16 - 리눅스의 네트워크 제어2. 리눅스는 네트워킹을 제어하기 위해 프로토콜 스택과 만을 제공하는 것이 아TCP/IP API 니다 리눅스 관리자가 네트워크를 간단하게 제어하게 하기위하여 쉘 환경에서 제공하는 여. 러 가지 메커니즘이 있다 리눅스는 수많은 네트워킹 도구들을 제공하고 있다 리눅스에서. . 네트워크 디바이스를 제어하기 위한 도구들은 명령과 파일 그리고ifconfig /proc/net/dev , 와 명령이 있다insmod rmmod . 1) ifconfig 커널 부팅이 끝나고 시스템 초기화 스크립트 과정을 거치면 커널에 포함되었거나 모듈로, 작성된 대부분의 네트워크 디바이스 드라이버가 커널에 올라가 동작대기 상태가 된다 그러. 나 아직 네트워크 디바이스를 검출하고 이를 초기화한 후에 해당하는 네트워크 디바이스를, 나타내는 구조체를 커널에 등록한 상태일 뿐으로 커널과 연동되어 동작하는 것net_device 은 아니다 이것은 장에서 다룬 블록 디바이스 드라이버에서 블록 디바이스가 마운트되어. 7 야만 커널에서 사용할 수 있는 것과 같은 맥락이다. 네트워크 디바이스도 블록 디바이스처럼 마운트할 수 있다 그러나 블록 디바이스를 마운. 트하는 명령어와 다른 명령어를 사용하여 네트워크 디바이스를 활성화시켜mount ifconfig 커널과 연동하게 하거나 비활성 시켜 커널과의 연동을 끊는다. 명령어의 사용은 간단하다 자주 쓰이는 파라미터들만 모아놓은 명령의ifconfig . ifconfig 간단한 형식은 다음과 같다 굵게 나타낸 것들은 명령어 사용 시 그대로 써야하는 문자열이. 고 그 외의 것은 적당한 값을 넣어주면 된다. ifconfig [interface-name] [ip-address] [netmask subnetmask] [up|down] 다음은 주소를 로 서브넷 마스크를 으로 하여 활성화IP 192.168.0.11 255.255.255.0 시키는 명령이다. 설정파일에 기본 설정을 해놓았으면 다음과 같이 명령어 옵션 과 만'interface-name' 'up' 주어도 된다 이 경우에 는 설정파일의. ifconfig /sys/config/network-scripts/ifcfg-eth* 내용을 읽어 와서 그 내용에 맞게 네트워크 카드를 활성화시킨다. $ ifconfig eth0:0 192.168.0.11 netmask 255.255.255.0 up
  • 17. - 17 - $ ifconfig eth0 up 동작중인 네트워크 디바이스의 동작은 다음과 같이 간단하게 중단시킬 수 있다. $ ifconfig eth0 down 마지막으로 현재 활성화되어 있는 네트워크 디바이스는 다음과 같은 명령으로 확인할 수 있다 이 명령을 사용하면 네트워크 디바이스의 이름 타입. (eth0), (Link encap:Ethernet), 하드웨어 주소 네트워크 주소(HWaddr 00:0D:60:5F:6A:31), (inet addr:210.94.178.168 상태 브로드 캐스트인Bcast:210.94.178.255 Mask:255.255.255.0), (UP, RUNNING), 지 멀티캐스트인지의 여부 데이터 처리능력(BROADCAST, MULTICAST), (MTU:1500 송신 및 수신 통계 정보Metric:1), (RX packets:7132616 errors:0 dropped:0 overruns:0 frame:0, TX packets:219956 errors:0 dropped:0 overruns:0 carrier:0, collisions:0 txqueuelen:100, RX bytes:548724167 (523.3 Mb) TX bytes:34737277 하드웨어에 접근하기 위한 주소와 인터럽트 주소 등의 커널 내의 하드웨(33.1 Mb)), I/O 어 정보 를 확인할(Interrupt:11 Base address:0x8000 Memory:c0220000-c0240000) 수 있다 참고로 이들은 모두 네트워크 디바이스 드라이버 작성자가 만들어 주어야 하는 부. 분이기도 하다. [root@150 /]$ ifconfig -a eth0 Link encap:Ethernet HWaddr 00:0D:60:5F:6A:31 inet addr:210.94.178.168 Bcast:210.94.178.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:7132616 errors:0 dropped:0 overruns:0 frame:0 TX packets:219956 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:100 RX bytes:548724167 (523.3 Mb) TX bytes:34737277 (33.1 Mb) Interrupt:11 Base address:0x8000 Memory:c0220000-c0240000 2) /proc/net/dev 파일은 이 커널과 연동되어 동작하는 네트워크 디바이스의 시스템 리소스/proc/net/dev 와 관련된 통계정보를 출력하는 역할을 한다 이 파일의 내용을 보기 위해서는 나. vi 같은 편집기를 사용하면 안 되고 명령어를 이용하여 사용해야 한다 이것은emacs cat .
  • 18. - 18 - 라는 파일 시스템의 특징 때문인데 이 안의 파일들은 커널이 커널작동과 관련/proc /proc 된 모든 정보를 실시간으로 파일로 저장하는 곳이기 때문이다 이는 마치 윈도우의 제어판. 과 같다. 네트워크 디바이스 드라이버에 대한 통계정보를 출력한 것을 보여준다 이것은 보는 바와. 같이 각 디바이스 별로 송신시와 수신시의 통계를 보여주고 있다 이 통계들은 네트워크 디. 바이스 드라이버 작성자가 작성해야 하는 부분이 아니고 프로토콜 스택이 패킷의TCP/IP 송수신을 처리하면서 통계정보를 수시로 모아 출력한 결과물이다. $ cat /proc/net/dev Inter-| Receive face |bytes packets errs drop fifo frame compressed multicast lo: 344042 571 0 0 0 0 0 0 eth0:548755206 7133018 0 0 0 0 0 0 | Transmit |bytes packets errs drop fifo colls carrier compressed 344042 571 0 0 0 0 0 0 34740843 219983 0 0 0 0 0 0 와3) insmod rmmod 네트워크 디바이스 드라이버를 커널에 등록하는 방법은 커널 컴파일시 삽입하는 방법과 모듈로 작성하여 런타임시 등록하는 방법이 있는데 이 두 명령어는 후자의 방법으로 디바, 이스 드라이버를 작성할 때 사용한다 전자의 방법은 다음절인 네트워크 디바이스 드라이버. 작성에서 자세히 설명한다. 명령어는 네트워크 디바이스를 검출하고 이를 초기화 한 후에 해당 디바이스를insmod , 다룰 수 있는 자료구조인 구조체를 커널에 등록하는 작업을 수행한다 이렇게net_device . 가 수행하는 세 개의 작업은 네트워크 디바이스 드라이버에 구현되어 있어야 하는insmod 부분으로 눈여겨 볼 필요가 있다. 명령어의 사용은 문자나 블록 디바이스 드라이버와 같이 컴파일 된 모듈을 인자insmod 로 주면 된다. 네트워크 디바이스 드라이버 모듈이름$ insmod _ _ _ .o 는 사용시 등록된 구조체를 커널에서 제거하는 역할을 한다rmmod insmod net_device .
  • 19. - 19 - 이 역할을 하는 함수 또한 네트워크 디바이스 드라이버에 구현되어 있어야 하는 부분이다. 의 사용은 와 비슷하나 모듈이름에서 확장자를 뺀 문자열을 인자로 주면rmmod insmod 된다. 네트워크 디바이스 드라이버 모듈이름$ rmmod _ _ _
  • 20. - 20 - 네트워크 디바이스 드라이버3. 네트워크 디바이스 드라이버는 그림 과 그림 그리고 그림 에서 보았듯이 실[ 1] [ 2], [ 3] 제적인 하드웨어인 네트워크 디바이스의 작동을 제어하는 역할을 담당하고 있다 그러나 리. 눅스에서는 네트워크 디바이스를 항상 실제의 하드웨어를 다루는 데에만 작성되지 않는다. 루프백 처럼 논리적인 디바이스를 구현하기도 한다(loop-back) . 또한 네트워크 디바이스와 연결되는 위의 계층들은 문자나 블록 디바이스와 다르게 프로토콜 스택과 연결되어 있다 이러한 이유로 네트워크 디바이스 드라이버는 문TCP/IP . 자나 블록 디바이스와는 다르게 구현된다 네트워크 디바이스 드라이버의 구현은 좀 더 복. 잡한 경향이 있다 일례로 그림 의 제어 흐름은 네트워크 디바이스가 인터럽트의 처리를. [ 3] 해야 제대로 된 송수신을 수행 할 수 있음을 알 수 있다 또한 명령어의 출력. 'ifconfig -a' 물은 네트워크 디바이스 드라이버의 여러 가지 초기화와 통계 정보 등을 구현해야 함을 알 수 있다. 네트워크 디바이스의 종류1) 네트워크 디바이스 드라이버는 패킷의 전송과 수신을 담당하는 실제 하드웨어의 작동을 제어하는 드라이버가 있으며 특수한 목적으로 구현된 논리적 디바이스 드라이버가 있다, . 리눅스에서 구현해놓은 논리적 디바이스 드라이버로는 루프백 과 더미(loop-back) 디바이스 드라이버가 있다(dummy) . 루프백 디바이스 드라이버는 리눅스에서 라는 이름으로 구현되어 있다 는 루프백lo . lo 네트워크 디바이스 로 이 이름만 사용할 수 있다 디바이스는 논리적 네트워(127.0.0.1) . lo 크 디바이스이고 네트워크 시스템이 동작하기 위한 최소한의 네트워크 디바이스 이다. 에서 자세한 정보를 얻을 수 있다linux/drivers/net/loopback.c . 디바이스는 일을 하지 않는 명목상의 네트워크 디바이스로 네트워크 디바이스dummy0 가 없을 경우에 포함되는 디바이스이다 주로 라우팅 디바이스 포인터로 사용되고. 에서 선언 내용을 볼 수 있다 문자열 뒤에 붙은 은linux/drivers/net/dummy.c . dummy 0 여러 개의 디바이스를 만들 수 있기 때문에 순서를 붙여놓은 것이다 이 숫자는 커dummy . 널에서 제공하는 루틴으로 자동으로 주어지게 할 수 있다. 여기서 논리 디바이스 드라이버는 목적에 따라서 이름이 정해져있다 물리 디바이스 드라. 이버의 이름은 드라이버 초기화 함수에서 자유자재로 붙일 수 있으나 보통 로 시작하고eth 디바이스마다 과 같은 식으로 뒤에 번호를 붙인다 번호를 붙이는 방법 또한0,1,2,3 . 디바이스 드라이버에서 번호를 붙이는 것과 같은 방식으로 한다 는 많이 되는dummy . eth 것으로 랜이라고도 불리는 계층의 이더넷 프로토콜을 의미한다 그러므로Link (ehternet) .
  • 21. - 21 - 나 등의 다른 프로토콜을 사용하는 네트워크 디바이스를 사용할 경우에는 이름을PPP SLIP 달리하여 구현해야 할 것이다. 네트워크 디바이스 드라이버 구현2) 커널 스택과 네트워크 디바이스 드라이버의 상관관계를 구체적으로 알아보기 위TCP/IP 하여 이더넷 제어 칩의 코드를 분석한다 일단 네트워크 디바이스 드라이버에서CS9800 . 구현해야 할 내용은 네트워크 디바이스 드라이버 가 모듈 또는 포함되어 컴파일 된 상태에 서 커널에 적재될 때의 초기화 처리 모듈의 커널에서 제거될 때의 종료 처리 네트워크 디, , 바이스의 검출 네트워크 디바이스의 초기화 및 등록 네트워크 디바이스의 열기와 닫기 네, , , 트워크 디바이스에서의 데이터 전송과 수신 인터럽트의 처리 네트워크 디바이스를 제어할, , 함수의 구현 멀티캐스트 와 브로드캐스트 의 처리 등이 있다ioctl() , (multicast) (braadcast) . 이들의 구현을 하나하나 살펴볼 것이다 먼저 초기화와 관련된 커다란 자료구조인. 구조체에 대하여 알아보자net_device . 구조체 의 이해i) net_device 네트워크 디바이스 드라이버 또한 다른 디바이스 드라이버와 마찬가지로 초기화루틴들이 필요하다 그러나 네트워크 디바이스 드라이버는 디바이스 파일형태로 존재하는 문자나 블. 록 디바이스와는 다르게 디바이스 파일로 접근하지 않고 다른 방법으로 구현된다 또한 이, . 것을 자세하게 표현하면 문자나 블록 디바이스 드라이버는 그림 의 계층에 속하는[ 2] VFS 구조체와 연계하여 구현되지만 네트워크 디바이스 드라이버는files_operations , 와 연계하는 부분을 프로토콜 스택에 맡기고 있으며 프로files_operations TCP/IP TCP/IP 토콜 스택의 최하위 계층인 계층과 연계하여 구현된다 그러므로 네트워크 디바이스Link . 드라이버는 계층에 속하는 구조체의 필드들의 내용을 초기화 함수들을 통Link net_device 하여 채워서 프로토콜과 연계해야 한다TCP/IP . 는 네트워크 디바이스 드라이버의 중요 자료구조로net_device include/linux/netdevice.h 에 선언되어 있다 커널 버전 에서는 이 구조체에 상태필드 제어필드 제어 함수 주소. 2.4 , , 필드 등 모두 무려 개의 필드가 정의 되어 있다 그래서 네트워크 디바이스 드라이버에75 . 서 사용하는 필드만을 골라서 상태필드와 제어필드에 대한 설명과 제어 함수 주소 필드에, 대한 설명을 분리하여 설명한다.
  • 22. - 22 - 상태나 제어를 나타내는 필드a. char name[16]; 네트워크 디바이스의 인터페이스 이름을 지정한다 이 필드는 주로 초기화 루틴인. init() 함수에서 지정한다 문자열은 의 포맷과 비슷하게 로 넣어주면 자동으로. printf() "eth%d" 와 같은 형식으로 처리된다 숫자를 붙일 필요가 없다면 과 같"eth0", "eth1", "eth2" . "net" 이 사용하면 된다. unsigned long base_addr; 하드웨어를 제어하기 위한 주소를 지정한다 여기에는 이더넷 제어 칩의 선두 주소를 지. 정하면 된다 와 같이 가 지원도록 만들어진 디바이스에서는 강제. PCI PnP(Plug and Play) 로 지정하면 안 되고 공란으로 두어야 한다. unsigned char irq; 인터럽트 서비스 번호이다 이 필드도 과 같이 가 지원도록 만들어진 디바. base_addr PnP 이스에서는 강제로 지정하면 안 되고 공란으로 두어야 한다. unsigned char if_port; 디바이스 드라이버 물리적인 동작방식을 지정한다 일례로 로 작동시키기를 원. 10Mbps 하면 이 필드에 를 로 작동시키기를 원하면 이 필드에IF_PORT_10BASET , 100Mbps 를 대입하면 된다IF_PORT_100BASET . 커널에는 에 값이 인 부터 시작하여include/linux/netdevice.h 0 IF_PORT_UNKNOWN IF_PORT_10BASE2, IF_PORT_10BASET, IF_PORT_AUI, IF_PORT_100BASET, 의 순서로 개가 열거형 키워드인IF_PORT_100BASETX, IF_PORT_100BASFX 7 enum 으로 정의되어 있다 그 외의 다른 동작방식을 정의하기 위해서는 네트워크 디바이스 드라. 이버에서 자체적으로 정의해 사용하면 된다. unsigned char dev_addr[8]; 디바이스와 연결된 네트워크의 주소를 지정할 때 사용된다. unsigned short flags; 디바이스의 상태와 사용가능한 기능 등을 나타내는 필드이다 로 이루어져 있어서. 12bit 각 비트마다 디바이스의 기능을 끄고 키는 역할을 한다 이 필드에 설정할 수 있는 플래그. 들은 에 선언되어 있다 이들 중 일부는 디바이스의 행동을 정의하므로include/linux/if.h . 중요하다 그림 에 일부 플래그의 의미를 정리하였다. [ 4] .
  • 23. - 23 - 상태 이름 값 설명 IFF_UP 0x1 디바이스가 활성화되어 있음을 나타낸다 디바이스 드. 라이버에서는 이 플래그를 설정하면 안 되고 참조만 해야 한다. IFF_BROADCAST 0x2 디바이스가 브로드캐스트를 처리할 수 있음을 나타낸 다. IFF_LOOPBACK 0x8 디바이스가 디바이스임을 나타낸다loop-bacck . IFF_POINTOPOINT 0x10 나 와 같은 연결을 갖는 디바이스임을PPP SLIP 1:1 나타낸다. IFF_NOARP 0x80 디바이스가 를 지원하지 않음을 나타낸다 보통ARP . 연결을 갖는 디바이스나 같은 논리적1:1 lo, dummy 인 디바이스에서 사용한다. IFF_PROMISC 0x100 네트워크에 발생한 모든 데이터를 수신함을 나타낸다. 보통 와 같은 유틸리티에 의해 설정된다tcpdump . IFF_ALLMULTI 0x200 모든 멀티캐스트 데이터를 수신하도록 하기위해 설정 한다 이 플래그는 가 지정되어 있. IFF_MULTICAST 을 때만 설정할 수 있다. IFF_MULTICAST 0x1000 디바이스가 멀티캐스트를 처리할 수 있음을 나타낸다. IFF_AUTOMEDIA 0x4000 여러 네트워크의 접속이 가능할 때 자동으로 사용가능 한 네트워크를 검출할 수 있음을 나타낸다. IFF_DYNAMIC 0x8000 하드웨어 주소가 고정되지 않고 바뀔 수 있음을 나타 낸다 이는 보통 디바이스에 하드웨어 주소를 고정하기. 위해 사용하는 이 없을 경우에 이 플래그를eeprom 설정하여 수동으로 설정해야 함을 알 수 있다. 그림 디바이스 상태 및 기능을 나타내는 플래그[ 4] unsigned long trans_start; 데이터 전송이 시작된 시간을 저장하는 필드이다 그러므로 네트워크 디바이스 드라이버. 는 데이터 전송이 시작되었을 때마다 이 필드 값을 로 설정해야 한다jiffies . unsigned long last_rx; 데이터가 수신된 시간을 저장하는 필드이다 그러므로 네트워크 디바이스 드라이버는 데. 이터가 수신되었을 때마다 이 필드 값을 로 설정해야 한다jiffies . int watchdog_timeo; 전송이 시작되고 나서의 시간을 설정하는 필드이다 단위는 이고 이 값을timeout . jiffies ,
  • 24. - 24 - 초과해 전송이 되면 커널은 필드에 지정된 함수를 호출한다tx_timeout . void *priv; 디바이스마다 처리해야 하는 정보를 저장할 메모리가 필요하면 이 필드를 이용해야 한다. 와 같은 함수를 사용할 경우에는 따로 할당할 필요가 없다alloc_ether() . struct dev_mc_list *mc_list; int mc_count 멀티캐스트를 처리할 때 사용되는 필드이다 는 멀티캐스트 주소 정보를 관리하는. mc_list 구조체의 선두 주소이고 는 에 포함된 멀티캐스트 정보의 목록수를 가지, mc_count mc_list 고 있다. 함수 포인터 필드b. int (*init)(struct net_device *dev); 디바이스 드라이버를 커널에 등록하는 역할을 하는 함수이다 이 함수는 명령어. insmod 를 이용하여 모듈을 커널에 삽입할 때 수행되고 커널에서 딱 한번만 실행되는 특징을 가지 고 있다 구조체의 대부분의 필드를 이 함수에서 채운다. net_device . int (*open)(struct net_device *dev); 커널에 등록된 디바이스를 활성화시키는 역할을 하는 함수이다 이 함수는 명령. ifconfig 어를 이용해 디바이스를 활성화시킬 때 수행된다 디바이스가 동작할 수 있도록 디바이스의. 각종 제어 레지스터들을 초기화하고 인터럽트 서비스 함수를 등록하는 일을 한다, . int (*stop)(struct net_device *dev); 디바이스를 비활성화 시키는 역할을 한다 이 함수 또한 명령어로 디바이스를 활. ifconfig 성화시킬 때 수행된다 디바이스의 동작을 정지시키고 등록된 인터럽트 서비스 함수를 제. , 거하는 등의 함수와 반대의 일을 한다open() . int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev); 그림 의 데이터 송신 흐름에서 살펴보았듯이 커널의 프로토콜 스택의8.8 TCP/IP Link 계층에서 이 함수를 호출하여 데이터를 송신한다 즉 데이터 송신 처리 루틴은 이 필드에. 등록된 함수에 구현해야 한다. void (*set_multicast_list)(struct net_device *dev); 네트워크 인터페이스가 처리하는 멀티캐스트 목록이 바뀌거나 멀티캐스트와 관련된 상태 가 바뀔 때 호출된다. void (*tx_timeout) (struct net_device *dev);
  • 25. - 25 - 데이터 송신 시 하나의 패킷을 전송한 후에 필드에 설정해 놓은 시간제watchdog_timeo 한 안에 다음 패킷을 전송을 하지 못해서 전송을 지속적으로 처리할 수 없게 될 때 커널에 서 호출되는 함수이다 이때 디바이스가 적절한 조취를 취할 수 있게 구현하면 된다. . struct net_device_stats* (*get_stats)(struct net_device *dev); 명령어와 같은 응용 프로그램에 네트워크 디바이스 드라이버의 통계 정보가 필ifconfig 요할 때 호출된다. 네트워크 디바이스 드라이버의 초기화와 제거ii) 네트워크 디바이스 드라이버의 초기화 단계는 크게 세 단계로 나눌 수 있다 첫 단계는. 디바이스 드라이버를 커널에 등록하는 단계이며 등록한 다음엔 바로 프로토콜 스, TCP/IP 택이 디바이스 드라이버를 사용할 수 있게 그 연결통로 역할을 하는 구조체를, net_device 초기화하고 하드웨어를 검출한다 마지막으로 네트워크 디바이스의 제어 레지스터들을 초기. 화시켜 장치를 활성화시키는 것이다 첫 번째 단계는 함수에서 등록한 함수. module_init() 가 그 일을 수행하며 두 번째 단계는 구조체의 함수 포인터 필드가 마지, net_device init , 막인 세 번째 단계는 구조체의 함수 포인터 필드가 수행한다net_device open . 커널에 디바이스 드라이버를 등록 또는 제거a. 코드 은 이더넷 제어 칩 의 코드 중 초기화의 첫 번째 단계인 디바이스 드[ 1-1] CS8900 라이버를 커널에 등록 초기화 제거 루틴을을 보여준다, , . 디바이스 드라이버 고유의 관리 구조체 선언/* */① typedef struct { struct net_device_stats stats; u16 txlen; int char_devnum; spinlock_t lock; } cs8900_1_t; 커널에 등록할 구조체 선언/* net_device */② static struct net_device cs8900_1_dev = { init: cs8900_1_probe };
  • 26. - 26 - 번 코드는 디바이스 드라이버가 드라이버를 관리하는 자기 자신의 구조체를 선언한 부1 분이다 이 구조체의 주소는 나중에 구조체의 필드의 내용이 된다. net_device priv . 번 코드에서는 커널에 등록할 구조체를 전역 변수로 선언하였다 여기서2 net_device . 구조체 전역 변수는 함수 포인터 필드만을 초기화 하였는데 이것은 번net_device init , 3 코드에서 구조체를 함수로 커널에 등록하기 전에 초기화 함net_device register_netdev() 수 주소를 지정해주어야 하기 때문이다 이렇게 하면 가 실행된 후에. cs8900_1_init() init 함수 포인터에 등록된 함수가 바로 실행되어 구조체의 나cs8900_1_probe() net_device 머지 필드들을 스택에서 사용할 수 있도록 초기화 할 수 있게 된다TCP/IP . 다른 필드들은 커널에서 제공하는 초기화 함수로 초기화 하거나 필드들의 목적에 맞게 적 당한 장소에서 초기화된다 일례로 우리가 작성하는 디바이스 드라이버는 이더넷 환경에서. 작동하는 드라이버를 작성할 것이기 때문에 구조체에서 이더넷 프로토콜과 관, net_device 련된 필드들은 함수로 쉽게 초기화할 수 있다 이 함수를 사용하는 코드를ether_setup() . 뒤에서 살펴 볼 것이다. 구조체 변수는 전역 변수로 선언하거나 함수를 이용해서net_device alloc_etherdev() 메모리 할당을 하는 방법이 있는데 여기서는 전자를 이용하였다 함수는. alloc_etherdev() 커널에 디바이스 모듈과 디바이스를 등록/* */③ static int __init cs8900_1_init (void) { strcpy(cs8900_1_dev.name, "eth%d"); return (register_netdev (&cs8900_1_dev)); } 커널에서 디바이스 모듈과 디바이스를 제거/* */④ static void __exit cs8900_1_cleanup (void) { cs8900_1_t *priv = (cs8900_1_t *) cs8900_1_dev.priv; unregister_chrdev(priv->char_devnum,"cs8900_1_eeprom"); release_region (cs8900_1_dev.base_addr,16); unregister_netdev (&cs8900_1_dev); } 모듈을 초기화하거나 제거할 함수를 지정/* , */⑤ module_init (cs8900_1_init); module_exit (cs8900_1_cleanup); 코드 모듈 초기화와 제거[ 1-1]
  • 27. - 27 - 파일에서 선언되어 파일에서 구현된 것include/linux/etherdevice.h drivers/net/net_init.c 으로 이 함수의 원형은 코드 와 같다 이 함수의 인자에는 꼭 디바이스 드라이버에서 자[ 2] . 체 선언한 관리용 구조체가 사용할 메모리 크기를 지정해야 한다 물론 관리용 구조체에 메. 모리를 따로 할당할 필요가 없다면 코드 과 같이 이 함수를 사용하지 않거나 인자를[ 1-1] 으로 주면 된다0 . #include <linux/etherdevice.h> struct net_device *alloc_etherdev(int sizeof_priv); 코드 함수의 원형[ 2] alloc_etherdev() 는 디바이스 자체의 관리용 구조체와 구조체에 메모리를 할alloc_etherdev() net_device 당받아 구조체의 필드에 관리용 구조체의 선두주소를 대입한다 또한net_device priv . , 구조체의 필드의 값을 자동으로 로 설정하여 메모리가 할당된net_device name "eth%d" 구조체의 선두주소를 반환한다 그러므로 이 함수를 사용할 때는 코드net_device . [ 1-1] 의 번과 번 그리고 번 코드가 달라진다 를 사용하여 달라진 코드2 3 4 . alloc_etherdev() [ 의 번과 번 코드는 코드 과 같다 번 코드에서 전역으로 선언했던1-1] 2 3 [ 3] . 2 net_device 구조체에 값을 대입하였고 등록 함수인 번 코드에서 의 반환 값NULL , 3 alloc_etherdev() 을 전역으로 선언했던 구조체에 대입하였으며 의 등록도 그net_device cs8900_1_probe() 다음 코드에 행해진다 또한 구조체에 메모리를 할당했으므로 모듈이 커널에서. net_device 제거될 때 를 사용한 후에 로 할당된 메모리를 제거하는 코드unregister_netdev() kfree() 가 추가되어야 한다. 커널에 등록할 구조체 선언/* net_device */ static struct net_device *cs8900_1_dev = NULL; 커널에 디바이스 모듈과 디바이스를 등록/* */ static int __init cs8900_1_init (void) { cs8900_1_dev = alloc_etherdev(sizeof(cs8900_1_t)); if(cs8900_1_dev == NULL) return -ENOMEM; cs8900_1_dev->init = cs8900_1_probe; return (register_netdev (&cs8900_1_dev)); } 커널에서 디바이스 모듈과 디바이스를 제거/* */
  • 28. - 28 - 는 파일에서 선언되어 파일에서 구현된 것으로 이free() include/linux/slab.h mm/slab.c 함수의 원형은 코드 와 같다 이 함수는 메모리에 할당된 객체를 메모리에서 해제하여[ 4] . 준다. #include <linux/slab.h> void kfree (const void *objp); 코드 함수의 원형[ 4] kfree() 번 코드는 커널에 디바이스 모듈과 디바이스를 등록하고 있다 이 함수는 모듈이 커널에3 . 적재될 때 가장 먼저 실행되는 것으로 번 코드의 함수에서 이 함수를 등록, 5 module_init() 하였기 때문이다 이 함수는 가장먼저 구조체의 필드를 지정하고 있다. net_device name . 네트워크 디바이스는 디바이스 드라이버가 정하므로 고유한 이름을 지정해야 한다 여기에. 서는 를 사용하는데 이는 동일한 네트워크 디바이스 드라이버를 사용하는 하드웨"eth%d" , 어가 등록될 때마다 와 같은 식으로 디바이스 이름이 다르게 등록된eth0, eth1, eth2, ... 다 만약 이름을 하나만 지원하고 디바이스도 하나만 지원한다면 고 같은 형식으로도. "net" 지정할 수 있다. 코드 의 번 코드의 는 번에 걸쳐 설정한 구조[ 1-1] 3 register_netdev() 1~2 net_device 체를 커널에 등록해주는 중요한 함수이다 이 함수는 파일에서. include/linux/netdevice.h 선언되어 에 구현된 것으로 이 함수의 원형은 코드 와 같다 디바drivers/net/net_init.c [ 5] . 이스 드라이버를 등록해주는 문자나 블록 디바이스와는 다르게 이 함수는 file_operations 구조체를 등록하지 않고 구조체를 등록한다 이것은 위의 를 설명할net_device . net_device 때 언급했던 것처럼 디바이스 드라이버를 응용 프로그램이 바로 사용하는 것이 아니라 응, 용 프로그램이 를 거쳐서 사용하기 때문인 것이다TCP/IP . #include <linux/netdevice.h> int register_netdev(struct net_device *dev); 코드 함수의 원형[ 5] register_netdev() static void __exit cs8900_1_cleanup (void) { 위와 상동 단지 마지막 줄에 함수를 추가/* , kfree() */ kfree(cs8900_1_dev); } 코드 함수를 사용한 디바이스 초기화와 제거 코드[ 3] alloc_etherdev()
  • 29. - 29 - 코드 의 번 는 모듈이 커널에서 제거될 때 실행되는 함수이다 이것 또[ 1-1] 4 cleanup() . 한 번의 초기화 함수처럼 번 코드의 에서 이 함수를 등록하였기 때문인3 5 module_exit() 것이다 이 함수에서는 디바이스 드라이버에서 할당한 메모리들을 제거하고 있다 중간에. . 수행하는 는 네트워크 디바이스가 가지고 있는 을 제거하기unregister_chrdev() EEPROM 위해서인 것이다 보통 네트워크 디바이스는 을 가지고 있어서 안에 디바이스의. ROM ROM 주소와 다른 필요한 정보들을 하드 코딩해 놓고 있다 만약 디바이스에 이 없다MAC . ROM 면 주소를 디바이스 드라이버 작성자가 드라이버에 주소를 설정하는 루틴을 직MAC MAC 접 작성해야 한다. 의 세 번째 줄에서는 를 사용하여 구조체의cleanup() release_region() net_device 필드에 할당된 메모리를 제거 해주고 있다 필드와base_addr . base_addr 은 두 번째와 세 번째 단계의 초기화를 수행하도록 등록된release_region() 와 연결된 설명이 필요하므로 뒤로 미루도록 하겠다cs8900_1_probe() . 에서 가장 중요한 것은 로 에서 등록한cleanup() unregister_netdev() register_netdev() 구조체를 커널에서 제거하는 함수이다 이 함수 또한 와 마net_device . register_netdev() 찬가지로 을 하고 에서 사용했던linux/netdevice.h include register_netdev() net_device 구조체 변수의 포인터를 매개변수로 호출하면 된다. 참고로 번 번 코드에서의 와 함수 와 키워드는 커널의2 3 init() cleanup() , __init __exit 효율적인 메모리 관리를 위해 만들어놓은 기법이다 먼저 와 함께 쓰인 함수는 커널에. __init 운영체제의 초기화 과정에만 사용된다는 것을 알려주게 된다 커널의 입장에서는 초기화 루. 틴들을 초기를 마친 후에 속해서 메모리에 남겨두고 있을 필요가 없다 그래서 커널은 이런. 초기화에만 필요한 함수나 변수를 컴파일 시 미리 별도의 영역에 따로 모아두었다가 초기, 화를 마친 후 이 영역의 메모리를 해제한다 그렇게 하면 필요 없는 메모리를 제거하여 커. 널이 차지하는 메모리의 양을 줄일 수 있게 되어 효율적인 운영체제가 되는 것이다. __exit 도 와 비슷한 맥락이다 와 함께 쓰인 함수들 또한 컴파일 시 미리 별도의 영역__init . __init 에 따로 모아두어 있다가 커널이 부팅할 때 메모리에 바로 올라오지 않는다 이들은 이들, . 함수가 호출될 때에 메모리에 적재되어 사용되고 바로 메모리에서 삭제된다. 하드웨어 검출 및 하드웨어 초기화b. 위의 모듈 초기화의 두 번째와 세 번째 단계는 프로토콜 스택이 디바이스 드라TCP/IP 이버를 사용할 수 있게 구조체를 초기화하고 하드웨어를 검출하여 하드웨어 장net_device 치를 활성화시키는 단계라 하였다. 이더넷 디바이스는 마지막 초기화 단계를 에서 수행하고 있CS9800 cs8900_1_probe() 다 의 코드는 코드 이다 실제로 는 하드웨어. cs8900_1_probe() [ 1-2] . cs8900_1_probe() 를 검출하고 활성화 시키는 코드들 중에서 상당양의 에러 제어 코드들이 있으나 이들은 네,
  • 30. - 30 - 트워크 디바이스 드라이버를 작성하는 데에는 큰 도움을 주지 못하므로 코드를 읽기 쉽게 삭제하였다. 또한 하드웨어를 제어하는 코드들 또한 상당양이 있지만 삭제하지 않았는데 이는 디바이, 스 드라이버의 코드가 실제 디바이스다운 모습을 잃지 않게 하기 위한 것이며 의, CS8900 하드웨어는 대부분의 디바이스 드라이버가 갖추어야 할 기본적인 하드웨어 스펙만을 갖추고 있기 때문에 어떠한 방식으로 하드웨어를 제어하는지 쉽게 파악할 수 있다 그렇지만 완전. 히 디바이스의 하드웨어의 내부를 이해하여야만 정확한 동작을 이해할 수 있다CS8900 . /* Driver initialization routines */ int __init cs8900_1_probe (struct net_device *dev) { static cs8900_1_t priv; int i,result; u16 value; /* initialize device fields */① u16 MAC_addr[3] = {0, 0, 0}; memset (&priv,0,sizeof (cs8900_1_t)); result = check_region(dev->base_addr,16); request_region (dev->base_addr,16,dev->name); dev->if_port = IF_PORT_10BASET; dev->priv = (void *) &priv; /* use ethernet */② ether_setup (dev); /* register service routines */③ dev->open = cs8900_1_start; dev->stop = cs8900_1_stop; dev->hard_start_xmit = cs8900_1_send_start; dev->get_stats = cs8900_1_get_stats; dev->set_multicast_list = cs8900_1_set_receive_mode; dev->tx_timeout = cs8900_1_transmit_timeout; dev->watchdog_timeo = HZ; /* probe ethernet hardware */④ #ifdef CONFIG_ARCH_PXA_LDS2000
  • 31. - 31 - dev->base_addr = 0xf0000000 + 0x300; dev->irq = IRQ_GPIO(0); for (i = 0; i < ETH_ALEN / 2; i++) { MAC_addr[i]= cs8900_1_read(dev,PP_IA+i*2); dev->dev_addr[i*2] = MAC_addr[i] & 0xff; dev->dev_addr[i*2+1] = (MAC_addr[i] >> 8) & 0xff; } #endif /* verify EISA registration number for Cirrus Logic */⑤ value = cs8900_1_read (dev,PP_ProductID); /* verify chip version */ value = cs8900_1_read (dev,PP_ProductID + 2); /* setup interrupt number */ cs8900_1_write (dev,PP_IntNum,0); /* If an EEPROM is present, use it's MAC address. */ /* A valid EEPROM will initialize the registers automatically. */ result = cs8900_1_eeprom (dev); /* prints initialization results */⑥ printk (KERN_INFO "%s: CS8900A rev %c at %#lx irq=%d", dev->name, 'B' + REVISION (value) - REV_B, dev->base_addr, dev->irq); printk (", eeprom ok"); printk (", addr:"); for (i = 0; i < ETH_ALEN; i += 2) { u16 mac = cs8900_1_read (dev,PP_IA + i); printk ("%c%02X:%2X", (i==0)?' ':':', mac & 0xff, (mac >> 8));
  • 32. - 32 - 는 먼저 하드웨어와 연관이 없는 필드부터 채운다 번 코드에서 일반cs8900_1_probe() . 1 적인 필드를 채우고 번 코드에서 이더넷 설정을 한다 마지막으로 번 코드에서 디바이스, 2 . 3 의 작동을 제어하는 함수들을 구조체에 등록을 한다 그 다음부터는 하드웨어와net_device . 관련된 초기화를 진행하는데 번 코드에서 네트워크 디바이스를 검출하고 검출이 성공하면4 , 번 코드를 거쳐서 네트워크 디바이스 내의 세부 하드웨어들을 찾아내고 초기화하여 최종5 적으로 디바이스 드라이버를 활성화시킨다 번 코드는 초기화를 끝내고 그 결과를 커널 메. 6 시지로 출력을 한 것뿐이다. 번 코드의 의 원형은 코드 과 같다 파일에서 선언되1 memset [ 6] . include/linux/string.h 었으며 파일에서 구현되었다 는 원하는 자료구조에 값을 채워주는, lib/string.c . memset() 함수이다 즉 는 새로 선언한 필드에. , “memset (&priv,0,sizeof (cs8900_1_t));” priv 0 을 채워 넣어 구조체의 필드에 그의 주소를 대입할 준비를 하는 것이다net_device priv . #include <linux/string.h> void * memset(void * s, int c, size_t count); 코드 함수의 원형[ 6] memset() 코드 의 번 는 앞서 보았던 디바이스의[ 1-2] 1 check_region(), request_region() 의 와 세트를 이룬다 이들은 매크로 함수로cleanup() release_region() . 에 선언되어 있으며 이 함수들의 원형은 코드 과 같다include/linux/ioport.h [ 7] . 는 세 번째 인자로 주어진 이름으로 커널 메모리를 두 번째 인자의 크기request_region() 만큼 첫 번째 인자에 할당한다 는 그 반대의 기능을 수행한다 마지막으. release_region() . 로 은 첫 번째 인자에 두 번째 인자의 크기만큼 시험 삼아 커널 메모리를check_region() 할당해 보고 그 결과를 반환한다. } printk ("n"); return (0); } 코드 의 마지막 초기화 단계 함수[ 1-2] CS9800 cs8900_1_probe()
  • 33. - 33 - #include <linux/ioport.h> struct resource * request_region(unsigned long start, unsigned long n, const char *name); int check_region(unsigned long start, unsigned long n); void release_region(unsigned long start, unsigned long n); 코드 함수들의 원형[ 6] xxx_region() 즉 의 디바이스 드라이버는 위 세 개의 함수를 이용하여 구조체의, CS8900 net_device 필드에 커널 메모리를 할당받는 것이다 의 필드는 네트base_addr . net_device base_addr 워크 디바이스의 제어 레지스터를 찾아가기 위하여 메모리 맵핑된 주소를 가지고 있는 필드 로써 디바이스 드라이버에서 하드웨어를 제어하는데 유용하게 쓰인다. 코드 의 번 은 이더넷 프로토콜과 관련된 구조체의 필[ 1-2] 2 ether_setup() net_device 드들의 세팅을 해준다 보통 이더넷과 토큰링과 같이 이미 정형화 되어 있는 프로토콜들은. 세팅해주는 함수들을 두고 있다 이 함수들은 파일에 선언되어. include/linux/netdevice.h 있으며 파일에 구현되어 있다 코드 의 계열 함수drivers/net/net_init.c . [ 7] xxx_setup() 의 인자는 구조체 변수를 넣어주며 된다net_device . #include <linux/netdevice.h> ether_setup(); 이더넷 관련 필드 초기화 함수/* */ fc_setup(); 광채널 관련 필드 초기화 함수/* */ fddi_setup(); 광통신 관련 필드 초기화 함수/* */ tr_setup(); 토큰링 관련 필드 초기화 함수/* */ 코드 계열 함수들의 원형[ 7] xxx_setup() 코드 의 역시 계열 함수들과 마찬가지로 여러 프로토[ 2] alloc_etherdev() xxx_setup() 콜을 지원한다 이 할당과 셋업 함수는 보통 네트워크 디바이스 구현 시 같이 사용되므로. 알아두는 것이 좋다 코드 은 할당 함수들의 원형을 보여주고 있다. [ 8] .
  • 34. - 34 - #include <linux/etherdevice.h> 이더넷/* */ struct net_device *alloc_etherdev(int sizeof_priv); #include <linux/fcdevice.h> 광채널/* */ struct net_device *init_fcdev(struct net_device *dev, int sizeof_priv); #include <linux/fddidevice.h> 광통신/* */ struct net_device *alloc_fddidev(int sizeof_priv); #include <linux/hippidevice.h> 고성능 병렬/* */ struct net_device *alloc_hippi_dev(int sizeof_priv); #include <linux/trdevice.h> 토큰링/* */ struct net_device *alloc_trdev(int sizeof_priv) 코드 함수들의 원형[ 8] alloc_xxx() 코드 의 번에서는 디바이스 제어 루틴을 등록하고 있다 이들의 함수들을 하나하[ 1-2] 3 . 나 살펴볼 것이다 여기서 구조체의 필드에 등록되는 는. net_device watchdog_timeo HZ 파일에 으로 정의되어 있다 또한 이 필드의 값은 항상 정include/asm-arm/param.h 100 . 의되어 있어야 디바이스 바르게 작동하므로 꼭 설정해주어야 한다. 코드 의 번에서 디바이스를 제어할 준비를 한다 여기서 와 필드에[ 1-2] 4 . base_ddr irq 하드웨어 정보를 대입한다 이 정보들을 바탕으로 하드웨어를 제어하게 되어 있다 보통. . 나 로 작동하는 디바이스일 경우에는 하드웨어를 검출하는 작업을 해주어야 하고PCI ISA , 임베디드 시스템에서는 보통 네트워크 디바이스가 와 메모리 매핑으로 연결되어 있으CPU 므로 해당필드에 하드웨어 제작자가 할당한 주소와 인터럽트 번호를 필드에 대입하여주면 된다. 코드 의 번에서 디바이스는 칩의 버전이 맞는 것인지 다시 확인하고 할당받은 인[ 1-2] 5 , 터럽트 번호를 하드웨어에 세팅하여 인터럽트가 발생하면 할당받은 인터럽트 번호로 커널에 전달하게 한다 마지막으로 하드웨어의 주소를 가지고 있는 에서 주. MAC EEPROM MAC 소를 읽어오고 있다 의 제어는 디바이스에서 따로 로 하고. EEPROM cs8900_1_eeprom() 있다. 네트워크 디바이스의 열기iii) 응용 프로그램으로 네트워크 디바이스를 활성화 시킬 때 수행되는 루틴이다ifconfig . 구조체의 함수 포인터 필드에 등록된 함수로 디바이스 드라이버net_device open CS8900 에서는 이라는 이름을 사용하였다 네트워크 디바이스 드라이버에서cs8900_1_start . open 필드에 등록된 함수는 크게 세 단계의 동작을 수행해야 한다 에 구현된. CS8900
  • 35. - 35 - 는 앞으로 언급할 세 단계를 그대로 수행하고 있으므로 이 함수를 기준으cs8900_1_start() 로 설명한다 는 코드 과 같다. cs8900_1_start() [ 1-3] . 첫 번째로 로 인터럽트 서비스 함수를 등록한다 인터럽트 서비스 함수는request_irq() . 송신과 수신시 항상 동작되는 함수로 여기서는 를 등록하였다 두 번cs8900_1_interrupt() . 째로는 디바이스가 동작할 수 있도록 내부 레지스터들에 값을 설정하여 네트워크와 디바이 스를 연결시킨다 마지막으로 구조체내에 정의된 송신 큐가 동작하도록. net_device 를 호출해야 한다 코드 에 각 단계별로 번호를 붙여 놓았다netif_start_queue() . [ 1-3] . 2 번 코드는 하드웨어 의존적인 코드들이 있지만 여기서는 한 줄만 남겨 놓았다. 마지막으로 모듈이 사용되고 있음을 나타내기 위해서 디바이스 드라이버의CS8900 는 매크로를 사용한다cs8900_1_start() MOD_INC_USE_COUNT . 네트워크 디바이스의 닫기iV) 응용 프로그램으로 네트워크 디바이스를 비활성화 시킬 때 수행되는 루틴이다ifconfig . 구조체의 함수 포인터 필드에 등록된 함수로 디바이스 드라이버net_device stop CS8900 에서는 이라는 이름을 사용하였다 네트워크 디바이스 드라이버에서cs8900_1_stop . stop 필드에 등록된 함수는 필드에 등록된 함수와 반대의 동작을 수행하면 된다open . 첫 번째로 로 등록된 인터럽트 서비스 함수를 제거한다 그 다음 디바이스가free_irq() . 동작할 수 있도록 내부 레지스터들에 값을 설정하여 네트워크와 디바이스와의 연결을 끊는 static int cs8900_1_start (struct net_device *dev) { /* install interrupt handler */① request_irq (dev->irq,&cs8900_1_interrupt,0,dev->name,dev); /* enable the ethernet controller */② cs8900_1_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE); /* start the queue */③ netif_start_queue (dev); MOD_INC_USE_COUNT; return (0); } 코드 필드에 등록된[ 1-3] open cs8900_1_start()
  • 36. - 36 - 다 마지막으로 를 호출하여 커널에 등록된 구조체 내에서. netif_stop_queue() net_device 동작하고 있는 송신 큐를 정지시킨다 디바이스 드라이버의 는. CS8900 cs8900_1_stop() 이 순서들을 그대로 수행하고 있지는 않지만 순서는 달라도 상관없다 또한 이 함수에서도. 번 코드와 같은 하드웨어 의존적인 코드들이 여러 줄이 있지만 첫 줄만 남겨 놓았다1 . 네트워크 디바이스에서의 패킷 전송V) 패킷의 전송은 디바이스에서 패킷을 전송하여 성공하면 금상첨화일 것이다 그러100% . 나 패킷을 전송하기까지의 과정이 프로토콜 스택을 거쳐서 진행되기 때문에 패킷TCP/IP 전송처리 뿐만 아니라 여러 요인으로 인하여 소켓 버퍼의 데이터가 전송되지 않을 수 있, 다 또한 리눅스 네트워킹 제어 흐름에서 살펴보았듯이 계층에서 직접 전송이 진행되. Link 지 않으면 나중에 데이터를 전송하기 위해 커널의 인터럽트 서비스 루틴에 패킷을 등록한 다 이러한 이유로 디바이스 드라이버에서의 전송은 세 개의 함수로 구현된다 가장 먼저. . 패킷전송을 수행하는 함수 전송시간이 초과되었을 경우에 수행되는 함수 인터럽트를 처리, , 하는 함수이다. 패킷의 전송처리a. 패킷전송은 리눅스의 네트워킹에서 살펴봤듯이 사용자가 를 호출하면 궁극적으로read() 코드 와 같은 함수가 호출된다 이 함수는 구조체의 함[ 1-5] . net_device hard_start_xmit static int cs8900_1_stop (struct net_device *dev) { /* disable ethernet controller */① cs8900_1_write (dev,PP_BusCTL,0); /* uninstall interrupt handler */② free_irq (dev->irq,dev); /* stop the queue */③ netif_stop_queue (dev); MOD_DEC_USE_COUNT; return (0); } 코드 필드에 등록된[ 1-4] stop cs8900_1_stop()
  • 37. - 37 - 수 포인터 필드에 등록된 함수로 디바이스 드라이버에서는CS8900 cs8900_1_send_start 이라는 이름을 사용하였다. static int cs8900_1_send_start (struct sk_buff *skb,struct net_device *dev) { cs8900_1_t *priv = (cs8900_1_t *) dev->priv; u16 status; spin_lock_irq(&priv->lock); 상위 계층의 송신 큐를 중단/* */① netif_stop_queue (dev); 디바이스에서 데이터를 전송하도록 설정/* */② cs8900_1_write (dev,PP_TxCMD,TxStart (After5)); cs8900_1_write (dev,PP_TxLength,skb->len); 디바이스의 상태 검사/* */③ status = cs8900_1_read (dev,PP_BusST); if ((status & TxBidErr)) { spin_unlock_irq(&priv->lock); printk (KERN_WARNING "%s: Invalid frame size %d!n", dev->name,skb->len); priv->stats.tx_errors++; priv->stats.tx_aborted_errors++; priv->txlen = 0; return (1); } if (!(status & Rdy4TxNOW)) { spin_unlock_irq(&priv->lock); printk (KERN_WARNING "%s: Transmit buffer not free!n",dev->name); priv->stats.tx_errors++; priv->txlen = 0; /* FIXME: store skb and send it in interrupt handler */ return (1);
  • 38. - 38 - 데이터의 전송을 처리하는 의 설계는 일반적으로 여섯 단계를 거친다hard_start_xmit() . 먼저 인자로 넘겨받은 소켓버퍼 구조체에서 전송할 데이터와 그 길이 등의 정보를 추출하여 보낼 준비를 한다 디바이스 드라이버에서는 이 과정이 생략되어 다음 단계를 실. CS8900 행할 때 소켓 버퍼 구조체에서 필요한 값을 바로 추출해서 인자로 넣어주고 있다 다음 단. 계는 디바이스가 패킷을 전송할 동안 상위 계층인 계층에서 패킷 전송요구를 할 수Link 없도록 번 코드와 같이 를 호출하여 송신 큐를 중단한다1 netif_stop_queue() . 그 다음에는 번 코드와 같이 디바이스를 설정하여 데이터를 전송하도록 하는데 상식적2 , 인 개념으로는 이 단계는 가장 마지막에 있어야할 단계처럼 보인다 그렇지만 디바이스안의. 버퍼가 비어있는 상태에서 디바이스를 활성화 시키면 디바이스는 데이터가 버퍼에 들어오, 자마자 바로 데이터를 보낼 수 있기 때문에 미리 전송시작을 하는 것이다 이는 디바이스의. 전송 회로를 활성화시킨 것으로도 볼 수 있다. 디바이스의 전송회로를 활성화시킨 후 디바이스 드라이버는 번 코드와 같이 디CS8900 3 바이스 자체 함수인 로 디바이스의 상태를 읽고 데이터 프레임 길이와cs8900_1_read() , 전송 버퍼의 사용여부를 검사하여 다음 단계로 진행할지 검사한다. 번 코드가 실제로 데이터를 전송하는 부분으로 디바이스 내부의 버퍼에 소켓버퍼의 데4 이터를 집어넣는 과정이다 실제 전송하는 과정은 하드웨어에서 회로로 구성되어 있기 때문. 에 디바이스 드라이버의 작성자는 하드웨어 내의 버퍼에 데이터를 넣어주기만 하면 되는 것 이다 디바이스 드라이버에서는 자체 함수인 에서 수행. CS8900 cs8900_1_frame_write() 하는데 코드 에서 보듯이 계열의 함수를 이용하여 데이터를 하드웨어에 출력[ 1-6] out() 하고 있음을 볼 수 있다. } 디바이스 내의 버퍼에 전송할 데이터인 소켓 버퍼를 넣는다/* FIFO . */④ cs8900_1_frame_write (dev,skb); spin_unlock_irq(&priv->lock); 전송 시간을 기록/* */⑤ dev->trans_start = jiffies; dev_kfree_skb (skb); priv->txlen = skb->len; return (0); } 코드 필드에 등록된[ 1-5] hard_start_xmit cs8900_1_send_start()
  • 39. - 39 - static inline void cs8900_1_frame_write (struct net_device *dev,struct sk_buff *skb) { outsw (dev->base_addr,skb->data,(skb->len + 1) / 2); } 코드 디바이스 버퍼에 데이터를 넣는[ 1-6] cs8900_1_frame_write() 실제 전송이 끝났지만 네트워크 디바이스 드라이버는 전송이 시작된 시간을 상위 계층에, 알려주어야 한다 코드 의 번에서 상위 계층에 알려주는 작업을 수행하고 있다 참. [ 1-5] 5 . 고로 는 현재 시각을 나타내는 커널전역변수로써 커널은 이 변수의 값을 항상 업데이jiffies 트 한다 그러므로 현재시각을 기록하려면 이 변수의 값을 그대로 대입하면 된다 여기까지. . 여섯 단계의 전송 처리 과정을 살펴보았다 이들의 순서는 어느 정도 바뀔 수 있어도 단계. 를 생략하면 디바이스에서 데이터를 전송하지 않으므로 준수하여야 한다. 코드 의 번 뒤에 나오는 코드들은 전송함수에서 사용한 자료구조들을 정리만을[ 1-5] 5 수행하고 있다 또한 디바이스 자체의 관리용 구조체에 전송정보를 기록하고 있음을 볼 수. 있다. 전송 시간 초과 시의 처리b. 커널은 소켓 버퍼의 데이터가 전송되지 않을 경우 마지막으로 전송된 시간을 참조하여 시 간 초과가 발생하면 네트워크 디바이스 드라이버에게 적절한 처리를 요구한다 즉 커널은, . , 구조체의 필드에 등록된 함수를 호출하는데 여기서는net_device tx_timeout , 가 해당된다 이 함수는 코드 과 같다cs8900_1_transmit_timeout() . [ 1-7] . 이 함수에서 구현해야 하는 기능은 간단하다 마지막으로 전송이 요구된 데이터가 디바이. 스에서 무시되도록 처리하고 관련된 에러 통계를 갱신하고 데이터를 전송할 수 있도록 하, , 드웨어를 재설정한다 마지막으로 를 호출하여 커널에서 패킷을 다시. netif_wake_queue() 전송할 수 있도록 알린다 에서는 데이터를 무시하고 데이터를 재전송 할 수 있도. CS8900 , 록 설정하는 하드웨어 처리과정은 생략되었으며 번 코드에서 자체 관리용 구조체에 에러, 1 통계를 갱신하고 있다. 필드에 등록된 함수는 번과 같이 커널이 패킷을 재전송 하도록 반드시tx_timeout 2 를 호출하여야 한다netif_wake_queue() .
  • 40. - 40 - static void cs8900_1_transmit_timeout (struct net_device *dev) { 디바이스 내에서의 에러에 대한 처리 수행/* */① cs8900_1_t *priv = (cs8900_1_t *) dev->priv; priv->stats.tx_errors++; priv->stats.tx_heartbeat_errors++; priv->txlen = 0; 커널내의 송신 큐를 깨운다/* . */② netif_wake_queue (dev); } 코드 필드에 등록된[ 1-7] tx_timeout cs8900_1_transmit_timeout() 인터럽트 처리c. 그림 의 네트워킹 제어흐름에서 살펴본 바와 같이 인터럽트 서비스를 처리하는 것이[ 3] 필요하다 또한 대부분의 네트워크 디바이스 드라이버는 기본적으로 인터럽트를 처리하지. 않으면 송수신을 할 수 없기 때문에 인터럽트 처리는 디바이스 드라이버에서 중요한 부분을 차지한다 코드 은 인터럽트 서비스 함수에서 송신과 관련된 코드만을 정리한 것이. [ 1-8] 다. 네트워크 디바이스는 번 코드에 나온 것과 같이 다섯 개의 상태에 의해 인터럽CS8900 1 트가 발생한다 다섯 가지 인터럽트 상태는 파일에 정의되어 있으며. drivers/net/cs8900.h , 이들 상태는 문으로 구현되어 있다 인터럽트 상태와 구현방법은 일반적인 네switch-case . 트워크 디바이스 드라이버에서 거의 공식과 같이 적용되고 있다. 첫 번째 상태인 는 수신된 데이터가 있을 때 디바이스에서 발생한다 네트워크RxEvent , . 디바이스 드라이버는 데이터의 수신을 인터럽트에서 처리해야 하므로 수신된 데이터가 있으 면 수신한 데이터를 소켓 버퍼 구조체로 변환하여 커널내의 프로토콜 스택에 전달TCP/IP 한다 수신된 데이터를 처리하는 방식은 을 사용하는 방식과 단순히 수신처리만 하는. poll , 방식이 있는데 방식은 커널 에서는 지원하지 않는 방식이므로 후자만 설명한다, poll 2.4 . 참고로 방식은 커널 에서부터 지원되는 방식이다poll 2.6 . 두 번째 상태인 는 이전에 디바이스가 가지고 있던 데이터 송신을 완료하였을TxEvent 경우에 발생한다 이때 를 호출하여 커널내의 프로토콜 스택. netif_wake_queue() TCP/IP 에 알려주어야 한다 자세한 구현방법은 코드 을 설명할 때 살펴본다. [ 1-8] . 세 번째 상태인 는 디바이스 안의 버퍼가 비어서 송신이 가능해졌을 경우BufEvent FIFO 에 발생한다 이 경우 또한 를 호출하여 커널내의 프로토콜. netif_wake_queue() TCP/IP 스택에 알려주어야 한다.
  • 41. - 41 - 네 번째 상태인 는 수신에러가 발생했을 경우 발생한다 이때 네트워크 디바이스RxMISS . 드라이버에서 수생해야 하는 것은 수신 에러에 대한 통계 정보를 갱신하고 네트워크 하드, 웨어의 에러 상태를 없애 주어야 한다. 마지막 상태인 는 송신에러가 발생했을 경우 발생한다 이때는 수신에러와 발생했TxCOL . 을 경우와 마찬가지로 네트워크 디바이스 드라이버에서는 송신 에러에 대한 통계 정보를 갱 신하고 네트워크 하드웨어의 에러 상태를 클리어 해야 한다, . 네트워크 디바이스 드라이버의 인터럽트 서비스는 코드 의 번과 같이 가장 먼저[ 1-8] 1 구조체를 얻으면서 시작한다 나머지는 상태별 인터럽트 구현만이 인터럽트 서net_device . 비스 함수를 구성하고 있다. 코드 의 번에서 번까지는 송수신 상태별로 인터럽트를 구현하고 있다 번 코드[ 1-8] 3 5 . 3 에서 디바이스에서 송신이 끝나서 데이터 전송이 가능해졌을 경우를 처리한다 먼저 디바이. 스의 정확한 상태를 알기위해 디바이스 내의 레지스터에서 값을 읽어서 판단한다 그 후에. 는 디바이스에 에러가 나서 송신이 가능해진 것인지 정상적으로 동작한 것인지를 판단하여, 통계 처리를 한다 의 디바이스 드라이버에서는 지금까지 살펴보았듯이 변수. CS8900 priv 를 이용해서 통계를 구하고 있다 통계처리가 끝나면 디바이스는 를 호. netif_wake_queue() 출해서 커널내의 송신 큐에 재전송 또는 새로운 패킷 전송을 시작하라고 알려주며 작업을 끝낸다. 번 코드는 버퍼가 비어있을 경우에 수행되는 코드이다 여기서는 디바이스에 에러가 발4 . 생했는지 검사한 후 에러가 발생했으면 통계 처리를 하고 마지막으로, netif_wake_queue() 를 호출해서 커널내의 송신 큐에 재전송을 시작하라고 알려주며 작업을 끝낸다 정상적인. 경우에는 통계 처리를 하지 않는데 이는 디바이스가 패킷을 보내고 있거나 받고 있지 않았, 기 때문이다 번 코드는 송신 에러 시 수행되는 코드인데 특별한 처리 없이 통계 작업만. 5 , 을 수행한다. /* drivers/net/cs8900.h PP_ISQ */① #define PP_ISQ 0x0120 /* Interrupt Status Queue */ #define RxEvent 0x0004 #define TxEvent 0x0008 #define BufEvent 0x000c #define RxMISS 0x0010 #define TxCOL 0x0012 /* drivers/net/lds2000_cs8900_1.c */ static void cs8900_1_interrupt (int irq,void *id,struct pt_regs *regs) { 구조체를 가장 먼저 얻어야 한다/* net_device . */②
  • 42. - 42 - struct net_device *dev = (struct net_device *) id; cs8900_1_t *priv = (cs8900_1_t *) dev->priv; u16 status; while ((status = cs8900_1_read (dev,PP_ISQ))) { switch (RegNum (status)) { 송신이 끝났을 때/* */③ case TxEvent: 디바이스의 상태를 읽음/* */ priv->stats.collisions += ColCount (cs8900_1_read (dev,PP_TxCOL)); 디바이스에 에러가 있을 경우의 통계 처리/* */ if (!(RegContent (status) & TxOK)) { priv->stats.tx_errors++; if ((RegContent (status) & Out_of_window)) priv->stats.tx_window_errors++; if ((RegContent (status) & Jabber)) priv->stats.tx_aborted_errors++; break; 정상적으로 동작하였을 경우의 통계 처리/* */ } else if (priv->txlen) { priv->stats.tx_packets++; priv->stats.tx_bytes += priv->txlen; } priv->txlen = 0; 커널에 알려 송신 큐를 재시작/* */ netif_wake_queue (dev); break; 버퍼가 비었을 때/* */④ case BufEvent: 에러 시 통계처리/* */ if ((RegContent (status) & TxUnderrun)) { priv->stats.tx_errors++;
  • 43. - 43 - 네트워크 디바이스에서의 패킷 수신Vi) 수신 처리는 가 데이터 수신을 위해서 계속 디바이스 드라이버를 검사할 수 없기 때CPU 문에 보통 인터럽트 처리부터 수신이 시작된다 일반적인 네트워크 디바이스 드라이버는 인. 터럽트가 발생하면 인터럽트 서비스 함수에서 수신 상태를 확인하고 수신 처리를 담당하는, 함수를 호출한다 수신 처리용 함수는 패킷 송신의 인터럽트를 설명할 때 수신한 데이터를. 소켓 버퍼 구조체로 변환하여 커널내의 프로토콜 스택에 전달한다고 설명하였다TCP/IP . 인터럽트 처리a. 코드 의 번은 데이터가 수신되었을 때 디바이스가 이 인터럽트를 발생시킨다 여[ 1-9] 1 . 기서는 추가의 작업 없이 바로 수신된 데이터를 처리하는 함수인 를cs8900_1_receive() 호출하고 있다. 코드 의 번은 송신시 인터럽트 처리부분에서 설명하였듯이 버퍼가 비었을 경우에[ 1-9] 2 발생한다 이 부분 또한 수신에러가 발생하였는지 검사한 다음 에러가 발생했으면 통계처리. 를 하고 아니면 아무 일도 하지 않는다 즉 송신시의 인터럽트 처리 부분과 합하면 코드. , [ 의 알고리즘과 같은데 버퍼가 비었을 경우에는 송수신 에러가 발생하였는지 검사하고 에9] , 러가 발생하였으면 알맞은 통계처리를 해야 한다는 것을 알 수 있다. priv->stats.tx_fifo_errors++; priv->txlen = 0; netif_wake_queue (dev); } 정상일 경우 아무 일도 하지 않음/* */ break; 송신 에러가 발생했을 때/* */⑤ case TxCOL: priv->stats.collisions += ColCount (cs8900_1_read (dev,PP_TxCOL)); break; } } } 코드 필드에 등록된 함수의 송신처리 코드[ 1-8] irq cs8900_1_interrupt()
  • 44. - 44 - 버퍼가 비었음case ( ): 송신 에러 발생if ( ) 송신 에러에 맞는 통계 처리( ); 수신 에러 발생else if ( ) 수신 에러에 맞는 통계 처리( ); else 아무 일도 하지 않음( ); break; 코드 디바이스 버퍼가 비었을 경우의 인터럽트 처리 알고리즘[ 9] 코드 의 번은 수신 에러 시 수행되는 코드인데 여기서도 송신 에러 시와 마찬가[ 1-9] 3 , 지로 단순히 통계 처리만을 수행함을 알 수 있다. static void cs8900_1_interrupt (int irq,void *id,struct pt_regs *regs) { struct net_device *dev = (struct net_device *) id; cs8900_1_t *priv = (cs8900_1_t *) dev->priv; u16 status; while ((status = cs8900_1_read (dev,PP_ISQ))) { switch (RegNum (status)) { 수신된 데이터가 있을 때/* */① case RxEvent: cs8900_1_receive (dev); break; 버퍼가 비었을 때/* */② case BufEvent: 에러 시 통계처리/* */ if ((RegContent (status) & RxMiss)) { u16 missed = MissCount (cs8900_1_read (dev,PP_RxMISS)); priv->stats.rx_errors += missed; priv->stats.rx_missed_errors += missed; } 정상일 경우 아무 일도 하지 않음/* */ break; 송신 에러가 발생했을 때/* */③
  • 45. - 45 - 수신 처리 함수b. 대부분의 네트워크 디바이스가 수신 처리 함수를 코드 과 거의 동일하게 구현한[ 1-10] 다 그것은 프로토콜 스택에서 제공하는 함수들을 사용하여 받은 데이터를. TCP/IP TCP/IP 프로토콜 스택이 이해할 수 있는 소켓 버퍼의 형태로 변환해야 하기 때문이다 디바이스마. 다 다른 부분은 코드 의 번 번 번 코드와 같이 디바이스에서 값을 얻어오거나[ 1-10] 1 , 2 , 6 , 통계처리를 하는 부분뿐이다 그러나 번 코드의 경우에는 번 코드에서 에러인지 판단하. 1 2 기 위한 상태 정보와 소켓 버퍼를 할당받기 위한 수신 데이터의 크기를 디바이스에서 받아, 와 한다 번 코드의 경우에는 에러일 경우 통계처리를 하고 바로 복귀한다. 2 . case RxMISS: status = MissCount (cs8900_1_read (dev,PP_RxMISS)); priv->stats.rx_errors += status; priv->stats.rx_missed_errors += status; break; } } } 코드 필드에 등록된 의 수신처리 코드[ 1-9] irq cs8900_1_interrupt() static void cs8900_1_receive (struct net_device *dev) { cs8900_1_t *priv = (cs8900_1_t *) dev->priv; struct sk_buff *skb; u16 status,length; 디바이스의 수신 상태와 데이터의 크기를 구함/* */① status = cs8900_1_read (dev,PP_RxStatus); length = cs8900_1_read (dev,PP_RxLength); 수신 상태가 에러이면 통계처리를 하고 복귀/* */② if (!(status & RxOK)) { priv->stats.rx_errors++; if ((status & (Runt | Extradata))) priv->stats.rx_length_errors++; if ((status & CRCerror)) priv->stats.rx_crc_errors++; return; }
  • 46. - 46 - 코드 의 번에서 번까지 변환할 소켓버퍼에 관한 연산을 수행하게 된다 가장[ 1-10] 3 6 . 먼저 변환 받을 소켓 버퍼를 할당받아야 한다 소켓 버퍼를 할당받기 위해서는 번 코드에. 3 서처럼 를 호출해야 한다 는 에 구dev_alloc_skb() . dev_alloc_skb() include/linux/skbuff.h 현되어 있으며 의 원형은 코드 과 같다 이 함수는 프로토콜, dev_alloc_skb() [ 10] . TCP/IP 스택 내의 소켓 버퍼 풀에서 빈 소켓 버퍼를 받아오는 역할을 한다. #include <linux/skbuff.h> static inline struct sk_buff *dev_alloc_skb(unsigned int length); 코드 의 원형[ 10] dev_alloc_skb() 소켓 버퍼 할당/* */③ if ((skb = dev_alloc_skb (length + 4)) == NULL) { priv->stats.rx_dropped++; return; } skb_reserve (skb,2); 할당받은 소켓 버퍼에 받은 데이터를 채움/* */④ skb->dev = dev; cs8900_1_frame_read (dev,skb,length); 소켓 버퍼의 프로토콜 타입 설정/* */⑤ skb->protocol = eth_type_trans (skb,dev); 소켓 버퍼를 커널에 전달/* */⑥ netif_rx (skb); 수신 데이터에 대한 통계 처리/* */⑦ dev->last_rx = jiffies; priv->stats.rx_packets++; priv->stats.rx_bytes += length; } 코드 디바이스 드라이버의 수신 처리 함수[ 1-10] cs8900_1_receive()
  • 47. - 47 - 그 다음에는 소켓 버퍼에 대한 연산을 수행하는 를 호출한다 이 함수의skb_reserve() . 원형은 코드 과 같다 이 함수는 소켓 버퍼의 데이터 공간을 앞쪽과 뒤쪽으로 두 번째[ 11] . 인자의 크기만큼 확장한다 이 함수는 메모리를 할당받는 것이 아니라 단순히 소켓 버퍼의. 필드만을 갱신한다data, tail, len . #include <linux/skbuff.h> static inline void skb_reserve(struct sk_buff *skb, unsigned int len); 코드 의 원형[ 11] dev_alloc_skb() 여기서 의 호출에서 데이터의 크기보다 약간 더 큰 데이터를 할당하고dev_alloc_skb() , 로 소켓버퍼의 크기를 조절하는 것은 디바이스 내에 있는 버퍼가 링 구조로skb_reserve() 이루어져 있기 때문에 소켓 버퍼의 앞뒤에 여유 공간을 두어서 디바이스 내부 버퍼로부터 원활한 복사를 하기 위한 것이다. 번 코드에서는 현재 할당받은 소켓 버퍼 데이터를 사용하는 디바이스가 무엇인지 알려4 주기 위하여 소켓 버퍼의 필드에 디바이스의 구조체의 선두주소를 대입하dev net_device 였다 그 다음에 나오는 코드는 소켓 버퍼에 받은 데이터를 복사하는 코드이다. . 소켓 버퍼에 수신된 데이터를 복사해서 넣은 후에는 코드 의 번과 같이[ 1-10] 5 를 사용하여 소켓 버퍼의 필드들을 이더넷 타입으로 설정한다 이 함수는eth_type_trans() . 구조체의 프로토콜 의존적인 필드들을 자동으로 세팅해주는 와net_device ether_setup() 비슷한 역할을 하는 함수이다 이 함수의 원형은 코드 와 같다. [ 12] . #include <linux/etherdevice.h> unsigned short eth_type_trans(struct sk_buff *skb, struct net_device *dev); 코드 함수의 원형[ 12] eth_type_trans() 코드 의 번까지 오면 소켓 버퍼에 대한 모든 처리가 끝난 것이다 여기서는[ 1-10] 6 . 를 호출하여 소켓 버퍼를 프로토콜 스택의 계층으로 보낸다 그림netif_rx() TCP/IP Link . [ 의 제어흐름에서 보았듯이 는 커널내의 인터럽트 요청 큐에 소켓 버퍼를 등록3] , netif_rx() 하여 준다 그러면 커널 인터럽스 서비스를 수행하는 커널 스레드 가 이를 발견하. ksoftirqd 여 상위 계층으로 계속 전달을 하는 것이다 데이터가 수신된 시간은 이 시점에서. 를 호출하고 난 바로 후임을 번 코드를 통해 알 수 있다netif_rx() 7 .
  • 48. - 48 - 네트워크 디바이스의 통계 처리Vii) 네트워크 디바이스 드라이버는 패킷의 송수신에 관련된 통계 정보를 관리해야 한다 일반. 적인 네트워크 디바이스 드라이버는 디바이스 드라이버의 전역 구조체 변수CS8900 priv 와 같이 디바이스 자체 관리용 구조체로 관리하고 드라이버 내의 루틴들의 곳곳에서 통계, 처리를 수행한다 그러나 디바이스에서 처리하는 통계는 커널 내의 프로토콜 스택. TCP/IP 또한 사용해야 하므로 미리 약속되어진 통계에 관한 자료구조가 필요하다. 그래서 커널에는 네트워크 디바이스가 처리해야할 네트워킹 통계에 관한 구조체가 존재한 다 그 구조체는 파일에 선언되어 있는 구조. include/linux/netdevice.h net_device_stats 체이다 이 구조체는 총 개의 필드를 가지고 있으며 이에 대한 상세한 설명은 코드. 23 [ 13] 과 같다 또한 이 구조체는 일반적으로 디바이스 자체 관리용 구조체 안에 멤버 변수로 선. 언되어 사용된다. struct net_device_stats { 수신된 총 패킷 수unsigned long rx_packets; /* */ 송신한 총 패킷 수unsigned long tx_packets; /* */ 수신된 패킷의 총 바이트 수unsigned long rx_bytes; /* */ 송신한 패킷의 총 바이트 수unsigned long tx_bytes; /* */ 수신시 에러난 패킷의 수unsigned long rx_errors; /* */ 송신시 에러난 패킷의 수unsigned long tx_errors; /* */ 수신버퍼에 공간이 없어 버려진 패킷 수unsigned long rx_dropped; /* */ 송신버퍼에 공간이 없어 버려진 패킷 수unsigned long tx_dropped; /* */ 수신된 멀티캐스트 패킷 수unsigned long multicast; /* */ 패킷 충돌이 발생한 횟수unsigned long collisions; /* */ 수신에러 필드에 대한 자세한 정보/* rx_errors */ 패킷길이 에러의 횟수unsigned long rx_length_errors; /* */ 수신 버퍼 오버플로우 에러의 횟수unsigned long rx_over_errors; /* */ 에러의 횟수unsigned long rx_crc_errors; /* CRC */ 프레임 에러의 횟수unsigned long rx_frame_errors; /* */ 디바이스 버퍼 오버플로우 에러의 횟수unsigned long rx_fifo_errors; /* */ 수신되지 않은 패킷의 수unsigned long rx_missed_errors; /* */ 송신에러 필드에 대한 자세한 정보/* tx_errors */ 송신 취소가 되어 발생한 에러의 횟수unsigned long tx_aborted_errors; /* */
  • 49. - 49 - 앞서 살펴보았듯이 구조체에는 통계를 관리하는 함수를 등록하는net_device get_stats 함수 포인터 필드가 있다 이 필드에는 에서 가. cs8900_1_probe() cs8900_1_get_stats() 등록되어 있다 이 함수에 대한 코드는 코드 과 같다 이 함수는 단순히 디바이스. [ 1-11] . 자체 관리용 구조체인 변수에서 구조체 변수인 필드를 반환한priv net_device_stats stats 다. static struct net_device_stats *cs8900_1_get_stats (struct net_device *dev) { cs8900_1_t *priv = (cs8900_1_t *) dev->priv; return (&priv->stats); } 코드 디바이스 드라이버의 통계 처리 함수[ 1-11] cs8900_1_get_stats() unsigned long tx_carrier_errors; 캐리어가 감지되지 않아 발생한 에러의 횟수/* */ 디바이스 버퍼 오버플로우 에러의 횟수unsigned long tx_fifo_errors; /* */ unsigned long tx_heartbeat_errors; unsigned long tx_window_errors; /* for cslip etc */ unsigned long rx_compressed; unsigned long tx_compressed; }; 코드 통계 처리 관련 구조체[ 13] net_device_stats
  • 50. - 50 - 결론III. 분석 결과1. 여러 통계적 자료를 통해 의 시장의 성장률이 크게 증가하고 있음을 확인할 수 있으PDA 며 그에 맞는 의 개발의 중요성도 높아지고 있다 현재는 대 운영체제간의 경쟁PDA OS . 3 에서 팜 가 선두를 유지하고 있으며 그 가운데 리눅스 운영체제는 공개성과 안정성을OS , 바탕으로 제 의 운영체제로 자리 잡을 것으로 예상된다 리눅스가 아직 용 표준이 마3 . PDA 련되지 않았다는 문제점을 가지고 있지만 시장조사회사 가트너의 한 분석가는 소비“PDA 자는 기능만 충족되면 어떤 가 탑재돼있든 관심을 보이지 않는다 면서 세계적으로OS .” “ 모바일기기용 리눅스프로그램 개발자들의 모임이 수천 개에 달할 정도로 용 리눅스PDA 는 프로그램 숫자만으로 및 의 와 겨룰 수 있을 것 으로 전망했다OS Palm MS OS ” . 디바이스 드라이버와 커널에 관하여 여러 이름 있는 웹사이트 저서들을 대부분 커널 또, 는 디바이스 드라이버만을 다룬다 그러나 디바이스 드라이버와 커널은 서로 뗄 수 없는 관. 계로 디바이스 드라이버의 작성방법은 리눅스 커널의 구현과 밀접한 관계가 있다 본 논문. 은 네트워크 디바이스 드라이버의 작성방법에 한 하여 커널과 밀접한 관계를 논하려고 노력 하였다 따라서 리눅스의 심화적인 이해를 통해 디바이스 드라이버의 제작 기술을 습득하고. 더불어 의 코드를 읽어 이해할 수 있는 능력을 배양한다OS .
  • 51. - 51 - 참 고 문 헌■ 도서1. 이상근 역 임베디드 개발자를 위한 리눅스 커널 심층 분석 에이콘출판사[1] , “ ”, , 2004 심마로 역 리눅스 커널의 이해 한빛미디어[2] , “ ”, , 2003 유영창 리눅스 디바이스 드라이버 한및미디어[3] , “ ”, , 2004 박재호 임베디드 리눅스 한빛미디어[4] , “IT EXPERT ”, , 2003 조유근 외 리눅스 매니아를 위한 커널 프로그래밍 교학사[5] , “ ”, , 2002 외[6] Jonathan , “Linux Device Driver 3rd”, O'REILLY, 2005 웹 사이트1. 코어벨[1] , (www.corebell.co.kr) [2] Korea Embedded Linux Project, (kelp.or.kr/korweblog) [3] Cross-Referencing Linux, (http://lxr.linux.no/) [4] Korea Linux Documents Project, (kldp.org) [5] KorOne.net, (www.korone.net) 월간 임베디드 월드[6] , (www.embeddedworld.co.kr) 하이버스[7] , (www.hybus.net) 한백전자[8] , (www.hanback.co.kr) 휴인스[9] , (www.huins.com) [10] FA. Linux (www.falinux.com) [11] Red Hat, (www.redhat.com) [12] GNU official homepage, (www.gnu.org)