您的位置:首页 > 运维架构 > Linux

linux网卡驱动一点点基础知识

2013-01-15 23:01 246 查看
这算是真正的第一次接触linux驱动吧,为了实现在linux平台下的虚拟网卡,开始钻研linux驱动开发。

先了解linux设备, 分为三种类型设备: 字符设备,块设备,网络接口设备。

linux驱动开发基本就是围绕这三种设备驱动的开发。

字符设备按照字节为单位传输数据,像字节流一样的顺序访问,

块设备每次按照一个数据块的方式传输,一般是512的倍数,可随机访问,用于磁盘驱动等驱动中。

网络接口设备发送和接收网络数据块,它并不对应文件系统节点,而是系统分配一个唯一的名字,比如eth0等,

网络设备使用BSD套接字与与用户程序交互信息,块设备和字符设备都通过 open/read/write与用户层交互。

linux内核跟windows内核一样都是十分复杂的,好在linux提供内核源代码,所以对linux的内核的深入研究可以通过阅读代码获得。

windows平台就没这么幸运了, 但是所有的基本概念,在现代操作系统中,都是通用的。

比如都有自旋锁,都有双向链表,都有内核线程,都有进程调度,都有内存分页等等。

linux驱动开发,不论从代码量或者概念的理解上,都要比windows驱动简单。

///test.c

int __init init_func(void)

{

///驱动初始化,

printk(KERN_INFO" init driver\n");

return 0;

}

void __exit exit_func(void)

{

//驱动卸载

printk(KERN_INFO"unload driver.\n");

}

module_init( init_func );

module_exit( exit_func );

MODULE_LICENSE("GPL");

/////////////////////////////////////////////////

如上就可以实现一个最简单的linux驱动模块, 如下编译成 test.ko

make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(shell pwd) modules

insmod test.ko //加载驱动模块

rmmod test //卸载驱动模块,

这估计是世界上最简单的代码了,所以不要以为linux驱动框架跟windows的一样复杂。

以下是实现一个简单的网卡驱动代码框架。

/// net_card.c

struct net_device* net = NULL;

int send_packet( struct sk_buff* skb, struct net_device* net)

{

//上层协议驱动发送数据

return 0;

}

int __init init_func(void)

{

///驱动初始化,

net = alloc_netdev( 0, "net_card%d", ether_setup);

net->init = net_init;

net->hard_start_xmit = send_packet ;

.....

register_netdev( net );

return 0;

}

void __exit exit_func(void)

{

//驱动卸载

unregister_netdev( net );

free_netdev( net );

printk(KERN_INFO"unload driver.\n");

}

module_init( init_func );

module_exit( exit_func );

MODULE_LICENSE("GPL");

//////////////////////////////////////////////

编译生成 net_card.ko,用insmod插入模块,

调用 ifconfig -a, 就会看到 一个名字叫 net_card0 的网卡出现,

实现一个网卡驱动框架就这么简单,简直比windows平台的网卡驱动框架简单多了。

为了要实现一个虚拟网卡驱动,需要把字符设备驱动和网卡接口驱动很好的融合起来,这里边牵涉到一些基本东西,

比如自旋锁,概念跟windows平台自旋锁一个意思。

还有重要的一点,就是如何把从网卡的数据传输给字符设备,

网卡驱动数据传输的一个核心的数据结构 sk_buff,以及与他相关的

skb_dequeue,skb_queue_head,skb_queue_len等函数实现此数据结构操作。

linux不同于windows驱动,windows利用IRP作为数据传输的基础,在windows驱动中,IRP几乎无处不在。

在windows派遣函数中,如有数据需要等待,可通过IoMarkIrpPending设置IRP为挂起状态,派遣函数直接返回STATUS_PENDING,

等有数据可读时,再完成这个挂起的IRP。

linux却不同,它使用等待队列的方式挂起当前进程,直到有数据可读为止。

简单说,就是到 linux字符设备驱动的 read回调函数中 ,调用 wait_event使得当前进程处于休眠,

直到另外函数发现有数据可读时,调用 wake_up唤醒休眠进程。

字符设备驱动的write回调函数中,构造 sk_buff结构,然后把数据copy进去,再调用 netif_rx通知上层协议接收。

可以看到,linux中网卡驱动比windows中NDIS驱动简单许多了,

windows提供了 NDIS概念,而且NDIS还分为协议驱动,中间驱动,网卡驱动,尤其复杂的是中间驱动。

linux也有网络数据包过滤驱动,但是他的实现框架也比windows的简单。

最后来一句: linux用牛刀杀牛,windows用牛刀杀鸡。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: