信号量、互斥锁、并发机制选择原则

一、信号量:基于阻塞的并发控制机制

a.定义信号量
struct semaphore sem;

b.初始化信号量
void sema_init(struct semaphore *sem, int val);

c.获得信号量P
int down(struct semaphore *sem);//深度睡眠

int down_interruptible(struct semaphore *sem);//浅度睡眠

d.释放信号量V
void up(struct semaphore *sem);

#include 

适用场合:任务上下文之间且临界区执行时间较长时的互斥或同步问题

实现代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "mychar.h"

#define BUF_LEN 100

int major = 11;					//主设备号
int minor = 0;					//次设备号
int char_num = 1;				//设备号数量

struct mychar_dev 
{
	struct cdev mydev;
	char mydev_buf[BUF_LEN];
	int curlen;
	struct semaphore sem;

	wait_queue_head_t rq;
	wait_queue_head_t wq;

	struct fasync_struct *pasync_obj;
};
struct mychar_dev gmydev;

int mychar_open (struct inode *pnode, struct file *pfile)//打开设备
{
	pfile->private_data = (void *) (container_of(pnode->i_cdev, struct mychar_dev, mydev));
	return 0;
}

int mychar_close(struct inode *pnode, struct file *pfile)//关闭设备
{
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;

	if(pmydev->pasync_obj != NULL) {
		fasync_helper(-1, pfile, 0, &pmydev->pasync_obj);
	}
	
	return 0;
}

ssize_t mychar_read (struct file *pfile, char __user *puser, size_t count, loff_t *p_pos) {

	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	down(&pmydev->sem);

	/* 判断是否有数据可读 */
	if(pmydev->curlen <= 0) {

		if(pfile->f_flags & O_NONBLOCK) { //非阻塞
			up(&pmydev->sem);
			printk("O_NONBLOCK Not Data Read\n");
			return -1;

		} else { //阻塞

			up(&pmydev->sem);
			/* 睡眠 当curlen>0 时返回 */
			ret = wait_event_interruptible(pmydev->rq, pmydev->curlen > 0);
			if(ret) {
				return -ERESTARTSYS;
			}
		}
		down(&pmydev->sem);
	}

	// 确定要读取的数据长度,如果请求大于设备当前数据长度,则读取全部可用数据
	if (count > pmydev->curlen) {
		size = pmydev->curlen;
	}
	else {
		size = count;
	}

	// 将设备数据复制到用户空间缓冲区
	ret = copy_to_user(puser, pmydev->mydev_buf, size);
	if(ret) {
		up(&pmydev->sem);
		printk("copy_to_user failed\n");
		return -1;
	}

	// 移动设备内部缓冲区,去除已读取的数据
	memcpy(pmydev->mydev_buf, pmydev->mydev_buf + size, pmydev->curlen - size);

	pmydev->curlen -= size;
	up(&pmydev->sem);

	wake_up_interruptible(&pmydev->wq);
	
	// 返回实际读取的字节数
	return size;
}

ssize_t mychar_write (struct file *pfile, const char __user *puser, size_t count, loff_t *p_pos) {

	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	down(&pmydev->sem);
	if(pmydev->curlen >= BUF_LEN) {

		if(pfile->f_flags & O_NONBLOCK) { //非阻塞
			up(&pmydev->sem);
			printk("O_NONBLOCK Can Not Write Data\n");
			return -1;

		} else { //阻塞
			up(&pmydev->sem);
			ret = wait_event_interruptible(pmydev->wq, pmydev->curlen < BUF_LEN);
			if(ret) {
				return -ERESTARTSYS;
			}
			down(&pmydev->sem);
		}
		
	}


	// 确定要写入的数据长度,如果请求大于设备缓冲区剩余空间,则写入剩余空间大小
	if (count > BUF_LEN - pmydev->curlen) {
		size = BUF_LEN - pmydev->curlen;
	}
	else {
		size = count;
	}

	// 从用户空间复制数据到设备缓冲区
	ret = copy_from_user(pmydev->mydev_buf + pmydev->curlen, puser, size);
	if(ret) {
		up(&pmydev->sem);
		printk("copy_from_user failed\n");
		return -1;
	}

	// 更新设备缓冲区中的数据长度
	pmydev->curlen += size;
	up(&pmydev->sem);


	/* 唤醒读阻塞 */
	wake_up_interruptible(&pmydev->rq);

	if(pmydev->pasync_obj != NULL) {
		kill_fasync(&pmydev->pasync_obj, SIGIO,POLL_IN);
	}

	 // 返回实际写入的字节数
	return size;
}

