0
点赞
收藏
分享

微信扫一扫

Linux杂项函数

zhyuzh3d 2022-03-12 阅读 64

内核、用户数据拷贝

常见的有四个函数:

copy_from_user、copy_to_user、get_user(或__get_user)、put_user(或__put_user)

copy_from_user:

    原型:static inline long copy_from_user(void *to, const void __user * from, unsigned long n)

    第一个参数to:       内核空间的数据目标地址指针

    第二个参数from:   用户空间的数据源地址指针     (void __user *也可)

    第三个参数n:         数据的长度,以字节为单位。

    如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数。

    例:

    static struct file_operations ker_rw_ops = {

        .owner            = THIS_MODULE,

        .unlocked_ioctl   = ker_rw_ioctl,

    };

    static long ker_rw_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

    {

            ...

            unsigned int buf[2];

            copy_from_user(buf, (const void __user *)arg, 8)

            ...

    }

copy_to_user:

    原型:static inline long copy_to_user(void __user *to,  const void *from, unsigned long n)

    第一个参数to:       用户空间的数据目标地址指针

    第二个参数from:   内核空间的数据源地址指针      (void *也可)

    第三个参数n:         数据的长度,以字节为单位。

    如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数。

    例:

    1.应用程序:

           #define KEY_VAL 100

           unsigned char key_vals[4];

           fd = open("/dev/buttons", O_RDWR);

           ioctl(fd, KEY_VAL, key_vals);   //第二个参数可为0或任意int型,可在驱动程序中对此值用switch判断

       驱动程序:

            static int second_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

            {

                    unsigned char key_vals[4];

                    copy_to_user((void __user *)(arg), key_vals, 4);

               }

        2.应用程序:

           unsigned int buf[2];

           #define KER_RW_R8      100

           ioctl(fd, KER_RW_R8, buf);

           驱动程序:

           static long ker_rw_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

           {

               ...

               unsigned int val;

               copy_to_user((void __user *)(arg+4), &val, 4);

                   ...

           }

          详细见:第30课第3节_驱动调试之自制工具_寄存器编辑器  (26th_debug_regeditor)

get_user(或__get_user):

    原型:#define get_user(x, p)  __get_user(x,p)

    第一个参数 x:  内核变量

    第二个参数 p:  用户空间的地址

    例:static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

         {

             void __user *argp = (void __user *)arg;

             int __user *p = argp;

             int x=3;

             get_user(x, p);

         }

put_user(或__put_user):

    原型:#define put_user(x, p)  __put_user(x,p)

    第一个参数 x:  内核变量

    第二个参数 p:  用户空间的地址

    例:static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

         {

             void __user *argp = (void __user *)arg;

             int __user *p = argp;

             int x;

             put_user(x, p);

         }

驱动获得进程的信息

current是 一个指向当前进程的task_struct的指针。

pid:      current->pid

进程名: current->common

CPU号: smp_processor_id()

是否在中断中: in_irq()、in_interrupe()

在内核中, 进程用task_struct结构表示, 其中有char comm[TASK_COMM_LEN]成员, 其含义是:进程的名字(不包含路径)

按照标准做法, 应该使用get_task_comm()/set_task_comm()函数来获取/设置此成员(为避免竞争, 这俩函数会调用task_lock()先拿锁).

通过file结构体获得文件名

获取文件名:struct file *filp with filp->f_path.dentry->d_iname.

获取全路径:dentry_path_raw(filp->f_path.dentry,buf,buflen)

DEVICE_ATTR

        使用DEVICE_ATTR,可以在sysfs中添加“文件”,通过修改该文件内容,可在运行过程中动态控制device。

        类似的还有DRIVER_ATTR,BUS_ATTR,CLASS_ATTR。DEVICE_ATTR对应的文件在/sys/devices/目录中对应的device下面(有可能在子目录之下),而其他几个分别在driver,bus,class中对应的目录下。

        这次主要介绍DEVICE_ATTR,其他几个类似。

在​documentation/driver-model/Device.txt​中有对DEVICE_ATTR的详细介绍。

DEVICE_ATTR的原型:

DEVICE_ATTR(_name, _mode, _show, _store)

_name:名称,也就是将在sys fs中生成的文件名称。

_mode:上述文件的访问权限,与普通文件相同,UGO的格式。

