头像

也许

广州医科大学




离线:22天前


最近来访(70)
用户头像
克里斯保罗
用户头像
Le_5
用户头像
ShawnShawn
用户头像
无名小子
用户头像
dys
用户头像
李京泽
用户头像
MJgopher
用户头像
NZX
用户头像
鳕鱼饭
用户头像
荒途孤影
用户头像
surfing_7
用户头像
15630138557
用户头像
Mondayhmd
用户头像
呐呐呐呐
用户头像
承影
用户头像
HAMRUS
用户头像
Malaimooooh
用户头像
Joanna
用户头像
Atacama_Dessert
用户头像
关注whale77


也许
22天前
xaml:基于XML的简单和声明性语言, 用于描述UI布局

XAML文件由平台特定的XAML处理器解释。

XAML处理器将XAML转换为描述UI元素的内部代码。

内部代码和C#代码通过部分类定义链接在一起,然后.NET编译器创建应用程序。

xaml 中每一个标签都声明了一个对象,通过对对象属性的赋值,可以改变UI显示效果

属性赋值语法:

    1. 使用字符串进行简单赋值 Attribute = value 只能是字符串

    2. 使用属性元素进行复杂赋值 

        <ClassName>
            <ClassName.Property>
                //以对象形式赋值
            <ClassName.Property />
        <ClassName />


WPF常用控件

    ContentControl 类 Button Window UserControl label RadioButton CheckBox

        设置内容的属性为 Content

        控件目录下只允许设置一次Content, 如果给按钮添加一个Image和一个文本显示Label, 报错误

    HeaderedContentControl 类 GroupBox TabItem

        设置内容的属性为 Content和Header

    ItemsControl 类 Menu ItemControl ListBox ComboBox TabControl 

        显示列表化数据

        内容属性为Items和ItemsSource

    TextBlock: 用于显示文本, 不允许编辑的静态文本。 Text设置显示文本的内容。

    TextBox: 用于输入/编辑内容的控件、作用与winform中TextBox类似, Text设置输入显示的内容。

    Button: 简单按钮、Content显示文本、Click可设置点击事件、Command可设置后台的绑定命令

WPF布局·

    WPF布局原则

    一个窗口中只能包含一个元素

    不应显示设置元素尺寸

    不应使用坐标设置元素的位置

    可以嵌套布局容器

WPF布局容器

StackPanel: 水平或垂直排列元素、Orientation属性分别: Horizontal / Vertical

    StackPanel主要用于垂直或水平排列元素、在容器的可用尺寸内放置有限个元素,元素的

    尺寸总和(长/高)不允许超过StackPanel的尺寸, 否则超出的部分不可见。

WrapPanel : 水平或垂直排列元素、针对剩余空间不足会进行换行或换列进行排列

DockPanel : 根据容器的边界、元素进行Dock.Top、Left、Right、Bottom设置

    DockPanel有一个LastChildFill属性, 该属性默认为true, 该属性作用为, 当容器中的最后一个元素时, 

    默认该元素填充DockPanel所有空间。

Grid : 类似table表格、可灵活设置行列并放置控件元素、比较常用

    able表格, 而Grid与其类似, Grid具备分割空间的能力。

UniformGrid : 指定行和列的数量, 均分有限的容器空间

Canvas : 使用固定的坐标设置元素的位置、不具备锚定停靠等功能





也许
22天前
委托:C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针,是一个变量

委托语法

//声明委托该委托可以引用任何一个带有一个单一的 string 参数的方法,并返回一个 int 类型变量
    public delegate int MyDelegate (string s);  

// 实例化委托:委托对象必须使用 new 关键字来创建,且与一个特定的方法有关
    public delegate void printString(string s);

    printString ps1 = new printString(WriteToScreen);

    printString ps2 = new printString(WriteToFile);

委托特性:多播

    委托对象可使用 "+" 运算符进行合并

    委托对象可使用 "-" 运算符进行去除

    只有相同类型的委托可被合并或去除

    可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting)

委托作用:

回调---实现与定义解耦合,委托做函数参数 

关联事件和事件处理程序,见下文

事件

    两个概念:事件与事件处理程序

    事件:相当与一个用户操作,如点击按钮,选中图片等。也可以是属性

    事件处理程序:应用程序需要在事件发生时响应事件,这个相应动作称为事件处理程序

    事件与事件处理程序通过委托建立关联,语法中体现

声明事件:在委托前加了evevt关键字

