本文内容整理自B站视频教程
自旋锁定义
内核发生访问资源冲突的时候,可以有两种锁的解决方案选择:一个是原地等待,一个是挂起当前进程,调度其他进程执行(休眠)。
spinlock是内核中提供的一种比较常见的锁机制,自旋锁是原地等待的方式解决资源冲突的,即一个线程获取了一个自旋锁后,另一个线程期望获取该自旋锁,获取不到,值呢挂钩原地“打转”(忙等待,仍然占有CPU,但不会去休眠)。
自旋锁优点:
注意事项:
- 系统性能下降,cpu在请求自旋锁时,仅仅是等待自旋锁,当共享资源较大,执行单元操作时间较长时会导致。
- 造成死锁的现象,当某个cpu已经拿到锁之后,再去申请锁时就会发生。
- 内核的崩溃,当cpu拿到锁执行下面程序时,如果遇到阻塞发生例如copy_from_usr() copy_to_usr() kmalloc()和msleep()等调用。
- 进程拥有自旋锁的时候,该CPU上是禁止抢占的。
- 一般用于多CPU之间的资源竞争,单核上一般不用。
- 由于自旋锁的这个忙等待的特性,注定了他使用场景上的限制--自旋锁不应该被长时间的持有(消耗CPU资源),一般应用在中断上下文。
自旋锁的死锁
死锁的两种情况
上面这种有一个条件是在单核CPU(可用ps -ef| grep softirq查看是否是单个CPU,因为一个softirq对应一个CPU),因为进程A此时拥有自旋锁了,该CPU被禁止抢占了,此时A内核阻塞了,又放弃了CPU占用,进程B申请自旋锁后一直自旋,一直占用CPU,其他进程,包含进程A又无法获得CPU了,导致了死锁。
死锁举例:
如何避免死锁
第一条意思是针对中断程序,拥有自旋锁时候要禁止中断;第三条意思是同一个任务不能申请两次锁,不然会死锁。
按同样顺序申请锁是指两个进程都要申请A和B两种锁,两个进程必须按照同样顺序沈亮A和B两种锁。
下面是一个死锁的实例代码:
static int major = 237;
static int minor = 0;
static dev_t devno;
static struct cdev cdev;
struct device *class_dev = NULL;
struct class *cls;static spinlock_t lock;
static int flage = 1;static int hello_open (struct inode *inode, struct file *filep)
{printk("hello_open()\n");spin_lock(&lock);if(flage != 1){spin_unlock(&lock);return -EBUSY;}flage = 0;return 0;
}
static int hello_release (struct inode *inode, struct file *filep)
{printk("hello_release()\n");flage = 1;spin_unlock(&lock); //这里在close文件的时候释放自旋锁,模拟死锁场景return 0;
}#define KMAX_LEN 32
char kbuf[KMAX_LEN+1] = "kernel";//read(fd,buff,40);static ssize_t hello_read (struct file *filep, char __user *buf, size_t size, loff_t *pos)
{int error;if(size > strlen(kbuf)){size = strlen(kbuf);}if(copy_to_user(buf,kbuf, size)){error = -EFAULT;return error;}return size;
}
//write(fd,buff,40);
static ssize_t hello_write (struct file *filep, const char __user *buf, size_t size, loff_t *pos)
{int error;if(size > KMAX_LEN){size = KMAX_LEN;}memset(kbuf,0,sizeof(kbuf));if(copy_from_user(kbuf, buf, size)){error = -EFAULT;return error;}printk("%s\n",kbuf);return size;
}static struct file_operations hello_ops =
{.open = hello_open,.release = hello_release,.read = hello_read,.write = hello_write,
};
static int hello_init(void)
{int result;int error;printk("hello_init \n");spin_lock_init(&lock); //初始化自旋锁result = register_chrdev( major, "hello", &hello_ops);if(result < 0){printk("register_chrdev fail \n");return result;}cls = class_create(THIS_MODULE, "hellocls");if (IS_ERR(cls)) {printk(KERN_ERR "class_create() failed for cls\n");result = PTR_ERR(cls);goto out_err_1;}devno = MKDEV(major, minor);class_dev = device_create(cls, NULL, devno, NULL, "hellodev");if (IS_ERR(class_dev)) {result = PTR_ERR(class_dev);goto out_err_2;}return 0;out_err_2:class_destroy(cls);
out_err_1:unregister_chrdev(major,"hello");return result;
}
static void hello_exit(void)
{printk("hello_exit \n");device_destroy(cls, devno);class_destroy(cls);unregister_chrdev(major,"hello");return;
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
main()
{int fd;int len;char buf[64]={0};char buf2[64+1]="peng";fd = open("/dev/hellodev",O_RDWR);if(fd<0){perror("open fail \n");return;}sleep(10); //休眠时候放弃了占用CPUclose(fd);
}