long mychar_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) 
{
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int __user *pret = (int *)arg;
	int maxlen = BUF_LEN;
	int ret = 0;

	switch(cmd) {
	case MYCHAR_IOCTL_GET_MAXLEN:
		ret = copy_to_user(pret, &maxlen, sizeof(int));
		if(ret) {
			printk("copy_from_user failed\n");
			return -1;
		}
		break;

	case MYCHAR_IOCTL_GET_CURLEN:
		down(&pmydev->sem);
		ret = copy_to_user(pret, &pmydev->curlen, sizeof(int));
		up(&pmydev->sem);
		if(ret) {
			printk("copy_from_user failed\n");
			return -1;
		}
		break;

	default:
		printk("The is a know\n");
		return -1;
	}
	return 0;
}

unsigned int mychar_poll(struct file *pfile, poll_table *ptb) {

	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	unsigned int mask = 0;

	poll_wait(pfile, &pmydev->rq, ptb);
	poll_wait(pfile, &pmydev->wq, ptb);

	down(&pmydev->sem);
	if(pmydev->curlen > 0) {
		mask |= POLLIN | POLLRDNORM;
	}
	if(pmydev->curlen < BUF_LEN) {
		mask |= POLLOUT | POLLWRNORM;
	}
	up(&pmydev->sem);

	return mask;
}

int mychar_fasync(int fd, struct file *pfile, int mode) {
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	return fasync_helper(fd, pfile, mode, &pmydev->pasync_obj);
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mychar_open,
	.read = mychar_read,
	.write = mychar_write,
	.unlocked_ioctl = mychar_ioctl,
	.poll = mychar_poll,
	.fasync = mychar_fasync,
};

int __init mychar_init(void) 
{
	int ret = 0;
	dev_t devno = MKDEV(major, minor);

	/* 手动申请设备号 */
	ret = register_chrdev_region(devno, char_num, "mychar");
	if (ret) {
		/* 动态申请设备号 */
		ret = alloc_chrdev_region(&devno, minor, char_num, "mychar");
		if(ret){
			printk("get devno failed\n");
			return -1;
		}
		/*申请成功 更新设备号*/
		major = MAJOR(devno);
	}
	
	/* 给struct cdev对象指定操作函数集 */
	cdev_init(&gmydev.mydev, &myops);

	/* 将struct cdev对象添加到内核对应的数据结构中 */
	gmydev.mydev.owner = THIS_MODULE;
	cdev_add(&gmydev.mydev, devno, char_num);

	/* 初始化 */
	init_waitqueue_head(&gmydev.rq);
	init_waitqueue_head(&gmydev.wq);

	sema_init(&gmydev.sem, 1);

	return 0;
}

void __exit mychar_exit(void) 
{

	dev_t devno = MKDEV(major, minor);
	printk("exit %d\n", devno);
	
	/* 从内核中移除一个字符设备 */
	cdev_del(&gmydev.mydev);

	/* 回收设备号 */
	unregister_chrdev_region(devno, char_num);

}

MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);

二、互斥锁:基于阻塞的互斥机制

a.初始化
struct mutex my_mutex;
mutex_init(&my_mutex);

b.获取互斥体
void mutex_lock(struct mutex *lock);

c.释放互斥体
void mutex_unlock(struct mutex *lock);

  1. 定义对应类型的变量
  2. 初始化对应变量