本质是用来对委托类型的变量进行封装,事件有有委托的多播特性,可以注册事件处理程序

    public delegate void BoilerLogHandler(string status); //声明委托

// 基于上面的委托定义事件,事件触发的时候会自动调用委托

    public event BoilerLogHandler BoilerEventLog;  

    string msg;

    BoilerEventLog(msg)     //事件触发  

    BoilerEventLog += new BoilerLogHandler(print)  //注册事件处理程序

使用自定义事件步骤:
    1:定义delegate对象类型,他有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。

    2:定义事件参数类,此类应当从system.eventargs类派生。如果事件不带参数,这一步能省略。

    3:定义事件处理方法,他应当和delegate对象具有相同的参数和返回值类型。

    4:用event关键字定义事件对象,他同时也是个delegate对象。

    5:用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。

    6:在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能

    以public方式调用,但能被子类继承。名字是能是oneventname。

    7:在适当的地方调用事件触发方法触发事件

事件的应用:
    线程间通信

    模块解耦合

主窗体生成一个子窗体,子窗体某些操作可以改变主窗体的控件显示,

强耦合:主窗体把自身指针传给子窗体,子窗体通过this指针改变主窗体

松耦合:子窗体通过事件发布触发,主窗体订阅事件




也许
1个月前

为什么引入动态链接

静态链接的缺点:

   浪费磁盘、内存空间:每个可执行文件都包含一个lib.so, 磁盘存储,内存加载

   更新困难,更新lib.so, 与之相关的应用程序都要重新链接

引入动态链接:将链接过程推迟到程序运行时

    节约磁盘,内存空间,磁盘和内存只需要存储一个lib.so文件

    更新简单,只需要将新的lib.so文件覆盖原来的文件即可,在程序运行时会链接新的库文件

生成地址无关的代码

能不能像可执行文件一样确定虚拟内存地址?Linux下为0x08040000

    不能,可执行文件只有一个,动态库文件有很多个,固定地址会产生地址覆盖

解决方法:装载时重定位:在程序装载时确定动态库虚拟内存地址,就好像静态链接,链接时确定虚拟内存地址一样

装载时重定位有什么问题?

    前面提到物理内存中只有一份lib.so,每个进程都共享这个动态库,即共享这个动态库的指令(数据每个进程都有副本)

    (多个进程的虚拟地址映射到同一块物理内存),但是共享库在装载到进程虚拟内存,重定位后共享库中有些指令会发

    生改变(对于不同进程装载时动态库的虚拟内存地址不同,当进程访问到动态库的全局变量或函数时,属于模块外

    部访问,要重定位动态库的全局变量的地址,原来不确定的地址会被填充上,这时动态库指令发生改变),这样共享库

    的指令部分对于每个进程都不同,无法共享同一份动态库指令。需要单独存储。这样就失去了节约内存这一优势。

解决方法:生成与地址无关的代码

    基本思想:将动态库中与地址相关的指令放到数据部分,(因为地址相关的指令需要在装载时会被修改,并且每个进程

    修改后的结果都不一样),而数据部分每个进程独有,而不是共享,去除这些地址相关的指令,剩下的指令就是与地址

    无关的指令了,所有进程共享这部分指令。

实现:引入GOT(global offest table),全局偏移表,与动态库的数据部分放在一起,每个进程都有副本,独立

    动态链接器在装载动态库时会查找每个变量(函数)所在地址,然后填充GOT的各个项。(动态库中的变量和函数)

    每个进程都有一份GOT,当访问到动态库全局变量或函数时,就查表(动态库加载时已经填好了GOT)

核心:编译器通过GOT,将指令与地址相关的部分(变量,函数)放到了数据部分,每个进程独有

填充GOT过程:printf符号为例

    根据动态重定位表,动态链接器在全局符号表查找printf地址,假设为0x08801234,那么链接器就将这个地址填充

    到GOT中

延迟绑定(符号查找,重定位)

想想前面的方法有什么缺点?

    1. 编译器无法区分变量/函数时模块内部引用,还是模块外部引用,统一当做模块外部引用,但是如果该变量是模块

        内部引用,本来可以直接相对位置跳转,这时编译器也去查GOT,间接跳转,消耗时间

    2. 动态链接器在装载动态库时,填充GOT,把整个动态库的符号全部解析,填充,可能应用程序只使用一部分函数/变

        量,造成性能消耗。

解决方法:延迟绑定

    基本思想:当函数第一次被调用时才进行绑定(填GOT表项),没有用到就不进行绑定,所以程序在开始执行时,

        模块间的函数调用没有绑定,而是在调用时动态链接器才负责绑定。


