Aprendizaje integrado impulsado por LINUX 8 condiciones de carrera y problemas relacionados con la concurrencia (4) semáforo

Aprendizaje integrado impulsado por LINUX 8 condiciones de carrera y problemas relacionados con la concurrencia (4) semáforo


El semáforo se ejecuta en el proceso y puede finalizar debido a un tiempo de espera o una señal de interrupción, y puede realizar la operación de suspensión.

1. Archivos de encabezado, funciones y descripciones

//源码位置:include/linux/semaphore.h
//结构体对象:struct semaphore {}
struct semaphore {
    
    
        raw_spinlock_t          lock;
        unsigned int            count;
        struct list_head        wait_list;
};

/*
    初始化信号量函数:sema_init()
*/
static inline void sema_init(struct semaphore *sem, int val)
{
    
    
        static struct lock_class_key __key;
        *sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
        lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}
#define __SEMAPHORE_INITIALIZER(name, n)                                \
{                                                                       \
        .lock           = __RAW_SPIN_LOCK_UNLOCKED((name).lock),        \
        .count          = n,                                            \
        .wait_list      = LIST_HEAD_INIT((name).wait_list),             \
}

/*
    获取信号量函数:down()
    函数实现源码位置:kernel/semaphore.c
*/
void down(struct semaphore *sem)
{
    
    
        unsigned long flags;
        raw_spin_lock_irqsave(&sem->lock, flags);
        /*
           判断是否还有信号量对象可以获取
               如果有,执行 -1 操作,
               如果没有,等待信号到来(但可以因超时或中断信号而结束)
           关于__down()函数的实现,见源码附A.1
        */
        if (likely(sem->count > 0))
                sem->count--;
        else
                __down(sem);
        raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(down);

/*
    释放信号量函数 :up()
    实现源码位置:kernel/semaphore.c
*/
void up(struct semaphore *sem)
{
    
    
        unsigned long flags;
        raw_spin_lock_irqsave(&sem->lock, flags);
        /*
            判断信号量等待列表是否有对象:
                如果没有对象(即:count =0),执行count ++ 操作
                如果有对象,唤醒等待的对象;
        */
        if (likely(list_empty(&sem->wait_list)))
                sem->count++;
        else
                __up(sem);//实现方式,见源码,附A1.2
        raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);

Dos, ejemplo de código (espacio del kernel)

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/semaphore.h>
static struct semaphore sema;

static int sema_open(struct inode * inode , struct file * file){
    
    
    printk("文件将准备获取信号量......\n");
    down(&sema);//获取信号量,如果没有,将执休眠等待操作
    printk("获取信号量成功,打开文件成功\n");
    return 0;
}
static int sema_close(struct inode * inode , struct file * file){
    
    
    printk("准备关闭文件\n");
    up(&sema);//释放信号量,并唤醒所有等待信号量的进程
    printk("文件关闭完成,释放信号量\n");
    return 0;
}

struct file_operations f_ops = {
    
    
    .owner = THIS_MODULE,
    .open = sema_open,
    .release = sema_close
};

struct miscdevice misc_fops = {
    
    
    .minor = MISC_DYNAMIC_MINOR,
    .name  = "mysema_drv",
    .fops = &f_ops
};

static int mysema_fops_init(void){
    
    
    sema_init(&sema,1);//初始化信号量对象,每次只能1个进程打开文件
    misc_register(&misc_fops);
    return 0;
}
static void mysema_fops_exit(void){
    
    
    misc_deregister(&misc_fops);
}
module_init(mysema_fops_init);
module_exit(mysema_fops_exit);
MODULE_LICENSE("GPL");

Tres, ejemplo de código (espacio de usuario)

include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc , char * argv[]){
    
    
    int fp  = open(argv[1],O_RDWR);
    if(fp < 0)
        goto err_file;
    sleep(100);
    close(fp)
    return 0;
err_file :
    printf("文件打开失败\n");
    return -1;
}

Cuatro, prueba

#关闭超时功能
echo 0 > /proc/sys/kernel/hung_task_timeout_secs 
/drivers/test # ./a /dev/mysema_drv &
/drivers/test # [  594.834000] 文件将准备获取信号量......
[  594.835000] 获取信号量成功,打开文件成功
/drivers/test # ./a /dev/mysema_drv &
/drivers/test # [  598.652000] 文件将准备获取信号量......
/drivers/test # ./a /dev/mysema_drv &
/drivers/test # 
/drivers/test # ./a /dev/mysema_drv &
[  694.838000] 准备关闭文件
[  694.838000] 文件关闭完成,释放信号量
[  694.839000] 获取信号量成功,打开文件成功
[  694.840000] 文件将准备获取信号量......
[  794.838000] 准备关闭文件
[  794.838000] 文件关闭完成,释放信号量
[  794.839000] 获取信号量成功,打开文件成功
[  794.840000] 文件将准备获取信号量......
[  894.839000] 准备关闭文件
[  894.839000] 文件关闭完成,释放信号量
[  894.840000] 获取信号量成功,打开文件成功
[  894.841000] 准备关闭文件
[  894.842000] 文件关闭完成,释放信号量

Anexo A.1

/*
     源码位置:kernel/semaphore.c
*/
static noinline void __sched __down(struct semaphore *sem)
{
    
    
        __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
} 

static inline int __sched __down_common(struct semaphore *sem, long state,
                                                                long timeout)
{
    
    
        struct task_struct *task = current;
        struct semaphore_waiter waiter;

        list_add_tail(&waiter.list, &sem->wait_list);
        waiter.task = task;
        waiter.up = 0;
        /*死循环,等待*/
        for (;;) {
    
    
                if (signal_pending_state(state, task))
                        goto interrupted;//因中断信号而结束
                if (timeout <= 0)
                        goto timed_out;  //因超时而结束
                __set_task_state(task, state);
                raw_spin_unlock_irq(&sem->lock);
                timeout = schedule_timeout(timeout);
                raw_spin_lock_irq(&sem->lock);
                if (waiter.up) //当up()执行时,会将waiter.up =1;此时结束循环
                        return 0;
        }

 timed_out:
        list_del(&waiter.list);
        return -ETIME;

 interrupted:
        list_del(&waiter.list);
        return -EINTR;
}

Apéndice 1.2

static noinline void __sched __up(struct semaphore *sem)
{
    
    
        struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
                                                struct semaphore_waiter, list);
        list_del(&waiter->list);
        waiter->up = 1;//此刻down()函数中的死循环会结束 
        wake_up_process(waiter->task);
}

Supongo que te gusta

Origin blog.csdn.net/weixin_47273317/article/details/108061948
Recomendado
Clasificación