您的位置:首页 > 其它

高级字符驱动程序操作之休眠(实践篇)

2010-12-27 14:33 435 查看
1. scull_pipe.c

#include <linux/module.h>
#include <linux/moduleparam.h>

#include <linux/kernel.h>	/* printk(), min() */
#include <linux/slab.h>		/* kmalloc() */
#include <linux/fs.h>		/* everything... */
#include <linux/proc_fs.h>
#include <linux/errno.h>	/* error codes */
#include <linux/types.h>	/* size_t */
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

struct scull_pipe {
wait_queue_head_t inq, outq;       /* read and write queues */
char *buffer, *end;                /* begin of buf, end of buf */
int buffersize;                    /* used in pointer arithmetic */
char *rp, *wp;                     /* where to read, where to write */
int nreaders, nwriters;            /* number of openings for r/w */
struct semaphore sem;              /* mutual exclusion semaphore */
struct cdev cdev;                  /* Char device structure */
};

/* parameters */
int scull_p_buffer =  4000;	/* buffer size */
static int pipe_major = 0;
static struct scull_pipe *pdev = NULL;

static int spacefree(struct scull_pipe *dev);
void pipe_cleanup(void);

static int pipe_open(struct inode *inode, struct file *filp)
{
struct scull_pipe *dev;

dev = container_of(inode->i_cdev, struct scull_pipe, cdev);
filp->private_data = dev;

if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (!dev->buffer) {
/* allocate the buffer */
dev->buffer = kmalloc(scull_p_buffer, GFP_KERNEL);
if (!dev->buffer) {
up(&dev->sem);
return -ENOMEM;
}
}
dev->buffersize = scull_p_buffer;
dev->end = dev->buffer + dev->buffersize;
dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning */

/* use f_mode,not  f_flags: it's cleaner (fs/open.c tells why) */
if (filp->f_mode & FMODE_READ)
dev->nreaders++;
if (filp->f_mode & FMODE_WRITE)
dev->nwriters++;
up(&dev->sem);

return nonseekable_open(inode, filp);
}

static int pipe_release(struct inode *inode, struct file *filp)
{
struct scull_pipe *dev = filp->private_data;

down(&dev->sem);
if (filp->f_mode & FMODE_READ)
dev->nreaders--;
if (filp->f_mode & FMODE_WRITE)
dev->nwriters--;
if (dev->nreaders + dev->nwriters == 0) {
kfree(dev->buffer);
dev->buffer = NULL; /* the other fields are not checked on open */
}
up(&dev->sem);
return 0;
}

static ssize_t pipe_read (struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
struct scull_pipe *dev = filp->private_data;

if (down_interruptible(&dev->sem))
return -ERESTARTSYS;

while (dev->rp == dev->wp) { /* nothing to read */
up(&dev->sem); /* release the lock */
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;

if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))
return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
/* otherwise loop, but first reacquire the lock */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
/* ok, data is there, return something */
if (dev->wp > dev->rp)
count = min(count, (size_t)(dev->wp - dev->rp));
else /* the write pointer has wrapped, return data up to dev->end */
count = min(count, (size_t)(dev->end - dev->rp));
if (copy_to_user(buf, dev->rp, count)) {
up (&dev->sem);
return -EFAULT;
}
dev->rp += count;
if (dev->rp == dev->end)
dev->rp = dev->buffer; /* wrapped */
up (&dev->sem);

/* finally, awake any writers and return */
wake_up_interruptible(&dev->outq);
return count;
}

/* Wait for space for writing; caller must hold device semaphore.  On
* error the semaphore will be released before returning. */
static int getwritespace(struct scull_pipe *dev, struct file *filp)
{
while (spacefree(dev) == 0) { /* full */
DEFINE_WAIT(wait);

up(&dev->sem);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;

prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);
if (spacefree(dev) == 0)
schedule();
finish_wait(&dev->outq, &wait);
if (signal_pending(current))
return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
return 0;
}

/* How much space is free? */
static int spacefree(struct scull_pipe *dev)
{
if (dev->rp == dev->wp)
return dev->buffersize - 1;
return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize) - 1;
}

static ssize_t pipe_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
struct scull_pipe *dev = filp->private_data;
int result;

if (down_interruptible(&dev->sem))
return -ERESTARTSYS;

/* Make sure there's space to write */
result = getwritespace(dev, filp);
if (result)
return result; /* scull_getwritespace called up(&dev->sem) */

