赞
踩
并发是指在同一时间段内,多个任务交替执行。并发可以发生在单核处理器上,通过任务切换实现
并行是指在同一时间段内,多个任务同时执行。并行可以发生在多核处理器上,例如下图任务1 和任务3同时进行,这是一个并行的过程
并发同样可以发生在多核处理器之间,通过真正的并行执行实现。并发的主要目的是提高程序的响应能力和利用系统资源的效率。
竞争是指多个线程或进程争夺相同资源(如内存、文件、网络连接等)时发生的冲突情况。竞争会导致资源争用问题,如死锁、饥饿、竞态条件等。
竞态条件是指多个线程或进程在没有适当同步的情况下访问和修改共享资源时,操作的执行顺序影响到最终结果,从而导致不确定性和错误的情况。竞态条件通常发生在以下情况下:
示例:
假设有两个线程A和B,它们都试图增加一个共享变量counter
的值。如果没有同步机制,可能会发生以下情况:
Thread A: load counter (counter = 0)
Thread B: load counter (counter = 0)
Thread A: increment counter (counter = 1)
Thread B: increment counter (counter = 1)
Thread A: store counter (counter = 1)
Thread B: store counter (counter = 1)
最终结果是counter
为1,而不是预期的2。
死锁是指两个或多个线程或进程在等待彼此持有的资源,导致所有参与者都无法继续执行的情况。死锁通常发生在以下情况下:
示例:
假设有两个线程A和B,以及两个资源R1和R2:
Thread A: lock R1
Thread B: lock R2
Thread A: wait for R2 (held by B)
Thread B: wait for R1 (held by A)
此时,线程A和B都在等待对方释放资源,形成循环等待,导致死锁。
饥饿是指一个线程或进程长期无法获得所需资源,从而无法继续执行的情况。饥饿通常发生在以下情况下:
示例:
假设有多个线程T1、T2、T3,其中T1和T2频繁请求资源R,而T3的请求较少:
Thread T1: request R
Thread T2: request R
Thread T3: request R (but always after T1 and T2)
由于T1和T2总是优先获得资源R,T3的请求得不到满足,导致T3长期处于饥饿状态。
原子操作是指在多线程或多进程环境下,不可分割、不能被中断的操作。原子操作要么完全执行,要么完全不执行,执行过程中不会被其他操作干扰。它们是并发编程中的基本构建块,用于实现线程安全的操作。
特点:
不可分割性:原子操作是不可分割的,执行过程中不会被中断,确保操作的完整性。
并发安全:原子操作在多线程环境中是安全的,多个线程可以并发执行而不会引起竞态条件。
应用:
原子操作在多线程编程中有广泛的应用,特别是在以下场景中:
优缺点
优点:
缺点:
Linux内核中常用的原子操作包括以下几种:
atomic_t
:定义一个原子变量。atomic_set(atomic_t *v, int i)
:将原子变量设置为给定值。atomic_read(const atomic_t *v)
:读取原子变量的值。atomic_add(int i, atomic_t *v)
:将给定值加到原子变量上。atomic_sub(int i, atomic_t *v)
:从原子变量中减去给定值。atomic_inc(atomic_t *v)
:将原子变量递增1。atomic_dec(atomic_t *v)
:将原子变量递减1。atomic_cmpxchg(atomic_t *v, int old, int new)
:如果原子变量的值等于old
,则将其值设置为new
。set_bit(int nr, volatile unsigned long *addr)
:设置特定位。clear_bit(int nr, volatile unsigned long *addr)
:清除特定位。test_and_set_bit(int nr, volatile unsigned long *addr)
:测试并设置特定位。test_and_clear_bit(int nr, volatile unsigned long *addr)
:测试并清除特定位。#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #include <linux/device.h> #define DEVICE_NAME "my_char_device" #define BUFFER_SIZE 64 static atomic_t counter = ATOMIC_INIT(0); static int major_number; static struct class *my_class = NULL; static struct device *my_device = NULL; static struct cdev mydev; // 声明 cdev 结构体 static int device_open(struct inode *inode, struct file *file) { printk(KERN_INFO "Device opened\n"); return 0; } static int device_release(struct inode *inode, struct file *file) { printk(KERN_INFO "Device closed\n"); return 0; } static ssize_t device_read(struct file *filp, char *buffer, size_t len, loff_t *offset) { char msg[BUFFER_SIZE]; int msg_len; msg_len = snprintf(msg, BUFFER_SIZE, "%d\n", atomic_read(&counter)); if (*offset >= msg_len) { return 0; } if (len > msg_len - *offset) { len = msg_len - *offset; } if (copy_to_user(buffer, msg + *offset, len)) { return -EFAULT; } *offset += len; return len; } static ssize_t device_write(struct file *filp, const char *buffer, size_t len, loff_t *offset) { char msg[BUFFER_SIZE]; if (len > BUFFER_SIZE - 1) { len = BUFFER_SIZE - 1; } if (copy_from_user(msg, buffer, len)) { return -EFAULT; } msg[len] = '\0'; if (strcmp(msg, "inc\n") == 0) { atomic_inc(&counter); } else if (strcmp(msg, "dec\n") == 0) { atomic_dec(&counter); } else if (sscanf(msg, "add %d\n", &len) == 1) { atomic_add(len, &counter); } else if (sscanf(msg, "sub %d\n", &len) == 1) { atomic_sub(len, &counter); } else { return -EINVAL; } return len; } static struct file_operations fops = { .open = device_open, .release = device_release, .read = device_read, .write = device_write, }; static int __init test_init(void) { int retval; dev_t dev; printk(KERN_INFO "module init success\n"); // 1. 动态分配主次设备号 retval = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); if (retval < 0) { printk(KERN_ERR "Failed to allocate major number\n"); goto fail_alloc_chrdev_region; } major_number = MAJOR(dev); printk(KERN_INFO "major number is: %d, minor number is: %d\n", major_number, MINOR(dev)); // 2. 初始化 cdev 结构体并添加到内核 cdev_init(&mydev, &fops); retval = cdev_add(&mydev, dev, 1); if (retval < 0) { printk(KERN_ERR "Failed to add cdev\n"); goto fail_cdev_add; } // 3. 创建设备类 my_class = class_create(THIS_MODULE, "my_class"); if (IS_ERR(my_class)) { printk(KERN_ERR "Failed to create class\n"); retval = PTR_ERR(my_class); goto fail_class_create; } // 4. 申请设备,内核空间就会通知用户空间的udev 进行创建设备,驱动程序本身自己是创建不了文件的! my_device = device_create(my_class, NULL, dev, NULL, DEVICE_NAME); if (IS_ERR(my_device)) { printk(KERN_ERR "Failed to create device\n"); retval = PTR_ERR(my_device); goto fail_device_create; } printk(KERN_INFO "my_char_device: module loaded\n"); return 0; fail_device_create: class_destroy(my_class); fail_class_create: cdev_del(&mydev); fail_cdev_add: unregister_chrdev_region(dev, 1); fail_alloc_chrdev_region: return retval; } static void __exit test_exit(void) { dev_t dev = MKDEV(major_number, 0); if (my_device) device_destroy(my_class, dev); if (my_class) class_destroy(my_class); cdev_del(&mydev); unregister_chrdev_region(dev, 1); printk(KERN_INFO "my_char_device: module unloaded\n"); } module_init(test_init); module_exit(test_exit); MODULE_AUTHOR("Marxist"); MODULE_LICENSE("GPL");
可以使用echo 和 cat 来简单演示 原子操作加减
root@imx8qmmek:~/module_test# echo "inc" > /dev/my_char_device
root@imx8qmmek:~/module_test# echo "add 5" > /dev/my_char_device
-sh: echo: write error: Invalid argument
root@imx8qmmek:~/module_test# cat /dev/my_char_device
6
ps:不知道为什么 sscanf 会报错,但是仍然是成功执行了
自旋锁(spinlock)是一种用于多处理器系统中的同步机制,它可以保护共享资源免受多个进程或线程的并发访问。自旋锁在等待锁的时候不会引起调度器的上下文切换,而是不断地循环检查锁的状态,直到锁可用为止。
在Linux内核中,自旋锁的API提供了一组函数来初始化、获取和释放自旋锁。以下是常用的自旋锁API:
spinlock_t my_lock;
:定义一个自旋锁。spin_lock_init(&my_lock);
:初始化自旋锁。spin_lock(&my_lock);
:获取自旋锁。如果锁已经被其他处理器获取,该处理器将进入忙等待。spin_unlock(&my_lock);
:释放自旋锁。spin_lock_irqsave(&my_lock, flags);
:获取自旋锁并保存中断状态,禁用本地中断。spin_unlock_irqrestore(&my_lock, flags);
:释放自旋锁并恢复中断状态。#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #include <linux/device.h> #define DEVICE_NAME "my_char_device" #define BUFFER_SIZE 64 static atomic_t counter = ATOMIC_INIT(0); static spinlock_t my_lock; static int major_number; static struct class *my_class = NULL; static struct device *my_device = NULL; static struct cdev mydev; // 声明 cdev 结构体 static int shared_resource = 0; static int device_open(struct inode *inode, struct file *file) { printk(KERN_INFO "Device opened\n"); spin_lock_init(&my_lock); // 打开设备的时候 初始化自旋锁 return 0; } static int device_release(struct inode *inode, struct file *file) { printk(KERN_INFO "Device closed\n"); return 0; } static ssize_t device_read(struct file *filp, char *buffer, size_t len, loff_t *offset) { // 保护共享资源 spin_lock(&my_lock); shared_resource++; printk(KERN_INFO "Shared resource value: %d\n", shared_resource); spin_unlock(&my_lock); // 模拟中断上下文中使用自旋锁 unsigned long flags; spin_lock_irqsave(&my_lock, flags); shared_resource++; printk(KERN_INFO "Shared resource value (interrupt context): %d\n", shared_resource); spin_unlock_irqrestore(&my_lock, flags); return 0; } static ssize_t device_write(struct file *filp, const char *buffer, size_t len, loff_t *offset) { char msg[BUFFER_SIZE]; if (len > BUFFER_SIZE - 1) { len = BUFFER_SIZE - 1; } if (copy_from_user(msg, buffer, len)) { return -EFAULT; } msg[len] = '\0'; if (strcmp(msg, "inc\n") == 0) { atomic_inc(&counter); } else if (strcmp(msg, "dec\n") == 0) { atomic_dec(&counter); } else if (sscanf(msg, "add %d\n", &len) == 1) { atomic_add(len, &counter); } else if (sscanf(msg, "sub %d\n", &len) == 1) { atomic_sub(len, &counter); } else { return -EINVAL; } return len; } static struct file_operations fops = { .open = device_open, .release = device_release, .read = device_read, .write = device_write, }; static int __init test_init(void) { int retval; dev_t dev; printk(KERN_INFO "module init success\n"); // 1. 动态分配主次设备号 retval = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); if (retval < 0) { printk(KERN_ERR "Failed to allocate major number\n"); goto fail_alloc_chrdev_region; } major_number = MAJOR(dev); printk(KERN_INFO "major number is: %d, minor number is: %d\n", major_number, MINOR(dev)); // 2. 初始化 cdev 结构体并添加到内核 cdev_init(&mydev, &fops); retval = cdev_add(&mydev, dev, 1); if (retval < 0) { printk(KERN_ERR "Failed to add cdev\n"); goto fail_cdev_add; } // 3. 创建设备类 my_class = class_create(THIS_MODULE, "my_class"); if (IS_ERR(my_class)) { printk(KERN_ERR "Failed to create class\n"); retval = PTR_ERR(my_class); goto fail_class_create; } // 4. 申请设备,内核空间就会通知用户空间的udev 进行创建设备,驱动程序本身自己是创建不了文件的! my_device = device_create(my_class, NULL, dev, NULL, DEVICE_NAME); if (IS_ERR(my_device)) { printk(KERN_ERR "Failed to create device\n"); retval = PTR_ERR(my_device); goto fail_device_create; } printk(KERN_INFO "my_char_device: module loaded\n"); return 0; fail_device_create: class_destroy(my_class); fail_class_create: cdev_del(&mydev); fail_cdev_add: unregister_chrdev_region(dev, 1); fail_alloc_chrdev_region: return retval; } static void __exit test_exit(void) { dev_t dev = MKDEV(major_number, 0); if (my_device) device_destroy(my_class, dev); if (my_class) class_destroy(my_class); cdev_del(&mydev); unregister_chrdev_region(dev, 1); printk(KERN_INFO "my_char_device: module unloaded\n"); } module_init(test_init); module_exit(test_exit); MODULE_AUTHOR("Marxist"); MODULE_LICENSE("GPL");
使用cat 读取设备,使用dmesg
查看内核消息
[33585.032083] module init success
[33585.035302] major number is: 235, minor number is: 0
[33585.040606] my_char_device: module loaded
[33594.159584] Device opened
[33594.162327] Shared resource value: 1
[33594.165941] Shared resource value (interrupt context): 2
[33594.171320] Device closed
[33607.662150] Device opened
[33607.664859] Shared resource value: 3
[33607.668436] Shared resource value (interrupt context): 4
[33607.673827] Device closed
信号量(Semaphore)是一种用于多线程或多进程同步和互斥的机制。它由荷兰计算机科学家 Edsger Dijkstra 在1960年代提出,用于解决操作系统中的资源共享问题。信号量主要用于控制对共享资源的访问,避免竞争条件和死锁。
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #include <linux/device.h> #include <linux/semaphore.h> #define DEVICE_NAME "my_char_device" #define BUFFER_SIZE 64 static atomic_t counter = ATOMIC_INIT(0); static int major_number; static struct class *my_class = NULL; static struct device *my_device = NULL; static struct cdev mydev; // 声明 cdev 结构体 static struct semaphore my_semaphore; //定义信号量 static int shared_resource = 0; //共享资源 static int device_open(struct inode *inode, struct file *file) { printk(KERN_INFO "Device opened\n"); // 初始化信号量 设为1 代表互斥,只能有一个进程访问 sema_init(&my_semaphore, 1); return 0; } static int device_release(struct inode *inode, struct file *file) { printk(KERN_INFO "Device closed\n"); return 0; } static ssize_t device_read(struct file *filp, char *buffer, size_t len, loff_t *offset) { int ret; size_t to_copy; // 获取信号量 if (down_interruptible(&my_semaphore)) { return -ERESTARTSYS; } // 模拟文件的读取行为 if (*offset >= sizeof(shared_resource)) { // 文件末尾,返回 0 up(&my_semaphore); return 0; } // 确定要复制的字节数 to_copy = min(len, (size_t)sizeof(shared_resource) - *offset); // 访问共享资源 ret = snprintf(buffer, to_copy, "%d\n", shared_resource); // 更新偏移量 *offset += ret; // 释放信号量 up(&my_semaphore); return ret; } static ssize_t device_write(struct file *filp, const char *buffer, size_t len, loff_t *offset) { char buf[64]; int new_value; // 获取信号量 if (down_interruptible(&my_semaphore)) { return -ERESTARTSYS; } // 从用户空间复制数据 if (copy_from_user(buf, buffer, len)) { up(&my_semaphore); return -EFAULT; } buf[len] = '\0'; if (sscanf(buf, "%d", &new_value) == 1) { shared_resource = new_value; } // 释放信号量 up(&my_semaphore); return len; } static struct file_operations fops = { .open = device_open, .release = device_release, .read = device_read, .write = device_write, }; static int __init test_init(void) { int retval; dev_t dev; printk(KERN_INFO "module init success\n"); // 1. 动态分配主次设备号 retval = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); if (retval < 0) { printk(KERN_ERR "Failed to allocate major number\n"); goto fail_alloc_chrdev_region; } major_number = MAJOR(dev); printk(KERN_INFO "major number is: %d, minor number is: %d\n", major_number, MINOR(dev)); // 2. 初始化 cdev 结构体并添加到内核 cdev_init(&mydev, &fops); retval = cdev_add(&mydev, dev, 1); if (retval < 0) { printk(KERN_ERR "Failed to add cdev\n"); goto fail_cdev_add; } // 3. 创建设备类 my_class = class_create(THIS_MODULE, "my_class"); if (IS_ERR(my_class)) { printk(KERN_ERR "Failed to create class\n"); retval = PTR_ERR(my_class); goto fail_class_create; } // 4. 申请设备,内核空间就会通知用户空间的udev 进行创建设备,驱动程序本身自己是创建不了文件的! my_device = device_create(my_class, NULL, dev, NULL, DEVICE_NAME); if (IS_ERR(my_device)) { printk(KERN_ERR "Failed to create device\n"); retval = PTR_ERR(my_device); goto fail_device_create; } printk(KERN_INFO "my_char_device: module loaded\n"); return 0; fail_device_create: class_destroy(my_class); fail_class_create: cdev_del(&mydev); fail_cdev_add: unregister_chrdev_region(dev, 1); fail_alloc_chrdev_region: return retval; } static void __exit test_exit(void) { dev_t dev = MKDEV(major_number, 0); if (my_device) device_destroy(my_class, dev); if (my_class) class_destroy(my_class); cdev_del(&mydev); unregister_chrdev_region(dev, 1); printk(KERN_INFO "my_char_device: module unloaded\n"); } module_init(test_init); module_exit(test_exit); MODULE_AUTHOR("Marxist"); MODULE_LICENSE("GPL");
root@imx8qmmek:~/module_test# rmmod kernel_test.ko
root@imx8qmmek:~/module_test# insmod kernel_test.ko
root@imx8qmmek:~/module_test# echo 5 > /dev/my_char_device
root@imx8qmmek:~/module_test# cat /dev/my_char_device
5
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。