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

 这算是真正的第一次接触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用牛刀杀鸡。

你可能感兴趣的:(驱动开发,linux)