驅動模塊中有一個file結構體。該結構體中有一個 loff_t 字段 用來維護當前讀寫位置。此回就拿loff_t這個字段來開刀。下面展示一個字符設備的驅動代碼,來自《Linux設備驅動開發詳解》-宋寶華一書
1 #include <linux/module.h> 2 #include <linux/slab.h> 3 #include <linux/types.h> 4 #include <linux/fs.h> 5 #include <linux/errno.h> 6 #include <linux/mm.h> 7 #include <linux/sched.h> 8 #include <linux/init.h> 9 #include <linux/cdev.h> 10 #include <asm/io.h> 11 #include <asm/system.h> 12 #include <asm/uaccess.h> 13 14 #define GLOBALMEM_SIZE 0x1000 /*全局內存最大4K字節*/ 15 #define MEM_CLEAR 0x1 /*清0全局內存*/ 16 #define GLOBALMEM_MAJOR 0 /*預設的globalmem的主設備號*/ 17 18 static globalmem_major = GLOBALMEM_MAJOR; 19 /*globalmem設備結構體*/ 20 struct globalmem_dev 21 { 22 struct cdev cdev; /*cdev結構體*/ 23 unsigned char mem[GLOBALMEM_SIZE]; /*全局內存*/ 24 struct semaphore sem; /*並發控制用的信號量*/ 25 }; 26 27 struct globalmem_dev *globalmem_devp; /*設備結構體指針*/ 28 29 /*文件打開函數*/ 30 int globalmem_open(struct inode *inode, struct file *filp) 31 { 32 /*將設備結構體指針賦值給文件私有數據指針*/ 33 filp->private_data = globalmem_devp; 34 return 0; 35 } 36 37 /*文件釋放函數*/ 38 int globalmem_release(struct inode *inode, struct file *filp) 39 { 40 return 0; 41 } 42 43 /* ioctl設備控制函數 */ 44 static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned 45 int cmd, unsigned long arg) 46 { 47 struct globalmem_dev *dev = filp->private_data; /*獲得設備結構體指針*/ 48 49 switch (cmd) 50 { 51 case MEM_CLEAR: 52 if (down_interruptible(&dev->sem)) 53 { 54 return - ERESTARTSYS; 55 } 56 memset(dev->mem, 0, GLOBALMEM_SIZE); 57 up(&dev->sem); //釋放信號量 58 59 printk(KERN_INFO "globalmem is set to zero\n"); 60 break; 61 62 default: 63 return - EINVAL; 64 } 65 return 0; 66 } 67 68 /*讀函數*/ 69 static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, 70 loff_t *ppos) 71 { 72 unsigned long p = *ppos; 73 unsigned int count = size; 74 int ret = 0; 75 struct globalmem_dev *dev = filp->private_data; /*獲得設備結構體指針*/ 76 77 /*分析和獲取有效的寫長度*/ 78 if (p >= GLOBALMEM_SIZE) 79 return count ? - ENXIO: 0; 80 if (count > GLOBALMEM_SIZE - p) 81 count = GLOBALMEM_SIZE - p; 82 83 if (down_interruptible(&dev->sem)) 84 { 85 return - ERESTARTSYS; 86 } 87 88 /*內核空間->用戶空間*/ 89 if (copy_to_user(buf, (void*)(dev->mem + p), count)) 90 { 91 ret = - EFAULT; 92 } 93 else 94 { 95 *ppos += count; 96 ret = count; 97 98 printk(KERN_INFO "read %d bytes(s) from %d\n", count, p); 99 } 100 up(&dev->sem); //釋放信號量 101 102 return ret; 103 } 104 105 /*寫函數*/ 106 static ssize_t globalmem_write(struct file *filp, const char __user *buf, 107 size_t size, loff_t *ppos) 108 { 109 unsigned long p = *ppos; 110 unsigned int count = size; 111 int ret = 0; 112 113 struct globalmem_dev *dev = filp->private_data; /*獲得設備結構體指針*/ 114 115 /*分析和獲取有效的寫長度*/ 116 if (p >= GLOBALMEM_SIZE) 117 return count ? - ENXIO: 0; 118 if (count > GLOBALMEM_SIZE - p) 119 count = GLOBALMEM_SIZE - p; 120 121 if (down_interruptible(&dev->sem))//獲得信號量 122 { 123 return - ERESTARTSYS; 124 } 125 /*用戶空間->內核空間*/ 126 127 128 if (copy_from_user(dev->mem + p, buf, count)) 129 ret = - EFAULT; 130 else 131 { 132 *ppos +=count; 133 ret = count; 134 135 printk(KERN_INFO "written %d bytes(s) from %d\n", count, p); 136 } 137 up(&dev->sem); //釋放信號量 138 return ret; 139 } 140 141 /* seek文件定位函數 */ 142 static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig) 143 { 144 loff_t ret = 0; 145 switch (orig) 146 { 147 case 0: /*相對文件開始位置偏移*/ 148 if (offset < 0) 149 { 150 ret = - EINVAL; 151 break; 152 } 153 if ((unsigned int)offset > GLOBALMEM_SIZE) 154 { 155 ret = - EINVAL; 156 break; 157 } 158 filp->f_pos = (unsigned int)offset; 159 ret = filp->f_pos; 160 break; 161 case 1: /*相對文件當前位置偏移*/ 162 if ((filp->f_pos + offset) > GLOBALMEM_SIZE) 163 { 164 ret = - EINVAL; 165 break; 166 } 167 if ((filp->f_pos + offset) < 0) 168 { 169 ret = - EINVAL; 170 break; 171 } 172 filp->f_pos += offset; 173 ret = filp->f_pos; 174 break; 175 default: 176 ret = - EINVAL; 177 break; 178 } 179 return ret; 180 } 181 182 /*文件操作結構體*/ 183 static const struct file_operations globalmem_fops = 184 { 185 .owner = THIS_MODULE, 186 .llseek = globalmem_llseek, 187 .read = globalmem_read, 188 .write = globalmem_write, 189 .ioctl = globalmem_ioctl, 190 .open = globalmem_open, 191 .release = globalmem_release, 192 }; 193 194 /*初始化並注冊cdev*/ 195 static void globalmem_setup_cdev(struct globalmem_dev *dev, int index) 196 { 197 int err, devno = MKDEV(globalmem_major, index); 198 199 cdev_init(&dev->cdev, &globalmem_fops); 200 dev->cdev.owner = THIS_MODULE; 201 dev->cdev.ops = &globalmem_fops; 202 err = cdev_add(&dev->cdev, devno, 1); 203 if (err) 204 printk(KERN_NOTICE "Error %d adding LED%d", err, index); 205 } 206 207 /*設備驅動模塊加載函數*/ 208 int globalmem_init(void) 209 { 210 int result; 211 dev_t devno = MKDEV(globalmem_major, 0); 212 213 /* 申請設備號*/ 214 if (globalmem_major) 215 result = register_chrdev_region(devno, 1, "globalmem"); 216 else /* 動態申請設備號 */ 217 { 218 result = alloc_chrdev_region(&devno, 0, 1, "globalmem"); 219 globalmem_major = MAJOR(devno); 220 } 221 if (result < 0) 222 return result; 223 224 /* 動態申請設備結構體的內存*/ 225 globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL); 226 if (!globalmem_devp) /*申請失敗*/ 227 { 228 result = - ENOMEM; 229 goto fail_malloc; 230 } 231 memset(globalmem_devp, 0, sizeof(struct globalmem_dev)); 232 233 globalmem_setup_cdev(globalmem_devp, 0); 234 init_MUTEX(&globalmem_devp->sem); /*初始化信號量*/ 235 return 0; 236 237 fail_malloc: unregister_chrdev_region(devno, 1); 238 return result; 239 } 240 241 /*模塊卸載函數*/ 242 void globalmem_exit(void) 243 { 244 cdev_del(&globalmem_devp->cdev); /*注銷cdev*/ 245 kfree(globalmem_devp); /*釋放設備結構體內存*/ 246 unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*釋放設備號*/ 247 } 248 249 MODULE_AUTHOR("Song Baohua"); 250 MODULE_LICENSE("Dual BSD/GPL"); 251 252 module_param(globalmem_major, int, S_IRUGO); 253 254 module_init(globalmem_init); 255 module_exit(globalmem_exit);
我們編譯這個模塊,然后insmod,mknod 之后,我們便可以開始寫測試程序來測試這個字符設備了
下面是我的第一個測試代碼
1 #include<sys/types.h> 2 #include<stdio.h> 3 #include<unistd.h> 4 #include<fcntl.h> 5 6 int main() 7 { 8 9 10 char buf[]="fuck gay"; 11 12 13 int dev_fd=open("/dev/globalmem_lock",O_RDWR | O_NONBLOCK); 14 if(!write(dev_fd,buf,8)) 15 printf("write error"); 16 if(!write(dev_fd,buf,8)) 17 printf("write error"); 18 19 20 return 0; 21 22 }
我們編譯運行這個代碼,如下所示
我們用cat 查看這個設備時 發覺,fuck gayfuck gay已經被正確的輸入到設備中,
但是如果此時,我們用echo命令向這個設備寫進東西,發覺會覆蓋掉原先寫進的內容,如下所示:
對於這種情況的唯一解釋是,每個進程都維護着自己獨有的loff_t 字段,所以當echo命令向該設備寫時,是從0開始寫的
但是我又遇到了一個問題,至今無法解決。源於我又寫了個測試程序如下
1 #include<sys/types.h> 2 #include<stdio.h> 3 #include<unistd.h> 4 #include<fcntl.h> 5 6 int main() 7 { 8 9 pid_t pid; 10 char a[]="fuck"; 11 char b[]="hello"; 12 int dev_fd=open("/dev/globalmem_lock",O_RDWR | O_NONBLOCK); 13 if( ( pid=fork() ) <0) 14 printf("fork error\n"); 15 else if(pid==0) 16 { 17 if(!write(dev_fd,b,8)) 18 printf("write error"); 19 sleep(3); 20 if(!write(dev_fd,b,8)) 21 printf("write error"); 22 } 23 else 24 { 25 if(!write(dev_fd,a,8)) 26 printf("write error"); 27 } 28 return 0; 29 30 }
編譯這個代碼 運行 ,然后cat 該設備,得到如下結果
父子進程仿佛是在同時維護着一個loff_t字段,令我不得其解啊。。求路過大神指導
-----------------------------------------------------------------------------------
經過一些的指點后我才知道我這個問題很二,也是因為對前面的知識遺忘的比較多。對上面問題的解答是,父子進程是共享一個文件描述符表的,而不同的進程是獨享
一個文件描述符表,現在上傳兩張圖,一看便知道