实现:引入PLT(procedure linkage table)

    将符号查找,重定位过程放到函数调用时




也许
2个月前

类对象的组合方式

生成子类继承

关联:

    构造函数传参(参数是另一个类的对象)

    直接在类中生成另一个类的对象

代理模式

无多态

适合于:为其他对象提供一种代理以控制对这个对象的访问。

构造一个代理类,代理类中使用另一个对象方法

模板:a是代理类,b类实现协议类protocol;a中包含b类,a可以使用b类的接口

好处:main函数不需要修改了。只需要修改协议实现类

装饰模式

有多态

功能:扩展对象的功能,是继承关系的一个替换方案。

把要添加的附加功能分别放在单独的类中,并让这个类包含它要装饰的对象

当需要执行时,客户端就可以有选择地、按顺序地使用装饰功能包装对象

适用于:

    动态(自由装饰)的给一个对象添加一些额外的职责。就增加功能来说,此模式比生成子类更为灵活。

适配器模式

没有多态,只是个接口中转站

适用于:

    是将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

构造一个适配器类,继承自客户需求的类,将实际类的对象指针传入适配器类中,在适配器类中实现客户需要的接口,使用

在客户需求的接口中使用实际类对象的方法,从而实现了将一个类的接口转换成客户希望的另外一个接口

桥接模式

适用于:桥接模式是将抽象部分与实现部分分离(解耦合),使它们都可以独立的变化。

车 安装 发动机 ;不同型号的车,安装不同型号的发动机

继承方法实现:不同的车型,不同型号,安装不同类型的发动机,组合很多,会引起子类的泛滥。

需要把“安装发动机”这个事,做很好的分解;把抽象 和 行为实现 分开

车抽象类,发动机抽象类----有多态,不同类型车调用不同的安装发动机方法

发动机类:有安装发动机方法,具体实现

车类中:有安装发动机方法,不具体实现。使用发动机使用的方法,通过构造函数传入发动机类对象的指针来关联发动机类

客户端可以自由组合      不同型号的车和不同型号的发动机自由组合




也许
2个月前

工厂模式与建造者模式的区别

Factory模式不考虑对象的组装过程,而直接生成一个我想要的对象。

Builder模式先一个个的创建对象的每一个部件,再统一组装成一个对象

建造者模式

适用情况:

    一个对象的构建比较复杂,将一个对象的构建(?)和对象的表示(?)进行分离。

对象的表示:对象的属性

对象的构建:对象的属性赋值

房子(对象):属性有:墙,地板,门

如何构建不同的房子(公寓,别墅的属性不同)

Builder:为创建产品各个部分(墙,地板,门),统一抽象接口。

ConcreteBuilder:继承自Builder,具体的创建产品的各个部分(实现),部分A, 部分B,部分C。并返回组装完成的对象

Director:构造一个使用Builder接口的对象。

Product:这个例子中表示房子,表示被构造的复杂对象。

构建公寓,别墅 案例代码

#include <iostream>
using namespace std;
#include "string"
class House
{
public:
    void setFloor(string floor)
    {
        this->m_floor = floor;
    }
    void setWall(string wall)
    {
        this->m_wall = wall;
    }
    void setDoor(string door)
    {
        this->m_door = door;
    }

    //
    string getFloor()
    {
        return m_floor;
    }
    string setWall()
    {
        return  m_wall;
    }
    string setDoor()
    {
        return m_door;
    }

protected:
private:
    string  m_floor;
    string  m_wall;
    string  m_door;
};

class Builder
{
public:
    virtual void makeFloor() = 0;
    virtual void makeWall() =  0;
    virtual void makeDoor() = 0;
    virtual House *GetHouse() = 0;
};

//公寓
class FlatBuild : public Builder
{
public:
    FlatBuild()
    {
        pHouse = new House;
    }
    virtual void makeFloor()
    {
        pHouse->setFloor("flat Door");
    }
    virtual void makeWall()
    {
        pHouse->setWall("flat Wall");
    }
    virtual void makeDoor()
    {
        pHouse->setDoor("flat Door");
    }
    virtual House *GetHouse()
    {
        return pHouse;
    }

private:
    House *pHouse;
};

//别墅
class VillaBuild : public Builder
{
public:
    VillaBuild()
    {
        pHouse = new House;
    }
    virtual void makeFloor()
    {
        pHouse->setFloor("villa floor");
    }
    virtual void makeWall()
    {
        pHouse->setWall("villa Wall");
    }
    virtual void makeDoor()
    {
        pHouse->setDoor("villa Door");
    }
    virtual House *GetHouse()
    {
        return pHouse;
    }
private:
    House *pHouse;
};

