全部博文(298)
分类: LINUX
2011-03-24 12:45:37
(4)并发处理-completion应用
注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方
1.本文所介绍的程序平台
开发板:arm9-mini2440
虚拟机为:Red Hat Enterprise Linux 5
开发板上系统内核版本:linux-2.6.32.2
2.程序清单
本次实验程序为国嵌培训和tekkamanninja博客《LDD3》代码,本人作了改动和较为详细的注释,如有错误请指出。
gong.h
#ifndef _GONG_H_
#define _GONG_H_
#include
#ifndef GONG_MAJOR
#define GONG_MAJOR 0 /*预设的mem的主设备号*/
#endif
#ifndef GONG_NR_DEVS
#define GONG_NR_DEVS 3/*设备数*/
#endif
#ifndef GONG_SIZE
#define GONG_SIZE 4096
#endif
/*mem设备描述的结构体*/
struct gong_dev
{
char *data;
unsigned long size;
wait_queue_head_t inq;
};//注意 这里要加分号!!!!!!!!!
/*定义幻数*/
#define GONG_IOC_MAGIC 'g'
/* 定义命令 ,这里的命令都是unsigned int类型*/
#define GONG_IOCPRINT _IO(GONG_IOC_MAGIC, 1) /*没有参数的命令*/
#define GONG_IOCGETDATA _IOR(GONG_IOC_MAGIC, 2, int) /*从驱动中读数据*/
#define GONG_IOCSETDATA _IOW(GONG_IOC_MAGIC, 3, int) /*写数据到驱动中*/
//定义命令的最大序列号
#define GONG_IOC_MAXNR 3
#endif
gong.c
/*********************************************
*阻塞版本
*********************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "gong.h"
static int gong_major = GONG_MAJOR;
bool have_data = false ;/*表明设备有足够空间可供读*/
module_param(gong_major, int, S_IRUGO);
struct gong_dev *gong_devp;/*设备结构体指针*/
struct cdev cdev;
DECLARE_COMPLETION(comp);/* 创建completion(声明+初始化) */
/*
struct file 常见成员:
loff_t f_pos 文件读写位置
struct file_operation *f_op
struct inode 常见成员:
dev_t i_rdev 设备号
*/
/*
为啥不能放在这里???
static const struct file_operations gong_fops =
{
.owner = THIS_MODULE,
.llseek = gong_llseek,
.read = gong_read,
.write = gong_write,
.open = gong_open,
.release = gong_release,
.ioctl = gong_ioctl,//!!为什么这里有‘,’??
};//!!!
*/
/*文件打开函数*/
int gong_open(struct inode *inode, struct file *filp)
{
struct gong_dev *dev;
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
/*大于最大的次设备号 就错误了 因为注册的时候要指定次设备的数目*/
if(num >= GONG_NR_DEVS)
return -ENODEV;
dev = &gong_devp[num];//获取为次设备号为num的设备结构
/*将设备描述结构指针赋值给文件私有数据指针*/
filp->private_data = dev;//!!
return 0;
}
/*文件释放函数*/
int gong_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*读函数*/
static ssize_t gong_read(struct file *filp, char __user *buf, size_t size, loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret = 0;
/*获得设备结构体指针 由于read没有struct inode *inode结构 而且函数的
参数不能改 只能改函数名 所以只能在open函数里面设置*/
struct gong_dev *dev = filp->private_data;
//并发处理
printk(KERN_DEBUG "process %i (%s) going to sleep\n",
current->pid, current->comm);//!!
wait_for_completion(&comp);/* 等待completion */
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
/*判断读位置是否有效 从内核空间读到用户空间*/
if(p >= GONG_SIZE)
return 0;
if(count > GONG_SIZE-p )
count = GONG_SIZE-p ;
/*没有数据可读 考虑为什么不用if 而用while !!!!??*/
while(!have_data)
{
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
/*wait_event_interruptible(queue, condition) 条件为真时立即返回否则让
进程进入TASK_INTERRUPTIBLE的睡眠
并挂在queue参数指定的等待队列*/
wait_event_interruptible(dev->inq, have_data);
}
/*读数据到用户空间*/
if(copy_to_user(buf, (void *)(dev->data+p), count))
ret = -EFAULT;
else
{
*poss += count;
ret = count;
printk("<5> read %d butes from %lu\n", count, p);
}
have_data = false;/*表明不再有数据可读 */
return ret;
}
/*写函数*/
static ssize_t gong_write(struct file *filp, const char __user *buf, size_t size, loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret = 0;
struct gong_dev *dev = filp->private_data;
if(p >= GONG_SIZE)
return 0;
if(count > GONG_SIZE-p)
count = GONG_SIZE-p;
if(copy_from_user(dev->data+p, buf, count))
{
ret = -EFAULT;
}
else
{
*poss += count;
ret = count;
printk("<5> write %d bytes foem %lu\n", count, p);
}
//并发处理
printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
current->pid, current->comm);
complete(&comp);/*唤醒一个等待completion的线程*/
have_data = true;/*有新数据可读*/
/*唤醒读进程
wake_up(wait_queue_t *q)
从等待队列q中唤醒状态为
TASK_INTERRUPTIBLE,TASK_UNINTERRUPTIBLE,TASK_KILLABLE
的所有进程*/
wake_up(&(dev->inq));
return ret;
}
int gong_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int err =0;
int ret = 0;
int ioarg = 0;
/* 检测命令的有效性 */
if(_IOC_TYPE(cmd) != GONG_IOC_MAGIC)//命令是否操作这一类设备
return -EINVAL;
if(_IOC_NR(cmd) > GONG_IOC_MAXNR)// 命令序列号是否超出定义
return -EINVAL;
/* 根据命令类型,检测参数空间是否可以访问 */
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
/* 根据命令,执行相应的操作 */
switch(cmd) {
/* 打印当前设备信息 */
case GONG_IOCPRINT:
printk("<--- CMD GONG_IOCPRINT Done--->\n\n");
break;
/* 获取参数 */
case GONG_IOCGETDATA:
ioarg = 1101;
ret = __put_user(ioarg, (int *)arg);
break;
/* 设置参数 */
case GONG_IOCSETDATA:
ret = __get_user(ioarg, (int *)arg);
printk("<--- In Kernel GONG_IOCSETDATA ioarg = %d --->\n\n",ioarg);
break;
default:
return -EINVAL;
}
return ret;
}
static loff_t gong_llseek(struct file *filp,loff_t offset,int whence)
{
loff_t newpos;
switch(whence)
{
case 0:
newpos = offset;
break;
case 1:
newpos = filp->f_pos + offset;
break;
case 2:
newpos = GONG_SIZE - 1 + offset;
break;
default:
return -EINVAL;
}
if((newpos<0) || (newpos>GONG_SIZE))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
static const struct file_operations gong_fops =
{
.owner = THIS_MODULE,
.llseek = gong_llseek,
.read = gong_read,
.write = gong_write,
.open = gong_open,
.release = gong_release,
.ioctl = gong_ioctl,//!!为什么这里有‘,’??
};//!!!
/*设备驱动模块加载函数*/
static int gong_init(void)
{
int res;
int i;
/*dev_t描述是设备 MKDEV把主设备号和次设备号合成*/
dev_t devno = MKDEV(gong_major, 0);
/*静态申请设备号*/
/*int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:注册从from开始的count个这备号,主设备号不变,次设备号增加,如果次设备号溢出,主设备号加1
参数:
from:要注册的第一个设备号
count:要注册的设备号个数
name:设备名(体现在/proc/devices)
*/
if(gong_major)
res = register_chrdev_region(devno, GONG_NR_DEVS, "gong_dev");
else/*动态申请设备号*/
/*int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
功能:动态申请count个设备号,第一个设备号的次设备号为baseminor
参数:
dev:分配到得设备号
baseminor:起始次设备号
count:要注册的设备号个数
name:设备名(体现在/proc/devices)
*/
{
res = alloc_chrdev_region(&devno, 0, GONG_NR_DEVS, "gong_dev");
gong_major = MAJOR(devno);
}
if(res < 0)
return res;
/*设备注册:
1.分配cdev
动态分配:
struct cdev *cdev_alloc(void)
2.初始化cdev
void cdev_init(struct cdev *cdev, const struct file_operation *fops)
3.添加cdev
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
*/
/*初始化cdev结构*/
cdev_init(&cdev, &gong_fops);
//cdev.ower = THIS_MODULE; 没有这个成员
cdev.ops = &gong_fops;
/*注册字符设备*/
cdev_add(&cdev, MKDEV(gong_major, 0), GONG_NR_DEVS);
/*为设备描述结构分配内存 有多少个设备分配多少个 */
gong_devp = kmalloc(GONG_NR_DEVS * sizeof(struct gong_dev), GFP_KERNEL);
if(!gong_devp)/*申请失败*/
{
res = -ENOMEM;
goto fail_malloc;
}
memset(gong_devp, 0, GONG_NR_DEVS * sizeof(struct gong_dev));
/*为设备分配内存 有多少个设备分配多少个*/
for(i = 0; i < GONG_NR_DEVS; i++)
{
gong_devp[i].size = GONG_SIZE;
gong_devp[i].data = kmalloc(GONG_SIZE, GFP_KERNEL);
/*初始化等待队列*/
init_waitqueue_head(&(gong_devp[i].inq));
}
return 0;
fail_malloc:
/*void unregister_chrdev_region(dev_t from, unsigned count)
功能:释放从from开始的count个设备号*/
unregister_chrdev_region(devno, GONG_NR_DEVS);
return res;
}
static void gong_exit(void)
{
cdev_del(&cdev);
kfree(gong_devp);
unregister_chrdev_region(MKDEV(gong_major, 0), GONG_NR_DEVS);
}
MODULE_LICENSE("GPL");
module_init(gong_init);
module_exit(gong_exit);
/* 自定义函数
static void scull_kfifo_setup_cdev(struct scull_kfifo *dev)
{
int err, devno = MKDEV(scull_kfifo_major, scull_kfifo_minor );
cdev_init(&dev->cdev, &scull_kfifo_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add (&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding scull_kfifo", err);
}
*/
gong_write.c
/*********************************************
*memdevapp.c
*********************************************/
#include
#include
#include
#include
#include
int main()
{
int fd;
char buf[4096];
//FILE *fp0 = NULL;
/*初始化buf*/
strcpy(buf,"This is write");
printf("buf:%s\n",buf);
/*打开设备文件*/
fd=open("/dev/gong_dev0",O_RDWR);
if(fd == -1)
{
printf("open memdev failed!\n");
return -1;
}
/*写入设备*/
write(fd,buf,sizeof(buf));
return 0;
}
gong_read.c
/*********************************************
*memdevapp.c
*********************************************/
#include
#include
#include
#include
#include
int main()
{
int fd;
char buf[4096];
//FILE *fp0 = NULL;
/*初始化buf*/
strcpy(buf,"This is read");
printf("buf:%s\n",buf);
/*打开设备文件*/
fd=open("/dev/gong_dev0",O_RDWR);
if(fd == -1)
{
printf("open memdev failed!\n");
return -1;
}
lseek(fd,0,SEEK_SET);
/*清除buf*/
strcpy(buf,"nothing");
/*读出设备*/
read(fd,buf,sizeof(buf));
/*检测结果*/
printf("buf:%s\n",buf);
return 0;
}
Arm平台实验:
[root@FriendlyARM /udisk]# insmod gong.ko
[root@FriendlyARM /udisk]# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 s3c2410_serial
253 gong_dev
254 rtc
Block devices:
259 blkext
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
[root@FriendlyARM /udisk]# mknod /dev/gong_dev0 c 253 0
[root@FriendlyARM /udisk]# ./read &
[root@FriendlyARM /udisk]# ./read &
[root@FriendlyARM /udisk]# ps
PID USER VSZ STAT COMMAND
1 root 3068 S init
2 root 0 SW [kthreadd]
3 root 0 SW [ksoftirqd/0]
4 root 0 SW [events/0]
5 root 0 SW [khelper]
11 root 0 SW [async/mgr]
209 root 0 SW [sync_supers]
211 root 0 SW [bdi-default]
213 root 0 SW [kblockd/0]
222 root 0 SW [khubd]
228 root 0 SW [kmmcd]
244 root 0 SW [rpciod/0]
251 root 0 SW [kswapd0]
298 root 0 SW [aio/0]
302 root 0 SW [nfsiod]
306 root 0 SW [crypto/0]
413 root 0 SW [mtdblockd]
623 root 0 SW [usbhid_resumer]
667 root 3068 S syslogd
670 root 3328 S /usr/sbin/inetd
674 root 1940 S /usr/sbin/boa
686 root 13084 S /opt/Qtopia/bin/qpe
687 root 3392 S -/bin/sh
688 root 3068 S init
689 root 3068 S init
692 root 3068 S init
699 root 0 SW [flush-31:0]
704 root 0 SW [scsi_eh_0]
705 root 0 SW [usb-storage]
730 root 1412 D ./read
731 root 1412 D ./read
732 root 3392 R ps
[root@FriendlyARM /udisk]# ./write
write 0 bytes foem 0
read 0 butes from 0
read ok! code=0
write ok! code=0
[1] - Done ./read
[root@FriendlyARM /udisk]# ps
PID USER VSZ STAT COMMAND
1 root 3068 S init
2 root 0 SW [kthreadd]
3 root 0 SW [ksoftirqd/0]
4 root 0 SW [events/0]
5 root 0 SW [khelper]
11 root 0 SW [async/mgr]
209 root 0 SW [sync_supers]
211 root 0 SW [bdi-default]
213 root 0 SW [kblockd/0]
222 root 0 SW [khubd]
228 root 0 SW [kmmcd]
244 root 0 SW [rpciod/0]
251 root 0 SW [kswapd0]
298 root 0 SW [aio/0]
302 root 0 SW [nfsiod]
306 root 0 SW [crypto/0]
413 root 0 SW [mtdblockd]
623 root 0 SW [usbhid_resumer]
667 root 3068 S syslogd
670 root 3328 S /usr/sbin/inetd
674 root 1940 S /usr/sbin/boa
686 root 13084 S /opt/Qtopia/bin/qpe
687 root 3392 S -/bin/sh
688 root 3068 S init
689 root 3068 S init
692 root 3068 S init
699 root 0 SW [flush-31:0]
704 root 0 SW [scsi_eh_0]
705 root 0 SW [usb-storage]
731 root 1412 D ./read
734 root 3392 R ps
[root@FriendlyARM /udisk]# ./write
write 0 bytes foem 0
read 0 butes from 0
write ok! code=0
[root@FriendlyARM /udisk]# read ok! code=0
ps
PID USER VSZ STAT COMMAND
1 root 3068 S init
2 root 0 SW [kthreadd]
3 root 0 SW [ksoftirqd/0]
4 root 0 SW [events/0]
5 root 0 SW [khelper]
11 root 0 SW [async/mgr]
209 root 0 SW [sync_supers]
211 root 0 SW [bdi-default]
213 root 0 SW [kblockd/0]
222 root 0 SW [khubd]
228 root 0 SW [kmmcd]
244 root 0 SW [rpciod/0]
251 root 0 SW [kswapd0]
298 root 0 SW [aio/0]
302 root 0 SW [nfsiod]
306 root 0 SW [crypto/0]
413 root 0 SW [mtdblockd]
623 root 0 SW [usbhid_resumer]
667 root 3068 S syslogd
670 root 3328 S /usr/sbin/inetd
674 root 1940 S /usr/sbin/boa
686 root 13084 S /opt/Qtopia/bin/qpe
687 root 3392 S -/bin/sh
688 root 3068 S init
689 root 3068 S init
692 root 3068 S init
699 root 0 SW [flush-31:0]
704 root 0 SW [scsi_eh_0]
705 root 0 SW [usb-storage]
736 root 3392 R ps
[2] + Done ./read
[root@FriendlyARM /udisk]# ./write
write 0 bytes foem 0
write ok! code=0
[root@FriendlyARM /udisk]#
注意:completion机制和等待队列的作用有什么区别??
(1)completion机制在本程序中是在写入数据后,告诉另一线程数据已写入,然后由另一线程从休眠中唤醒开始读数据,若是读线程没有设置阻塞(在本程序中由于线程等待放于阻塞检测前面,所以不用考虑)则返回错误。
(2)等待队列机制在本程序中是在写入数据后,告诉另一线程数据已写入,然后由另一线程从休眠中唤醒开始读数据,若是读线程没有设置阻塞则返回错误。