在Linux线程同步机制里常用有互斥锁和信号量两种方法.
在理解为什么有些代码需要加锁后再执行,先了解一下原子操作的概念
所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,
中间不会有任何 context switch (切换到另一个线程)
原子操作是不可分割的,在执行完毕之前不会被任何其它任务或事件中断
——百度百科
意思就是这种操作是单位级的操作,执行过程保证绝对不受影响,执行结果一定 .而且互斥锁必须总是由它上锁的线程解锁。
RTDM还有一种锁叫自旋锁(Spinlock),它和互斥锁的区别是:
自旋锁是一种互斥锁的实现方式,相比一般的互斥锁会在等待期间放弃cpu,自旋锁则是不断循环并测试锁的状态,这样就一直占着cpu。自旋锁:与互斥量类似,它不是通过休眠使进程阻塞,而是在获取锁之前一直处于忙等(自旋)阻塞状态。用在以下情况:锁持有的时间短,而且线程并不希望在重新调度上花太多的成本。”原地打转”(自旋)。
xenomai RTDM 模式下的互斥锁有关函数
//初始化互斥锁
void rtdm_mutex_init ( rtdm_mutex_t * mutex )
此函数初始化一个带优先级反转保护的基本互斥锁,它不允许锁的所有者递归第锁定同一个锁.
//请求互斥锁
int rtdm_mutex_lock (rtdm_mutex_t * mutex)
参数mutex, 是rtdm_mutex_init()返回的互斥句柄
这是rtdm_mutex_timedlock()的轻量级版本,表示无限超时。
//释放互斥锁
void rtdm_mutex_unlock ( rtdm_mutex_t * mutex )
参数mutex是rtdm_mutex_init()返回的互斥句柄
这个函数释放给定的互斥锁,唤醒可能被rtdm_mutex_lock()或rtdm_mutex_timedlock()阻塞的等待者。
//请求带超时的互斥锁
int rtdm_mutex_timedlock ( rtdm_mutex_t * mutex,
nanosecs_rel_t timeout,
rtdm_toseq_t * timeout_seq
)
参数:
mutex , 是rtdm_mutex_init()返回的互斥句柄
timeout ,相对超时时间单位纳秒,参考 RTDM_TIMEOUT_xxx 具体的数值
timeout_seq, 由rtdm_toseq_init()或NULL返回的超时序列的句柄
返回值:
成功请求返回0,其他值如下
-ETIMEDOUT ,如果在指定的时间内没有满足请求。
-EWOULDBLOCK ,如果timeout为负,而信号量值当前不是正的。
-EIDRM ,如果互斥对象已被销毁。
-EPERM ,如果检测到非法调用环境。
详细超时的值
RTDM_TIMEOUT_INFINITE 永远阻塞
RTDM_TIMEOUT_NONE 任何负超时都意味着无阻塞。
//动态初始化锁,该函数对任务没有实时性限制,它申请的是一个自旋锁
static void rtdm_lock_init( rtdm_lock_t * lock )
//从不可抢占的上下文获取锁
static void rtdm_lock_get( rtdm_lock_t *lock )
//释放锁而不进行抢占恢复
static void rtdm_lock_put( rtdm_lock_t * lock )
//释放锁并恢复抢占状态
static void rtdm_lock_put_irqrestore(rtdm_lock_t *lock,rtdm_lockctx_t context)
//获取锁定和禁用抢占,通过停止头域
define rtdm_lock_get_irqsave(__lock, __context) /
((__context) = __rtdm_lock_get_irqsave(__lock))
信号量(Semaphore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
xenomai RTDM 模式下的信号量有关函数
rtdm_sem_init(rtdm_sem_t * sem,unsigned long value ) //信号量初始化,值为value
int rtdm_sem_down (rtdm_sem_t *sem) //信号量自减
int rtdm_sem_up (rtdm_sem_t *sem) //信号量增加
Mutex/Semaphore应用场景demo########
在rtdm设备中,rtdm_driver的device_flags若设置了独占模式保护了内核资源的安全性,但这样该模块则只能被一个应用打开,限制了其并发使用,要解决这个问题就需要采用锁机制来保证既能多个应用使用.
点击查看代码
/*
* Copyright (C) 2020 BNIS.
*
*/
#include <linux/module.h>
#include <rtdm/driver.h>
#include <rtdm/testing.h>
MODULE_DESCRIPTION("RTDM Mutext test");
MODULE_AUTHOR("bniss@aliyun.com");
MODULE_VERSION("0.1.0");
MODULE_LICENSE("GPL");
struct rtdm_basic_context {
int buffer_data;
};
rtdm_lock_t lock;
rtdm_sem_t sem;
int open_times = 0;//统计总打开的次数
int inUse_times = 0;//当前还在是使用的次数
static int rtdm_basic_open(struct rtdm_fd *fd, int oflags)
{
struct rtdm_basic_context *ctx = rtdm_fd_to_private(fd);
//打开的设备名: /dev/rtdm1
rtdm_lock_get(&lock);
rtdm_task_sleep(300000000ULL);//sleep 300ms test
open_times++;
inUse_times++;
printk("device is open , flag = %d ,open/inUse = %d/%d ",
oflags,open_times,inUse_times);
rtdm_lock_put(&lock);
return 0;
}
static void rtdm_basic_close(struct rtdm_fd *fd)
{
rtdm_lock_get(&lock);
inUse_times--;
printk("device is close. in use times = %d " , inUse_times );
rtdm_lock_put(&lock);
}
static int rtdm_basic_ioctl_rt(struct rtdm_fd *fd,
unsigned int request, void __user *arg)
{
rtdm_sem_down(&sem);
int ret = 0 ;
int *ext = (int*)arg;
printk(" RealTime Request = %d ! task id = %d ." , request,*ext);
switch (request) {
case 0x001:
printk(" RT Request case = 1 .");
break;
case 0x002:
printk(" RT Request case = 2. ");
ret = -EFAULT;
break;
case 0x003:
printk(" RT Request case = 3. ");
rtdm_task_sleep(500000ULL);
break;
default:
printk(" RT Request other_case = %d. ", request );
ret = -ENOSYS;
break;
}
rtdm_sem_up(&sem);
return ret;
}
static int rtdm_basic_ioctl_nrt(struct rtdm_fd *fd,
unsigned int request, void __user *arg)
{
rtdm_sem_down(&sem);
struct rtdm_basic_context *ctx = rtdm_fd_to_private(fd);
int* ext =(int*)arg;
int ret = 0 ;
printk(" No-RealTime Request = %d ! task id = %d ." , request ,*ext);
switch (request) {
case 0x001:
printk(" nRT Request case = 1 .");
break;
case 0x002:
printk(" nRT Request case = 2. ");
ret = -EFAULT;
break;
case 0x003:
printk(" nRT Request case = 3. ");
break;
default:
printk(" nRT Request other_case = %d. ", request );
ret = -ENOSYS;
}
rtdm_sem_up(&sem);
return ret;
}
static struct rtdm_driver rtdm_basic_driver = {
.profile_info = RTDM_PROFILE_INFO(rtdm_test_basic,
RTDM_CLASS_TESTING,
RTDM_SUBCLASS_RTDMTEST,
RTTST_PROFILE_VER),
.device_flags = RTDM_NAMED_DEVICE ,// 非独占| RTDM_EXCLUSIVE,
.device_count = 2,
.context_size = sizeof(struct rtdm_basic_context),
.ops = {
.open = rtdm_basic_open,
.close = rtdm_basic_close,
.ioctl_rt = rtdm_basic_ioctl_rt,
.ioctl_nrt = rtdm_basic_ioctl_nrt,
},
};
static struct rtdm_device device[2] = {
[0 ... 1] = {
.driver = &rtdm_basic_driver,
.label = "rtdm%d",
}
};
static int __init rtdm_test_init(void)
{
int i, ret;
printk("------rtdm initial--------- ");
if (!realtime_core_enabled())
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(device); i++) {
ret = rtdm_dev_register(device + i);
if (ret)
goto fail;
}
rtdm_lock_init(&lock); //初始化自旋锁
rtdm_sem_init(&sem,1);//信号量初始化
return 0;
fail:
printk("------ init fail:%d -------- ",ret);
while (i-- > 0)
rtdm_dev_unregister(device + i);
return ret;
}
static void __exit rtdm_test_exit(void)
{
int i;
printk("------rtdm exit--------- ");
for (i = 0; i < ARRAY_SIZE(device); i++)
rtdm_dev_unregister(device + i);
rtdm_sem_destroy(&sem);
}
module_init(rtdm_test_init);
module_exit(rtdm_test_exit);
用户端测试调用
点击查看代码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <rtdm/rtdm.h>
#include <errno.h>
#include <sys/resource.h>
#include <unistd.h>
#include <native/task.h>
#define DEVICE_NAME "rtdm1"
#define MAX_PRIO 99
RT_TASK task_desc1;
RT_TASK task_desc2;
RT_TASK task_desc3;
typedef struct _TAG_TASK_DATA {
int taskId;
} TASK_DATA;
void task_body (void *cookie){
TASK_DATA* taskDat =(TASK_DATA*) cookie;
int tid = 0;
if(taskDat){
tid = taskDat->taskId;
}
int i,ret,device;
rt_printf(" task_body process ,task_id = %d /n",tid);
/* open the device */
device = rt_dev_open(DEVICE_NAME, 0);
if (device < 0) {
rt_printf("ERROR : can't open device %s (%s)/n", DEVICE_NAME, strerror(-device));
return ;
}
else{
rt_printf("device open sucess ! /n");
}
for(i=1;i<5;i++){
ret = rt_dev_ioctl (device, i, &tid );
rt_printf("user-request = %d , return = %d , taskId=%d /n", i, ret,tid );
rt_task_sleep(100000000ULL);//100ms
}
}
int main(int argc, char *argv)
{
int ret = 0;
printf("---test mutext in task --- /n");
ret = rt_task_create(&task_desc1, "my_task1", 0, MAX_PRIO , T_FPU);
if (ret < 0) {
fprintf(stderr, "Failed to create task1: %s/n", strerror(-ret));
return -1;
}
ret = rt_task_create(&task_desc2, "my_task2", 0, MAX_PRIO , T_FPU);
if (ret < 0) {
fprintf(stderr, "Failed to create task2: %s/n", strerror(-ret));
return -1;
}
ret = rt_task_create(&task_desc3, "my_task3", 0, MAX_PRIO , T_FPU);
if (ret < 0) {
fprintf(stderr, "Failed to create task3: %s/n", strerror(-ret));
return -1;
}
printf("Starting my_task.../n");
TASK_DATA taskData;
taskData.taskId = 1;
ret = rt_task_start(&task_desc1, &task_body, &taskData);
if (ret < 0) {
fprintf(stderr, "Failed to start task1: %s/n", strerror(-ret));
return -1;
}
taskData.taskId = 2;
ret = rt_task_start(&task_desc2, &task_body, &taskData);
if (ret < 0) {
fprintf(stderr, "Failed to start task2: %s/n", strerror(-ret));
return -1;
}
taskData.taskId = 3;
ret = rt_task_start(&task_desc3, &task_body, &taskData);
if (ret < 0) {
fprintf(stderr, "Failed to start task3: %s/n", strerror(-ret));
return -1;
}
exit:
printf("----waiting task -------00/n");
rt_task_sleep( 3000000000ULL );
printf("----task exit--------01/n");
return 0;
}
在内核调试输出中我们可以看到有
[Xenomai] my_task1[6441] called regular ioctl() on /dev/rtdm/rtdm1
这是在用户端调用ioctl 在 request = 0x4 时输出的,此请求在rt函数返回-ENOSYS, 所以切换成nrt非实时处理.
————————————————
版权声明:本文为CSDN博主「wabil」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wabil/article/details/104418388
————————————————
版权声明:本文为CSDN博主「wabil」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wabil/article/details/104418388
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/271877.html