class Director
{
public:
    void Construct(Builder *builder)
    {
        builder->makeFloor();
        builder->makeWall();
        builder->makeDoor();
    }
protected:
private:
};


void main()
{
    //客户直接造房子
    House *pHose = new House;
    pHose->setDoor("wbm门");
    pHose->setFloor("wbmFloor");
    pHose->setWall("wbmWall");
    delete pHose;


    /* //工程队直接造房子 
    Builder *builder = new FlatBuild;
    builder->makeFloor();
    builder->makeWall();
    builder->makeDoor();
    */

    //指挥者(设计师)指挥 工程队 和 建房子
    Director *director = new Director;

    //建公寓
    Builder *builder = new FlatBuild;
    director->Construct(builder); //设计师 指挥 工程队干活
    House *house = builder->GetHouse();
    cout << house->getFloor() << endl;
    delete house;
    delete builder;

    //建别墅
    builder = new VillaBuild;
    director->Construct(builder); //设计师 指挥 工程队干活
    house = builder->GetHouse();
    cout << house->getFloor() << endl;
    delete house;
    delete builder;

    delete director;

    system("pause");
    return ;
}



也许
2个月前

简单工厂模式

class Fruit
{
public:
    virtual void getFruit() = 0;

protected:
private:
};


class Banana : public Fruit
{
public:
    virtual void getFruit()
    {
        cout<<"香蕉"<<endl;

    }
protected:
private:
};


class Pear : public Fruit
{
public:
    virtual void getFruit()
    {
        cout<<"梨子"<<endl;

    }
protected:
private:
};

class Factory 
{
public:
    static Fruit* Create(char *name)
    {
        Fruit *tmp = NULL;
        if (strcmp(name, "pear") == 0)
        {
            tmp = new Pear();
        }
        else if (strcmp(name, "banana") == 0)
        {
            tmp = new Banana();
        }
        else
        {
            return NULL;
        }
        return tmp;
    }
protected:
private:
};

void main()
{
    Fruit *pear =  Factory::Create("pear");
    if (pear == NULL)
    {
        cout<<"创建pear失败\n";
    }
    pear->getFruit();

    Fruit *banana =  Factory::Create("banana");
    banana->getFruit();

    system("pause");
}

工厂模式

简单工厂模式,当有新的对象需要创建时,需要修改factory类的方法,不符合开闭原则

工厂模式:当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要

进行任何修改

#include "iostream"
using namespace std;

class Fruit
{
public:
    virtual void sayname()
    {
        cout<<"fruit\n";
    }
};

class FruitFactory
{
public:
    virtual Fruit* getFruit()
    {
        return new Fruit();
    }
};


//香蕉
class Banana : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"Banana \n"<<endl;
    }
};

//香蕉工厂
class BananaFactory : public  FruitFactory
{
public:
    virtual Fruit* getFruit()
    {
        return new Banana;
    }
};


//苹果
class Apple : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"Apple \n"<<endl;
    }
};

//苹果工厂
class AppleFactory : public  FruitFactory
{
public:
    virtual Fruit* getFruit()
    {
        return new Apple;
    }
};

void main()
{
    FruitFactory * ff  = NULL;
    Fruit *fruit = NULL;

    //1
    ff = new BananaFactory();
    fruit = ff->getFruit();
    fruit->sayname();

    delete fruit;
    delete ff;

    //2苹果
    ff = new AppleFactory();
    fruit = ff->getFruit();
    fruit->sayname();

    delete fruit;
    delete ff;

    cout<<"hello....\n";
    system("pause");
}

抽象工厂模式

工厂模式只能生产一个产品。(要么香蕉、要么苹果)

抽象工厂可以一下生产一个产品族(里面有很多产品组成)

class Fruit
{
public:
    virtual void sayname()
    {
        cout<<"fruit\n";
    }
};

class FruitFactory
{
public:
    virtual Fruit* getApple()
    {
        return new Fruit();
    }
    virtual Fruit* getBanana()
    {
        return new Fruit();
    }
};

//南方香蕉
class SouthBanana : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"South Banana \n"<<endl;
    }
};


//南方苹果
class SouthApple : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"South Apple \n"<<endl;
    }
};


//北方香蕉
class NorthBanana : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"North Banana \n"<<endl;
    }
};