_show:​显示函数,cat该文件时,此函数被调用​。

_store:​写函数,echo内容到该文件时,此函数被调用​。

看看我们怎么填充这些要素:

名称可以随便起一个,便于记忆,并能体现其功能即可。

模式可以为只读0444,只写0222,或者读写都行的0666。当然也可以对User\Group\Other进行区别。

显示和写入函数就需要实现了。

实例:

第二期视频:“第32课第1.2节_3.4.2内核下的I2C驱动之框架编写代码”

insmod at24cxx_drv.ko; //只加载drv

echo at24c08 0x50 > /sys/devices/platform/s3c2440-i2c/i2c-0/new_device;

发现运行了probe

echo 0x50 > /sys/devices/platform/s3c2440-i2c/i2c-0/delete_device;

发现运行了remove函数

搜索“new_device”

i2c_core.c: static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);

i2c_sysfs_new_device

    i2c_new_device(adap, &info)

EXPORT_SYMBOL()

见:​​Linux驱动编程中EXPORT_SYMBOL()介绍​​

container_of

作用

通过一个结构变量中一个成员的地址找到这个结构体变量的首地址

示例

1.定义

linux3.4.2/include/linux/kernel.h:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#define container_of(ptr, type, member) ({ \

const typeof( ((type *)0)->member ) *__mptr = (ptr); \

(type *)( (char *)__mptr - offsetof(type,member) );})

linux-4.19-rc/include/linux/kernel.h:

#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#define container_of(ptr, type, member) ({ \

void *__mptr = (void *)(ptr); \

BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \

!__same_type(*(ptr), void), \

"pointer type mismatch in container_of()"); \

((type *)(__mptr - offsetof(type, member))); })

__same_type的含义:如果type_a与 type_b相同的话,就会返回1,否则的话,返回0。

__builtin_types_compatible_p是GCC的内建函数。

2. 使用

linux-3.4.2/include/linux/i2c.h 

#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)

struct i2c_adapter {

struct device dev;

    ...

}

i2c-dev.c

static int i2cdev_attach_adapter(struct device *dev, void *dummy)

{

                ruct i2c_adapter *adap;

                ap = to_i2c_adapter(dev);

                .

}

3.函数调用顺序

to_i2c_adapter(d);

        container_of(d, struct i2c_adapter, dev)

                const typeof( ((struct i2c_adapter *)0)->dev ) *__mptr = (d); \

                        (struct i2c_adapter *)( (char *)__mptr - offsetof(struct i2c_adapter,dev) );

                        const typeof( ((struct i2c_adapter *)0)->dev ) *__mptr = (d); \

                        (struct i2c_adapter *)( (char *)__mptr - ((size_t) &((struct i2c_adapter *)0)->dev) );

高级用法

container_of支持多层​,例如:

struct stru1 t1 = {

  int a;

  char c;

};

struct stru2 t2 = {

  int b;

  struct stru1 t;

};

假设目前已知t1的c成员地址p,可以获得t2的地址,方法为:

struct stru2 *pt2;

pt2 = container_of(p, struct stru2, t.c);


相关知识

1. typeof:获得变量的类型

typeof的参数可以是两种形式:表达式或类型。

例1:struct device dev;

        typeof dev 等价于 struct device

例2:typeof(int *) a,b; 

        等价于 int *a, *b;

例3:int func1(void);

        typeof(func1(void)) var 等价于 int var

typeof构造中的类型名不能包含存储类说明符,如extern或static;允许包含类型限定符,如const或volatile.

例1:typeof(extern int) a; //这是无效的。因为它在typeof构造中声明了extern;

        extern typeof(int)a;    //这是有效的。

例2:typeof(char*const) p; //这是有效的。常指针,不能修改指针指向;

2.为什么container_of(linux-3.4.2版本)要定义同一地址的变量呢?

const typeof( ((struct i2c_adapter *)0)->dev ) *__mptr = (d);

答:如果开发者使用时输入的参数有问题:ptr与member类型不匹配,编译时便会有warnning,

但是如果去掉该行,那个就没有了,而这个警告恰恰是必须的(防止出错有不知道错误在哪里)。

3.为什么将0设为地址不会出错(即offsetof的宏定义)?

按理,0地址也就是空指针,空指针指向成员变量一定会出错,但是,此处不一样,解答如下。