P/加锁
临界区
V/解锁

#include 

适用场合:任务上下文之间且临界区执行时间较长时的互斥问题

实现代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "mychar.h"

#define BUF_LEN 100

int major = 11;					//主设备号
int minor = 0;					//次设备号
int char_num = 1;				//设备号数量

struct mychar_dev 
{
	struct cdev mydev;
	char mydev_buf[BUF_LEN];
	int curlen;
	struct mutex lock;

	wait_queue_head_t rq;
	wait_queue_head_t wq;

	struct fasync_struct *pasync_obj;
};
struct mychar_dev gmydev;

int mychar_open (struct inode *pnode, struct file *pfile)//打开设备
{
	pfile->private_data = (void *) (container_of(pnode->i_cdev, struct mychar_dev, mydev));
	return 0;
}

int mychar_close(struct inode *pnode, struct file *pfile)//关闭设备
{
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;

	if(pmydev->pasync_obj != NULL) {
		fasync_helper(-1, pfile, 0, &pmydev->pasync_obj);
	}
	
	return 0;
}

ssize_t mychar_read (struct file *pfile, char __user *puser, size_t count, loff_t *p_pos) {

	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	mutex_lock(&pmydev->lock);

	/* 判断是否有数据可读 */
	if(pmydev->curlen <= 0) {

		if(pfile->f_flags & O_NONBLOCK) { //非阻塞
			mutex_unlock(&pmydev->lock);
			printk("O_NONBLOCK Not Data Read\n");
			return -1;

		} else { //阻塞

			mutex_unlock(&pmydev->lock);
			/* 睡眠 当curlen>0 时返回 */
			ret = wait_event_interruptible(pmydev->rq, pmydev->curlen > 0);
			if(ret) {
				return -ERESTARTSYS;
			}
		}
		mutex_lock(&pmydev->lock);
	}

	// 确定要读取的数据长度,如果请求大于设备当前数据长度,则读取全部可用数据
	if (count > pmydev->curlen) {
		size = pmydev->curlen;
	}
	else {
		size = count;
	}

	// 将设备数据复制到用户空间缓冲区
	ret = copy_to_user(puser, pmydev->mydev_buf, size);
	if(ret) {
		mutex_unlock(&pmydev->lock);
		printk("copy_to_user failed\n");
		return -1;
	}

	// 移动设备内部缓冲区,去除已读取的数据
	memcpy(pmydev->mydev_buf, pmydev->mydev_buf + size, pmydev->curlen - size);

	pmydev->curlen -= size;
	mutex_unlock(&pmydev->lock);

	wake_up_interruptible(&pmydev->wq);
	
	// 返回实际读取的字节数
	return size;
}

ssize_t mychar_write (struct file *pfile, const char __user *puser, size_t count, loff_t *p_pos) {

	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	mutex_lock(&pmydev->lock);
	if(pmydev->curlen >= BUF_LEN) {

		if(pfile->f_flags & O_NONBLOCK) { //非阻塞
			mutex_unlock(&pmydev->lock);
			printk("O_NONBLOCK Can Not Write Data\n");
			return -1;

		} else { //阻塞
			mutex_unlock(&pmydev->lock);
			ret = wait_event_interruptible(pmydev->wq, pmydev->curlen < BUF_LEN);
			if(ret) {
				return -ERESTARTSYS;
			}
			mutex_lock(&pmydev->lock);
		}
		
	}


	// 确定要写入的数据长度,如果请求大于设备缓冲区剩余空间,则写入剩余空间大小
	if (count > BUF_LEN - pmydev->curlen) {
		size = BUF_LEN - pmydev->curlen;
	}
	else {
		size = count;
	}

	// 从用户空间复制数据到设备缓冲区
	ret = copy_from_user(pmydev->mydev_buf + pmydev->curlen, puser, size);
	if(ret) {
		mutex_unlock(&pmydev->lock);
		printk("copy_from_user failed\n");
		return -1;
	}

	// 更新设备缓冲区中的数据长度
	pmydev->curlen += size;
	mutex_unlock(&pmydev->lock);


	/* 唤醒读阻塞 */
	wake_up_interruptible(&pmydev->rq);

	if(pmydev->pasync_obj != NULL) {
		kill_fasync(&pmydev->pasync_obj, SIGIO,POLL_IN);
	}

	 // 返回实际写入的字节数
	return size;
}