//北方苹果
class NorthApple : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"North Apple \n"<<endl;
    }
};

class SourthFruitFactory : public FruitFactory
{
public:
    virtual Fruit* getApple()
    {
        return new SouthApple();
    }
    virtual Fruit* getBanana()
    {
        return new SouthBanana();
    }
};

class NorthFruitFactory : public FruitFactory
{
public:
    virtual Fruit* getApple()
    {
        return new NorthApple();
    }
    virtual Fruit* getBanana()
    {
        return new NorthBanana();
    }
};

void main()
{   
    FruitFactory * ff  = NULL;
    Fruit *fruit = NULL;

    ff = new SourthFruitFactory();
    fruit = ff->getApple();
    fruit->sayname();
    fruit = ff->getBanana();
    fruit->sayname();

    delete fruit;
    delete ff;

    ff = new NorthFruitFactory();
    fruit = ff->getApple();
    fruit->sayname();
    fruit = ff->getBanana();
    fruit->sayname();

    delete fruit;
    delete ff;

    cout<<"hello....\n";
    system("pause");
}



也许
2个月前

设计模式基本原则

开放封闭原则 

    类的改动是通过增加代码进行的,而不是修改源代码。

依赖倒置原则

    依赖于抽象(接口),不要依赖具体的实现(类),也就是针对接口编程。

单例模式

目的:保证为一个类只生成唯一的实例对象

应用场景:多个线程共享同一个资源或者操作同一个对象

实现单例模式步骤:

    构造函数私有化

    提供一个全局的静态方法(全局访问点) //使用静态方法不需要对类进行实例化

    在类中定义一个静态指针,指向本类的变量的静态变量指针   // 初始化不需要依赖具体对象 

懒汉式单例模式

在全局的静态方法中创建对象

class  Singelton
{
private:
    Singelton()
    {
        m_singer = NULL;
        m_count = 0;
        cout << "构造函数Singelton ... do" << endl;
    }

public:
    static Singelton *getInstance()
    {
        if (m_singer == NULL )  //懒汉式:1 每次获取实例都要判断 2 多线程会有问题
        {
            m_singer = new Singelton;
        }
        return m_singer;
    }
    static void printT()
    {
        cout << "m_count: " << m_count << endl;
    }

private:
    static Singelton *m_singer;
    static int m_count;
};

Singelton *Singelton::m_singer = NULL;  //懒汉式 并没有创建单例对象
int Singelton::m_count = 0;


void main01_1()
{
    cout << "演示 懒汉式" << endl;
    Singelton *p1 = Singelton::getInstance(); //只有在使用的时候,才去创建对象。
    Singelton *p2 = Singelton::getInstance();
    if (p1 != p2)
    {
        cout << "不是同一个对象" << endl;
    }
    else
    {
        cout << "是同一个对象" << endl;
    }
    p1->printT();
    p2->printT();

    system("pause");
    return ;
}

饿汉式单例模式

不在静态方法中创建对象,而是在全局区直接创建对象

class  Singelton2
{
private:
    Singelton2()
    {
        m_singer = NULL;
        m_count = 0;
        cout << "构造函数Singelton ... do" << endl;
    }

public:
    static Singelton2 *getInstance()
    {
//      if (m_singer == NULL )
//      {
//          m_singer = new Singelton2;
//      }
        return m_singer;
    }
    static void Singelton2::FreeInstance()
    {
        if (m_singer != NULL)
        {
            delete m_singer;
            m_singer = NULL;
            m_count = 0;
        }
    }
    static void printT()
    {
        cout << "m_count: " << m_count << endl;
    }

private:
    static Singelton2 *m_singer;
    static int m_count;
};

Singelton2 *Singelton2::m_singer = new Singelton2; //不管你创建不创建实例,均把实例new出来
int Singelton2::m_count = 0;

void main()
{
    cout << "演示 饿汉式" << endl;

    Singelton2 *p1 = Singelton2::getInstance(); //只有在使用的时候,才去创建对象。
    Singelton2 *p2 = Singelton2::getInstance();
    if (p1 != p2)
    {
        cout << "不是同一个对象" << endl;
    }
    else
    {
        cout << "是同一个对象" << endl;
    }
    p1->printT();
    p2->printT();
    Singelton2::FreeInstance();
    Singelton2::FreeInstance();


    system("pause");
}



也许
3个月前

中断线程化处理引入

复杂、耗时的事情,尽量使用内核线程来处理。上节视频介绍的工作队列用起来挺简单,但是它有一个缺点:工作队列中有

