了,在两台终端上运行server守护程序,然后执行telnet:
[root@localhost test]# telnet 192.168.5.2
Trying 192.168.5.2...
Connected to 192.168.5.2 (192.168.5.2).
Escape character is '^]'.
Red Hat Linux release 9 (Shrike)
Kernel 2.4.20-8 on an i686
login:
编写字符设备驱动程序用户空间的进程主要通过两种方式和内核空间模块打交道,一种是使用proc文件系统,另一种是
使用字符设备。本文所描述的两个字符设备sending device 和receiving device事实上是内核空间和用户空间交换数据的缓存
区,编写字符设备驱动实际上就是编写用户空间读写字符设备所需要的内核设备操作函数。 在头文件中,我们定义
ED_REC_DEVICE为receiving device,名字是ed_rec;定义ED_TX_DEVICE为sending device,名字是ed_tx。 #define
MAJOR_NUM_REC 200
#define MAJOR_NUM_TX 201
#define IOCTL_SET_BUSY _IOWR(MAJOR_NUM_TX,1,int)
200和201分别代表receiving device 和 sending device的主设备号。在内核空间,驱动程序是根据主、次设备号识别设备
的,而不是设备名;本文的字符设备的次设备号都是0,主设备号是用户定义的且不能和系统已有的设备的主设备有冲突。
IOCTL_SET_BUSY _IOWR(MAJOR_NUM_TX,1,int)是ioctl的操作函数定义(从用户空间发送命令到内核空间),主要作用
是使得每次在同一时间,同一字符设备上,只可进行一次操作。我们可以使用mknod来建立这两个字符设备
[root@localhost]#mknod c 200 0 /dev/ed_rec
[root@localhost]#mknod c 201 0 /dev/ed_tx
设备建立后,编译好的模块就可以动态加载了:[root@localhost]#insmod ed_device.o
为了方便对设备编程,我们还需要一个字符设备管理的数据结构: struct ed_device{
int magic;
char name[8];
int busy;
unsigned char *buffer;
#ifdef LINUX_24
wait_queue_head_t rwait;
#endif
int mtu;
spinlock_t lock;
int data_len;
int buffer_size;
struct file *file;
ssize_t (*kernel_write)(const char *buffer,size_t length,int buffer_size);
};
这个数据结构是用来保存字符设备的一些基本状态信息。ssize_t (*kernel_write)(const char *buffer,size_t length,int
buffer_size) 是一个指向函数的指针,它的作用是为伪网络驱动程序提供写字符设备数据的系统调用接口。magic字段主要是
标志设备类型号的,这里没有别的特殊意义;busy字段用来说明字符设备是否是处于忙状态,buffer指向内核缓存区,用来存
放读写数据;mtu保存当前可发送的网络数据包最大传输单位,以字节为单位;lock的类型是自旋锁类型spinlock_t,它实际以
一个整数域作为锁,在同一时刻对同一字符设备,只能有一个操作,所以使用内核锁机制保护防止数据污染;data_len是当前
缓存区内保存的数据实际大小,以字节为单位;file是指向设备文件结构struct file的一个指针,其作用主要是定位设备的私有
数据 file-> private_data。定义字符设备struct ed_device ed[2],其中ed[ED_REC_DEVICE]就是receving
device,ed[ED_TX_DEVICE]就是sending device。如果sending device ED_TX_DEVICE没有数据,用户空间的read调用将