sk_buff 구조체
네트워크 디바이스 드라이버가 패킷을 받게되면 인터럽트가 발생한다. 인터럽트 함수가 패킷의 도착시에 호출되며 디바이스의 레지스터 상태를 확인해서 이것이 RX(receive)인 경우에 netif_rx()함수를 호출 해서 네트워크의 패킷이 도착했음을 상위에 알려준다.
sk_buff구조체는 소켓연결에서 사용하는 구조체로 사용자의 데이터와 각종 프로토콜의 정보를 담는데 쓰인다. 커널내에서 메모리간의 복사(copy) 횟수를 줄여주며, 각각의 프로토콜에서 자신만의 정보를 나타내기에
용이한 점이 있다. -> 네트워크로 전송되는 패킷을 나타내는 구조체
http://www.zdnet.co.kr/builder/platform/nix/0,39031679,39136665,00.htm
Field Description
struct sk_buff *next, *prev sk_buff list의 다음과 이전을 가리키는 포인터
struct sk_buff_head *list 현재 sk_buff가 속한 sk_buff_head에 대한 포인터
struct sock *sk 현재 sk_buff를 사용하는 INET sock에 대한 포인터
struct timeval stamp sk_buff가 도착한 시간
struct net_device *dev 받거나 보내는 Network device의 구조체에 대한 포인터(Network device driver를 참고)
union{} h Transport layer(INET아래에 있는 layer) 들의header정보
union{} nh Network layer(Transport layer아래에 있는 layer)들의 header정보
union{} mac Mac layer(Medium Access Control: Link layer –Network layer아래에 있는 layer) 의 header정보
struct dst_entry 목적지 주소의 entry에 대한 포인터
char cb[48] Control buffer( 모든 layer에서 사용가능하다.)
unsigned int len 실제 데이터의 길이
unsigned int csum Checksum
volatile char used 데이터가 사용자에게 넘어갔음을 나타냄
unsigned char cloned sk_buff_head가 clone(복제)되었음을 나타냄
unsigned char pkt_type 패킷의 타입
unsigned char ip_summed IP checksum으로 driver가 제공함
__u32 priority 패킷의 queueing 우선순위
atomic_t users 패킷의 사용자 카운트
unsigned short protocol 드라이버로부터 받은 패킷의 프로토콜
unsigned short security 패킷의 보안 레벨
unsigned int truesize 버퍼의 실제 크기
unsigned char *head 데이터 버퍼의 head를 가리키는 포인터
unsigned char *data 데이터 버퍼의 데이터 부분을 가리키는 포인터
unsigned char *tail 데이터 버퍼의 데이터 마지막 부분을 가리키는 포인터
unsigned char *end 데이터 버퍼의 마지막을 가리키는 포인터
void (*destructor)(struct sk_buff) sk_buff 구조체를 없애는 함수에 대한 포인터
... 나머지 커널의 구성(configuration)에 따른 변수들
struct sk_buff_head {
/* These two members must be first. */
struct sk_buff * next;
struct sk_buff * prev;
__u32 qlen;
spinlock_t lock;
};
Rx Controller
현재 IP Protocol이 receive 구조를 살펴보면 Device Driver에서 Interrupt를 받고 status를 확인해 Data를 받은 status면 Data를 Device 에서 가져오고 socket buffer에 넣어netif_rx() 함수를 호출하여 socket buffer를 현재 CPU의 softnet_data 구조체의 input_pkt_queue에 넣고, dev structure의 poll_list 정보를 softnet_data structure의 poll_list에 추가한 후 NET_RX_SOFTIRQ를 발생시킨다. 나머지 부분은 softirq로 처리하며 이에 대한 처리는 <net/core/dev.c>에 정의된 net_rx_action() 함수가 맡고 있다
이와 같이 softirq를 발생시켜 후 처리를 IP Stack 에서 하도록 설계 되어 있다.
int netif_rx(struct sk_buff *skb)
{
struct softnet_data *queue;
unsigned long flags;
/* if netpoll wants it, pretend we never saw it */
if (netpoll_rx(skb))
return NET_RX_DROP;
if (!skb->tstamp.off_sec)
net_timestamp(skb);
/*
* The code is rearranged so that the path is the most
* short when CPU is congested, but is still operating.
*/
local_irq_save(flags);
queue = &__get_cpu_var(softnet_data); //받은패킷들을 저장할 CPU에 배정된 큐
__get_cpu_var(netdev_rx_stat).total++;
if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
if (queue->input_pkt_queue.qlen) {
enqueue:
dev_hold(skb->dev);
__skb_queue_tail(&queue->input_pkt_queue, skb);
local_irq_restore(flags);
return NET_RX_SUCCESS;
}
netif_rx_schedule(&queue->backlog_dev);
goto enqueue;
}
__get_cpu_var(netdev_rx_stat).dropped++;
local_irq_restore(flags);
kfree_skb(skb);
return NET_RX_DROP;
}
local_irq_save() 함수를 알기전에 cli()라는 함수를 알아야 합니다.
cli 는 clear Interrupt 의 약자로서 CPU 가 interrupt signal을 받더라도
이를 무시하도록 하는 것입니다. 하드웨어 인터럽트와 소프트웨어 모두다
입니다.
이를 통해 synchronization 문제를 해결합니다.
data corruption 을 막는 거죠.
cli 로 critical region 을 보호한 후 sti 로 interrupt signal 받아들이도록
세팅해주어야 합니다.
문제는 sti 로 인터럽트를 받아들이기로 세팅하면 cli 호출
전에 인터럽트를 받아
들이기로 한것인지 아닌지를 깡그리 무시한채 sti 세팅합니다.
cli 호출 전에 인터럽트를 받아들이는 상태였다면 다행이지만
인터럽트 무시 상태였다면 sti 호출후 상태가 틀려지게 되죠.
즉 cli 함수 사용전의 인터럽트 관련 세팅 상태가 원상복귀
되지 않는거죠.
그래서 사용하는 것이 local_irq_save() 함수 입니다.
패러미터로 들어가는
flag 변수(스택에 존재)에
현재 인터럽트 관련 플래그를 저장하는 IF 라는 레지스터
정보를 저장합니다.
cli 가 sti 와 짝을 이루듯 local_irq_save 는 local_irq_restore 와
짝을 이룹니다. local_irq_restore 함수가 호출되면서
flag 변수에 저장되었던 것을
다시 IF 레지스터에 넣는거죠.
그러면 local_irq_save 전의 인터럽트 관련 세팅 상태로 원상복귀됩니다.
printk
http://wiki.kldp.org/wiki.php/UnreliableGuideToHackingTheLinuxKernel#s-6.1
아래의 리스트는 netdev_max_backlog - 네트워크 패킷의 전체 갯수 (보통 300) - 와 같은 일반적인 네트워크 설정을 보여준다. 이 값은 패킷을 받을 때 네트워크의 BandWidth 를 제한할 수 있다. 리눅스는 버퍼를 비우기 위해 스케줄링이 일어날 때 까지 기다려야 한다 (bottom half 메카니즘에 의해 처리). 1000/HZ ms 일 때
300 * 100 = 30 000 packets HZ(Timeslice freq) packets/s 30 000 * 1000 = 30 M packets average (Bytes/packet) throughput Bytes/s |
만약 더 높은 throughput 을 원한다면 netdev_max_backlog 을 다음과 같이 증가시킬 수 있다:
echo 4000 > /proc/sys/net/core/netdev_max_backlog
참고: HZ 값에 주의하기 바란다: (alpha 나 arm-tbox 같은) 특정 환경에서는 이 값이 1000 으로 설정되어 있기 때문에 평균 300 MBytes/sec 의 throughput 이 나온다.
http://mindeater.tistory.com/282 netif_rx
http://www.ibm.com/developerworks/kr/library/l-linux-networking-stack/ 리눅스 네트워킹 스택분석
http://www.wegra.org/products/network/contents/UDP%20Analysis.pdf udp 분석
http://kernelstudy.net/3 cpu별 변수 사용
http://www.zdnet.co.kr/builder/platform/nix/0,39031679,39136665,00.htm 네트워크 서브시스템
http://vger.kernel.org/~davem/skb.html 리눅스커널
'Sabzil' 카테고리의 다른 글
ABI (Application Binary Interface) (0) | 2009.03.17 |
---|---|
Nefif_rx (0) | 2009.03.17 |
usleep에 대해서 (0) | 2009.01.08 |
strtok_r (0) | 2008.09.29 |
this is RAM (0) | 2008.08.21 |
nit_if.h (0) | 2008.07.17 |
about ftp (0) | 2008.07.15 |
amd64 and x86 endian order (0) | 2008.06.23 |
glibc-2.5/elf/dl-runtime.c 코드 분석을 통한 lazy binding 재배치 방식 분석 (0) | 2008.06.23 |
xss in 엠파스 게시판 (0) | 2008.06.22 |