多个work,前一个work没处理完会影响后面的work。解决方法有很多种,比如干脆自己创建一个内核线程,不跟别的work凑

在一块了。

对于中断处理,还有另一种方法:threadedirq,线程化的中断处理。中断的处理仍然可以认为分为上半部、下半部。上半

部用来处理紧急的事情,下半部用一个内核线程来处理,这个内核线程专用于这个中断。

函数

request_threaded_irq(unsigned int irq, irq_handler_t handler,  irq_handler_t thread_fn,unsigned long flags, 

const char *name, void *dev);

作用:将中断线程化,中断将作为内核线程运行

参数:

irq             中断号,所申请的中断向量

handler         中断处理函数,在驱动中一般这个参数是NULL,为NULL时使用默认的处理,这个相当于中断的上半段

thread_fn       中断发生时,如果handler为NULL,就直接将thread_fn扔到内核线程中去执行

flags           指定中断属性、中断触发方式(一般用宏定义表示)等,定义在linux/interrupt.h中

name            指定中断名字,用命令cat /proc/interrupts可查看系统中断申请与使用情况

dev             传入中断处理程序的参数,可以为NULL,但在注册共享中断时,此参数不能为NULL。该参数可作为共享中
                断时的中断区别参数,还可以把其传给一个结构体变量,用于保存一个设备的信息,使中断处理函数可以获得该设备的信息



编程

probe 函数中将request_irq() 换成 request_threaded_irq()



也许
3个月前

工作队列引入

前面讲的定时器、下半部tasklet,它们都是在中断上下文中执行,它们无法休眠(不能被调度)。当要处理更复杂的事情

时,往往更耗时这些更耗时的工作放在定时器或是下半部中,会使得系统很卡;并且循环等待某件事情完成也太浪费CPU资

源了。

如果使用线程来处理这些耗时的工作,那就可以解决系统卡顿的问题:因为线程可以休眠。

在内核中,我们并不需要自己去创建线程,可以使用“工作队列”(workqueue)。内核初始化工作队列就为它创建了内核线程

常用函数

初始化工作队列:#define INIT_WORK(_work, _func) 

调用schedule_work

    就会把work_struct结构体放入队列中,并唤醒对应的内核线程。内核线程就会从队列里把work_struct结构体取出来,

    执行里面的函数。

编程


    probe 函数中初始化work_queue

    中断服务程序中调度work_queue(创建内核线程)

    任务处理函数中打印key值




也许
3个月前

内核函数

定义tasklet:

    中断下半部使用结构体tasklet_struct来表示

    struct tasklet_struct
    {
        struct tasklet_struct *next;   //下一个节点的地址
        unsigned long state;      // bit0 == TASKLET_STATE_SCHED = 1 表示放入链表中了
                                  // bit1 == TASKLET_STATE_RUN = 1 表示重在运行
        atomic_t count;           // 0 :使能 1:禁止
        void (*func)(unsigned long);  // 中断下半部函数
        unsigned long data;          //函数参数
    };

初始化tasklet:

    extern void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);

使能/禁止tasklet

    static inline void tasklet_enable(struct tasklet_struct *t);

    static inline void tasklet_disable(struct tasklet_struct *t);

调度tasklet:发出软中断

    static inline void tasklet_schedule(struct tasklet_struct *t);    

kill tasklet

    extern void tasklet_kill(struct tasklet_struct *t);

tasklet使用方法

先定义tasklet,需要使用时调用tasklet_schedule,驱动卸载前调用tasklet_kill。

tasklet_schedule只是把tasklet放入内核队列,它的func函数会在软件中断的执行过程中被调用。

调用过程:

    asklet属于TASKLET_SOFTIRQ软件中断,入口函数为tasklet_action

    当发生硬件中断时,内核处理完硬件中断后,会处理软件中断。对于TASKLET_SOFTIRQ软件中断,会调用tasklet_actio

    n函数。

总结    

    执行过程还是挺简单的:从队列中找到tasklet,进行状态判断后执行func函数,从队列中删除tasklet

    tasklet_schedule调度tasklet时,其中的函数并不会立刻执行,而只是把tasklet放入队列;

    调用一次tasklet_schedule,只会导致tasklnet的函数被执行一次;

    如果tasklet的函数尚未执行,多次调用tasklet_schedule也是无效的,只会放入队列一次。


编程

将中断服务程序中打印key值操作放到中断下半部中

    probe 函数中初始化tasklet

    中断服务程序中调度tasklet(触发软中断)

    任务处理函数中打印key值