转自:http://www.cnblogs.com/Blue-Moon/archive/2012/09/25/2702028.html
驱动模块中有一个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字段,令我不得其解啊。。求路过大神指导
-----------------------------------------------------------------------------------
经过一些的指点后我才知道我这个问题很二,也是因为对前面的知识遗忘的比较多。对上面问题的解答是,父子进程是共享一个文件描述符表的,而不同的进程是独享
一个文件描述符表,现在上传两张图,一看便知道