/* ok, space is there, accept something */
count = min(count, (size_t)spacefree(dev));
if (dev->wp >= dev->rp)
count = min(count, (size_t)(dev->end - dev->wp)); /* to end-of-buf */
else /* the write pointer has wrapped, fill up to rp-1 */
count = min(count, (size_t)(dev->rp - dev->wp - 1));

if (copy_from_user(dev->wp, buf, count)) {
up (&dev->sem);
return -EFAULT;
}
dev->wp += count;
if (dev->wp == dev->end)
dev->wp = dev->buffer; /* wrapped */
up(&dev->sem);

/* finally, awake any reader */
wake_up_interruptible(&dev->inq);  /* blocked in read() and select() */

return count;
}

struct file_operations pipe_fops = {
.owner =	THIS_MODULE,
.read =		pipe_read,
.write =	pipe_write,
.open =		pipe_open,
.release =	pipe_release,
};

int pipe_init(void)
{
int result;
dev_t dev = 0;

if (pipe_major) {
dev = MKDEV(pipe_major, 0);
result = register_chrdev_region(dev, 1, "pipe0");
} else {
result = alloc_chrdev_region(&dev, 0, 1, "pipe0");
pipe_major = MAJOR(dev);
}
if (result < 0) {
return result;
}

pdev = kmalloc(sizeof(struct scull_pipe), GFP_KERNEL);
if (!pdev) {
result = -ENOMEM;
goto fail;  /* Make this more graceful */
}
memset(pdev, 0, sizeof(struct scull_pipe));

init_waitqueue_head(&(pdev->inq));
init_waitqueue_head(&(pdev->outq));
init_MUTEX(&(pdev->sem));

cdev_init(&(pdev->cdev), &pipe_fops);
(pdev->cdev).owner = THIS_MODULE;
cdev_add(&(pdev->cdev), dev, 1);

return 0;

fail:
pipe_cleanup();
return result;
}

void pipe_cleanup(void)
{
dev_t devno;
devno = MKDEV(pipe_major, 0);

if (!pdev)
return; /* nothing else to release */

cdev_del(&(pdev->cdev));
kfree(pdev->buffer);
kfree(pdev);
pdev = NULL; /* pedantic */

unregister_chrdev_region(devno, 1);
}

MODULE_AUTHOR("victorsummer");
MODULE_LICENSE("Dual BSD/GPL");

module_init(pipe_init);
module_exit(pipe_cleanup);


2. Makefile

KERNELDIR = /usr/src/linux-headers-2.6.31-14-generic

PWD := $(shell pwd)

obj-m := scull_pipe.o

modules:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules


3. pipe_testr.c 从pipe0读取8个字节

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
char *read_buffer = malloc(sizeof(char*)*30);
int pipe_fd;
int code;
int back = 0;

pipe_fd = open("/dev/pipe0", O_RDONLY);

if((code = read(pipe_fd , read_buffer , 8)) != -1)
{
printf("Read %d bytes from scull/n", code);
read_buffer += code;
back += code;
}
read_buffer -= back;

printf("Read /"%s/" from scull /n", read_buffer);

close(pipe_fd);

exit(0);
}


4. pipe_testw 向pipe0写入24个字节

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
char *write_buffer = "Hello, Character driver!";
int pipe_fd;
int code;

pipe_fd = open("/dev/pipe0", O_WRONLY);
while(*write_buffer != '/0')
{
code = write(pipe_fd , write_buffer , 24);
printf("Write %d bytes to scull/n", code);
write_buffer += code;
}

close(pipe_fd);

exit(0);
}


5. Makefile

all : pipe_testw.o pipe_testr.o
gcc -o pipe_testw.o pipe_testw.c
gcc -o pipe_testr.o pipe_testr.c


6. 测试

装载驱动模块

sudo insmod ./scull_pipe.ko

查看主设备号,假设为251

cat /proc/devices

建立设备节点

sudo mknod /dev/pipe0 c 251 0

更改权限

sudo chgrp staff /dev/pipe0

sudo chmod 664 /dev/pipe0

开启四个终端

分别在终端1,2,3 运行三个pipe_testr.o

现在没有数据,全部进入休眠

在终端4运行pipe_testw.o

终端4打印

Write 24 bytes to scull

首先运行在终端1的读进程被唤醒 打印

Read 8 bytes from scull
Read "Hello, C" from scull

接着运行在终端2的读进程被唤醒 打印

Read 8 bytes from scull
Read "haracter" from scull

最后运行在终端3的读进程被唤醒 打印

Read 8 bytes from scull
Read " driver!" from scull
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