long mychar_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) 
{
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int __user *pret = (int *)arg;
	int maxlen = BUF_LEN;
	int ret = 0;

	switch(cmd) {
	case MYCHAR_IOCTL_GET_MAXLEN:
		ret = copy_to_user(pret, &maxlen, sizeof(int));
		if(ret) {
			printk("copy_from_user failed\n");
			return -1;
		}
		break;

	case MYCHAR_IOCTL_GET_CURLEN:
		mutex_lock(&pmydev->lock);
		ret = copy_to_user(pret, &pmydev->curlen, sizeof(int));
		mutex_unlock(&pmydev->lock);
		if(ret) {
			printk("copy_from_user failed\n");
			return -1;
		}
		break;

	default:
		printk("The is a know\n");
		return -1;
	}
	return 0;
}

unsigned int mychar_poll(struct file *pfile, poll_table *ptb) {

	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	unsigned int mask = 0;

	poll_wait(pfile, &pmydev->rq, ptb);
	poll_wait(pfile, &pmydev->wq, ptb);

	mutex_lock(&pmydev->lock);
	if(pmydev->curlen > 0) {
		mask |= POLLIN | POLLRDNORM;
	}
	if(pmydev->curlen < BUF_LEN) {
		mask |= POLLOUT | POLLWRNORM;
	}
	mutex_unlock(&pmydev->lock);

	return mask;
}

int mychar_fasync(int fd, struct file *pfile, int mode) {
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	return fasync_helper(fd, pfile, mode, &pmydev->pasync_obj);
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mychar_open,
	.read = mychar_read,
	.write = mychar_write,
	.unlocked_ioctl = mychar_ioctl,
	.poll = mychar_poll,
	.fasync = mychar_fasync,
};

int __init mychar_init(void) 
{
	int ret = 0;
	dev_t devno = MKDEV(major, minor);

	/* 手动申请设备号 */
	ret = register_chrdev_region(devno, char_num, "mychar");
	if (ret) {
		/* 动态申请设备号 */
		ret = alloc_chrdev_region(&devno, minor, char_num, "mychar");
		if(ret){
			printk("get devno failed\n");
			return -1;
		}
		/*申请成功 更新设备号*/
		major = MAJOR(devno);
	}
	
	/* 给struct cdev对象指定操作函数集 */
	cdev_init(&gmydev.mydev, &myops);

	/* 将struct cdev对象添加到内核对应的数据结构中 */
	gmydev.mydev.owner = THIS_MODULE;
	cdev_add(&gmydev.mydev, devno, char_num);

	/* 初始化 */
	init_waitqueue_head(&gmydev.rq);
	init_waitqueue_head(&gmydev.wq);

	mutex_init(&gmydev.lock);

	return 0;
}

void __exit mychar_exit(void) 
{

	dev_t devno = MKDEV(major, minor);
	printk("exit %d\n", devno);
	
	/* 从内核中移除一个字符设备 */
	cdev_del(&gmydev.mydev);

	/* 回收设备号 */
	unregister_chrdev_region(devno, char_num);

}

MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);

三、选择并发控制机制的原则

  1. 不允许睡眠的上下文需要采用忙等待类,可以睡眠的上下文可以采用阻塞类。在异常上下文中访问的竞争资源一定采用忙等待类。
  2. 临界区操作较长的应用建议采用阻塞类,临界区很短的操作建议采用忙等待类。
  3. 中断屏蔽仅在有与中断上下文共享资源时使用。
  4. 共享资源仅是一个简单整型量时用原子变量

你可能感兴趣的:(驱动开发,linux)