本处,用了&符号(取地址),导致没有访问内存位置,只是取了地址,这些地址一般存储在

机器寄存器或是通常的本地堆栈。

例如:
例程1

int main()

{

        t *a = NULL;

        intf("a = %u\n",&(*a));

}

运行结果:

a = 0

例程2:

int main()

{

             *a = NULL;

            ntf("*a = %u\n",*a);

}

运行结果:

Segmentation fault (core dumped)

4.linux-4.19-rc与linux-3.4.2的函数差别

(1)用__same_type宏来做类型检测,显得更加的直观明了,错了还可以有提示

(2)用void *取代了char *来做减法

ARRAY_SIZE

    获得结构体数组的成员个数

    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

test_and_set_bit

module相关

insmod给驱动传参数

简介

一般用户态传递参数是通过main函数,第一个参数表示args个数,即argc,第二个参数表示具体的参数。

在kernel态,无法通过这样的方式传递参数,一般使用module_param的方式,步骤如下:

1.使用module_param指定模块的参数

2.加载driver时给模块传递参数

代码

static int cnt = 61;

static char *str = "China" ;

static int word_count_init(void)

{

    printk(KERN_ALERT "word_count_init_sucess \n");

    printk(KERN_ALERT "cnt=%d , str=%s \n",cnt,str);

    return 0;

}

static void word_count_exit(void)

{

    printk(KERN_ALERT "word_count_init_exit_sucess \n");

}

//注册初始化linux驱动的函数

module_init(word_count_init);

//注册退出linux驱动的函数

module_exit(word_count_exit);

module_param(cnt,int,0644);

module_param(str,charp,0644);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Mstar jacky.zhu");

试验

1.无参数执行结果

# insmod hello_world.ko 

[ 122.774769] word_count_init_sucess 

[ 122.778314] cnt=61 , str=China 

# rmmod hello_world.ko 

[ 129.133626] word_count_init_exit_sucess 

以上是不带参数,正常的挂载和卸载

2.有参数执行结果

#insmod hello_world.ko cnt=50 str='qingdao' 

[ 293.104785] word_count_init_sucess 

[ 293.108306] cnt=50 , str=qingdao 

在控制台可以将参数传递给hello_world.ko

3.sys路径结果

# ls -al /sys/module/hello_world/parameters/ 

-rw-r--r-- root root 4096 2007-01-01 20:07 str

-rw-r--r-- root root 4096 2007-01-01 20:07 cnt

在串口可以查看参数的值

# cat cnt 

50

#cd /sys/module/hello_world/parameters

#cat str

qingdao

4.module_param三个参数的解释

module_param(cnt,int,0644);

第一个参数是参数的name,自己定义

第二个参数是变量的类型,比如int,long,char,float等

第三个参数是权限,类似于文件的权限。这里应该是指哪些user可以修改这个参数的意思。

比如上面的数据:

# ls -al /sys/module/hello_world/parameters/ 

-rw-r--r-- root root 4096 2007-01-01 20:07 str

-rw-r--r-- root root 4096 2007-01-01 20:07 cnt

权限就是644,是在这里指定的。

module_platform_driver()

在include/linux/platform_device.h中定义:

#define module_platform_driver(​__platform_driver​) \

module_driver(​__platform_driver​, platform_driver_register, \

platform_driver_unregister)

#define module_driver(​__driver​, __register, __unregister, ...) \

static int __init ​__driver​##_init(void) \

{ \

        return __register(&(​__driver​) , ##__VA_ARGS__); \

} \

module_init(__driver##_init); \

static void __exit __driver##_exit(void) \

{ \

        __unregister(&(​__driver​) , ##__VA_ARGS__); \

} \

module_exit(​__driver​##_exit);

用法:

例:定义名为samsung_i2c_drv 的结构体的初始化、退出函数:

static const struct of_device_id samsung_i2c_match[] = {

    {.compatible = "samsung,i2c", },

    {}

};

static struct platform_driver samsung_i2c_drv = {

        .probe  = ... ,

        .remove = ... ,

        .driver = {

                .name = ... ,

                .owner = THIS_MODULE ,

                .of_match_table = of_match_ptr(samsung_i2c_match),

        },

};

module_platform_driver(samsung_i2c_drv );

try_module_get


举报

相关推荐

0 条评论