Linux串口上網(wǎng)的程序實(shí)現方法
[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:
編寫(xiě)字符設備驅動(dòng)程序用戶(hù)空間的進(jìn)程主要通過(guò)兩種方式和內核空間模塊打交道,一種是使用proc文件系統,另一種是使用字符設備。本文所描述的兩個(gè)字符設備sending device 和receiving device事實(shí)上是內核空間和用戶(hù)空間交換數據的緩存區,編寫(xiě)字符設備驅動(dòng)實(shí)際上就是編寫(xiě)用戶(hù)空間讀寫(xiě)字符設備所需要的內核設備操作函數。 在頭文件中,我們定義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的主設備號。在內核空間,驅動(dòng)程序是根據主、次設備號識別設備的,而不是設備名;本文的字符設備的次設備號都是0,主設備號是用戶(hù)定義的且不能和系統已有的設備的主設備有沖突。IOCTL_SET_BUSY _IOWR(MAJOR_NUM_TX,1,int)是ioctl的操作函數定義(從用戶(hù)空間發(fā)送命令到內核空間),主要作用是使得每次在同一時(shí)間,同一字符設備上,只可進(jìn)行一次操作。我們可以使用mknod來(lái)建立這兩個(gè)字符設備 [root@localhost]#mknod c 200 0 /dev/ed_rec
[root@localhost]#mknod c 201 0 /dev/ed_tx
設備建立后,編譯好的模塊就可以動(dòng)態(tài)加載了:[root@localhost]#insmod ed_device.o
為了方便對設備編程,我們還需要一個(gè)字符設備管理的數據結構: 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);
};
這個(gè)數據結構是用來(lái)保存字符設備的一些基本狀態(tài)信息。ssize_t (*kernel_write)(const char *buffer,size_t length,int buffer_size) 是一個(gè)指向函數的指針,它的作用是為偽網(wǎng)絡(luò )驅動(dòng)程序提供寫(xiě)字符設備數據的系統調用接口。magic字段主要是標志設備類(lèi)型號的,這里沒(méi)有別的特殊意義;busy字段用來(lái)說(shuō)明字符設備是否是處于忙狀態(tài),buffer指向內核緩存區,用來(lái)存放讀寫(xiě)數據;mtu保存當前可發(fā)送的網(wǎng)絡(luò )數據包最大傳輸單位,以字節為單位;lock的類(lèi)型是自旋鎖類(lèi)型spinlock_t,它實(shí)際以一個(gè)整數域作為鎖,在同一時(shí)刻對同一字符設備,只能有一個(gè)操作,所以使用內核鎖機制保護防止數據污染;data_len是當前緩存區內保存的數據實(shí)際大小,以字節為單位;file是指向設備文件結構struct file的一個(gè)指針,其作用主要是定位設備的私有數據 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沒(méi)有數據,用戶(hù)空間的read調用將被阻塞,并把進(jìn)程信息放于rwait隊列中。當有數據的時(shí)候,kernel_write()中的wake_up_interruptible()將喚醒等待進(jìn)程。kernel_write()函數定義如下: ssize_t kernel_write(const char *buffer,size_t length,int buffer_size)
{
if(length > buffer_size )
length = buffer_size;
memset(ed[ED_TX_DEVICE].buffer,0,buffer_size);
memcpy(ed[ED_TX_DEVICE].buffer,buffer,buffer_size);
ed[ED_TX_DEVICE].tx_len = length;
#ifdef LINUX_24
wake_up_interruptible(ed[ED_TX_DEVICE].rwait);
#endif
return length;
}
字符設備的操作及其相關(guān)函數調用過(guò)程如圖3 所示。 圖 3 當ed_device模塊被加載的時(shí)候,eddev_module_init()調用register_chrdev()內核API注冊ed_tx和ed_rec兩個(gè)字符設備。這個(gè)函數定義在linux/fs.h>: int register_chdev(unsigned int major, const char *, struct fle_operations *fops)
字符設備被注冊成功后,內核把這兩個(gè)字符設備加入到內核字符設備驅動(dòng)表中。內核字符設備驅動(dòng)表保留指向struct file_operations的一個(gè)數據指針。用戶(hù)進(jìn)程調用設備讀寫(xiě)操作時(shí),通過(guò)這個(gè)指針訪(fǎng)問(wèn)設備的操作函數, struct file_operations中的域大部分是指向函數的函數指針,指向用戶(hù)自己編寫(xiě)的設備操作函數。 struct file_operations ed_ops ={
#ifdef LINUX_24
NULL,
#endif
NULL,
device_read,
device_write,
NULL,
NULL,
device_ioctl,
NULL,
device_open,
NULL,
device_release,
};
注意到Linux2.4.x和Linux2.2.x內核中定義的struct file_operations是不一樣的。device_read()、device_write()、device_ioctl()、device_open()、device_release()就是需要用戶(hù)自己定義的函數操作了,這幾個(gè)函數是最基本的操作,如果需要設備驅動(dòng)程序完成更復雜的任務(wù),還必須編寫(xiě)其他struct file_operations中定義的操作。eddev_module_init()除了注冊設備及其操作外,它還有初始化字符設備結構struct ed_device,分配內核緩存區所需要的空間的作用。在內核空間,分配內存空間的API函數是kmalloc()。 下面介紹一下字符設備的主要操作例程device_open()、device_release()、device_read()、devie_write()。字符設備文件操作結構ed_ops中定義的指向以上函數的函數指針的原形: device_open: int(*open)(struct inode *,struct file *)
device_release: int (*release) (struct inode *, struct file *);
device_read: ssize_t (*read) (struct file *, char *, size_t, loff_t *);
device_write: ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
linux操作系統文章專(zhuān)題:linux操作系統詳解(linux不再難懂)linux相關(guān)文章:linux教程
評論