[xenomai驱动] 互斥锁 和信号量 api

在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非实时处理.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值