Linux 设备驱动开发 —— platform设备驱动应用实例解析

前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动 ,下面将通过一个实例来深入我们的学习。

       

一、platform 驱动的工作过程

        platform模型驱动编程,需要实现platform_device(设备)platform_driver(驱动)platform(虚拟总线)上的注册、匹配,相互绑定,然后再做为一个普通的字符设备进行相应的应用,总之如果编写的是基于字符设备的platform驱动,在遵循并实现platform总线上驱动与设备的特定接口的情况下,最核心的还是字符设备的核心结构:cdev、 file_operations(他包含的操作函数接口)、dev_t(设备号)、设备文件(/dev)等,因为用platform机制编写的字符驱动,它的本质是字符驱动。

      我们要记住,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳

     在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中。那么我们编写platform模型驱动时,需要完成两个工作:

a -- 实现platform驱动 

b -- 实现platform设备

     然而在实现这两个工作的过程中还需要实现其他的很多小工作,在后面介绍。platform模型驱动的实现过程核心架构就很简单,如下所示:


platform驱动模型三个对象:platform总线platform设备platform驱动

platform总线对应的内核结构:struct bus_type-->它包含的最关键的函数:match() (要注意的是,这块由内核完成,我们不参与)

platform设备对应的内核结构:struct platform_device-->注册:platform_device_register(unregister)

platform驱动对应的内核结构:struct platform_driver-->注册:platform_driver_register(unregister)

       

那具体platform驱动的工作过程是什么呢:

     设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;

     如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;

    如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用



二、实现platform 驱动与设备的详细过程

1、思考问题?

      在分析platform 之前,可以先思考一下下面的问题:

a -- 为什么要用 platform 驱动?不用platform驱动可以吗?

b -- 设备驱动中引入platform 概念有什么好处?

        现在先不回答,看完下面的分析就明白了,后面会附上总结。


2、platform_device 结构体 VS platform_driver 结构体

      这两个结构体分别描述了设备和驱动,二者有什么关系呢?先看一下具体结构体对比

设备(硬件部分):中断号,寄存器,DMA等
platform_device 结构体
 驱动(软件部分)
platform_driver 结构体       
struct platform_device {
 const char    *name;       名字
int        id;
bool        id_auto;
struct device    dev;   硬件模块必须包含该结构体
u32        num_resources;        资源个数
struct resource    *resource;         资源  人脉
const struct platform_device_id    *id_entry;
/* arch specific additions */
struct pdev_archdata    archdata;
};
struct platform_driver {
int (*probe)(struct platform_device *);
    硬件和软件匹配成功之后调用该函数
int (*remove)(struct platform_device *);
    硬件卸载了调用该函数
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;内核里所有的驱动程序必须包含该结构体
const struct platform_device_id *id_table;  八字
};
设备实例:
static struct platform_device hello_device=
{
.name = "bigbang",
.id = -1,
.dev.release = hello_release,
};
驱动实例:
static struct platform_driver hello_driver=
{
.driver.name = "bigbang",
.probe = hello_probe,
.remove = hello_remove,
};

       前面提到,实现platform模型的过程就是总线对设备和驱动的匹配过程 。打个比方,就好比相亲,总线是红娘,设备是男方,驱动是女方

a -- 红娘(总线)负责男方(设备)和女方(驱动)的撮合;     

b -- 男方(女方)找到红娘,说我来登记一下,看有没有合适的姑娘(汉子)—— 设备或驱动的注册

c -- 红娘这时候就需要看看有没有八字(二者的name 字段)匹配的姑娘(汉子)——match 函数进行匹配,看name是否相同;

d -- 如果八字不合,就告诉男方(女方)没有合适的对象,先等着,别急着乱做事 —— 设备和驱动会等待,直到匹配成功;

e -- 终于遇到八字匹配的了,那就结婚呗!接完婚,男方就向女方交代,我有多少存款,我的房子在哪,钱放在哪等等(struct resource    *resource),女方说好啊,于是去房子里拿钱,去给男方买菜啦,给自己买衣服、化妆品、首饰啊等等(int (*probe)(struct platform_device *) 匹配成功后驱动执行的第一个函数),当然如果男的跟小三跑了(设备卸载),女方也不会继续待下去的(  int (*remove)(struct platform_device *))。


3、设备资源结构体

      在struct platform_device 结构体中有一重要成员 struct resource *resource

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct resource {  
  2.     resource_size_t start;  资源起始地址     
  3.     resource_size_t end;   资源结束地址  
  4.     const char *name;        
  5.     unsigned long flags;   区分是资源什么类型的  
  6.     struct resource *parent, *sibling, *child;  
  7. };  
  8.   
  9. #define IORESOURCE_MEM        0x00000200  
  10. #define IORESOURCE_IRQ        0x00000400     

       flags 指资源类型,我们常用的是 IORESOURCE_MEM、IORESOURCE_IRQ  这两种。start 和 end 的含义会随着 flags而变更,如

a -- flags为IORESOURCE_MEM 时,start 、end 分别表示该platform_device占据的内存的开始地址和结束值;  

b -- flags为 IORESOURCE_IRQ   时,start 、end 分别表示该platform_device使用的中断号的开始地址和结束值; 

下面看一个实例:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static struct  resource beep_resource[] =  
  2. {  
  3.     [0] = {  
  4.             .start = 0x114000a0,  
  5.         .end = 0x114000a0+0x4,  
  6.             .flags = IORESOURCE_MEM,  
  7.     },  
  8.   
  9.     [1] = {  
  10.             .start = 0x139D0000,  
  11.             .end = 0x139D0000+0x14,  
  12.             .flags = IORESOURCE_MEM,  
  13.     },  
  14. };  

4、将字符设备添加到 platform的driver中
前面我们提到platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳,下面我们看一下添加的过程:
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static struct file_operations hello_ops=  
  2. {  
  3.     .open = hello_open,  
  4.     .release = hello_release,  
  5.     .unlocked_ioctl = hello_ioctl,  
  6. };  
  7.   
  8. static int hello_remove(struct platform_device *pdev)  
  9. {  
  10.     注销分配的各种资源  
  11. }  
  12.   
  13. static int hello_probe(struct platform_device *pdev)  
  14. {  
  15.     1.申请设备号  
  16.     2.cdev初始化注册,&hello_ops  
  17.     3.从pdev读出硬件资源  
  18.     4.对硬件资源初始化,ioremap,request_irq( )  
  19. }  
  20.   
  21. static int hello_init(void)  
  22. {  
  23.     只注册 platform_driver  
  24. }  
  25.   
  26. static void hello_exit(void)  
  27. {  
  28.     只注销 platform_driver  
  29. }  
可以看到,模块加载和卸载函数仅仅通过paltform_driver_register()、paltform_driver_unregister() 函数进行 platform_driver 的注册和注销,而原先注册和注销字符设备的工作已经被移交到 platform_driver 的 probe() 和 remove() 成员函数中

5、platform是如何匹配device和driver
这时就该总线出场了,系统为platform总线定义了一个bus_type 的实例platform_bus_type,其定义如下:
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct bus_type platform_bus_type = {  
  2.     .name        = "platform",  
  3.     .dev_groups    = platform_dev_groups,  
  4.     .match        = platform_match,  
  5.     .uevent        = platform_uevent,  
  6.     .pm        = &platform_dev_pm_ops,  
  7. };  
其又是怎样工作的呢?在platform.c (e:\linux-3.14-fs4412\drivers\base)    31577    2014/3/31 中可以看到
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. __platform_driver_register()  
  2. {  
  3.     drv->driver.bus = &platform_bus_type;     536行  
  4. }  
在 platform_bus_type 中调用 了platform_match:
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static int platform_match(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(dev);  
  4.     struct platform_driver *pdrv = to_platform_driver(drv);  
  5.   
  6.     匹配设备树信息,如果有设备树,就调用 of_driver_match_device() 函数进行匹配  
  7.     if (of_driver_match_device(dev, drv))  
  8.         return 1;  
  9.   
  10.   
  11.     匹配id_table  
  12.     if (pdrv->id_table)  
  13.         return platform_match_id(pdrv->id_table, pdev) != NULL;  
  14.   
  15.     最基本匹配规则  
  16.     return (strcmp(pdev->name, drv->name) == 0);  
  17. }  


6、解决问题

现在可以回答这两个问题了

a -- 为什么要用 platform 驱动?不用platform驱动可以吗?

b -- 设备驱动中引入platform 概念有什么好处?

      引入platform模型符合Linux 设备模型 —— 总线、设备、驱动,设备模型中配套的sysfs节点都可以用,方便我们的开发;当然你也可以选择不用,不过就失去了一些platform带来的便利;

      设备驱动中引入platform 概念,隔离BSP和驱动。在BSP中定义platform设备和设备使用的资源、设备的具体匹配信息,而在驱动中,只需要通过API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。


三、实例

        这是一个蜂鸣器的驱动,其实前面已经有解析 Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动, 下面来看一下,套上platform 外壳后的程序:

1、device.c

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/device.h>  
  3. #include <linux/platform_device.h>  
  4. #include <linux/ioport.h>  
  5.   
  6. static struct resource beep_resource[] =  
  7. {  
  8.     [0] ={  
  9.         .start = 0x114000a0,  
  10.         .end =  0x114000a0 + 0x4,  
  11.         .flags = IORESOURCE_MEM,  
  12.     },  
  13.   
  14.     [1] ={  
  15.         .start = 0x139D0000,  
  16.         .end =  0x139D0000 + 0x14,  
  17.         .flags = IORESOURCE_MEM,  
  18.     }  
  19. };  
  20.   
  21. static void hello_release(struct device *dev)  
  22. {  
  23.     printk("hello_release\n");  
  24.     return ;  
  25. }  
  26.   
  27.   
  28.   
  29. static struct platform_device hello_device=  
  30. {  
  31.     .name = "bigbang",  
  32.     .id = -1,  
  33.     .dev.release = hello_release,  
  34.     .num_resources = ARRAY_SIZE(beep_resource),  
  35.     .resource = beep_resource,  
  36. };  
  37.   
  38. static int hello_init(void)  
  39. {  
  40.     printk("hello_init");  
  41.     return platform_device_register(&hello_device);  
  42. }  
  43.   
  44. static void hello_exit(void)  
  45. {  
  46.     printk("hello_exit");  
  47.     platform_device_unregister(&hello_device);  
  48.     return;  
  49. }  
  50.   
  51. MODULE_LICENSE("GPL");  
  52. module_init(hello_init);  
  53. module_exit(hello_exit);  

2、driver.c

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/cdev.h>  
  4. #include <linux/device.h>  
  5. #include <linux/platform_device.h>  
  6. #include <asm/io.h>  
  7.   
  8. static int major = 250;  
  9. static int minor=0;  
  10. static dev_t devno;  
  11. static struct class *cls;  
  12. static struct device *test_device;  
  13.            
  14. #define TCFG0         0x0000                 
  15. #define TCFG1         0x0004                              
  16. #define TCON          0x0008               
  17. #define TCNTB0        0x000C            
  18. #define TCMPB0        0x0010             
  19.   
  20. static unsigned int *gpd0con;  
  21. static void *timer_base;  
  22.   
  23. #define  MAGIC_NUMBER    'k'  
  24. #define  BEEP_ON    _IO(MAGIC_NUMBER    ,0)  
  25. #define  BEEP_OFF   _IO(MAGIC_NUMBER    ,1)  
  26. #define  BEEP_FREQ   _IO(MAGIC_NUMBER   ,2)  
  27.   
  28. static void fs4412_beep_init(void)  
  29. {     
  30.     writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);  
  31.     writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);   
  32.     writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );   
  33.   
  34.     writel (500, timer_base +TCNTB0  );  
  35.     writel (250, timer_base +TCMPB0 );  
  36.     writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );   
  37. }  
  38.   
  39. void fs4412_beep_on(void)  
  40. {  
  41.     writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );  
  42. }  
  43.   
  44. void fs4412_beep_off(void)  
  45. {  
  46.     writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );  
  47. }  
  48.   
  49. static void beep_unmap(void)  
  50. {  
  51.         iounmap(gpd0con);  
  52.         iounmap(timer_base);  
  53. }  
  54.   
  55. static int beep_open (struct inode *inode, struct file *filep)  
  56. {  
  57.     fs4412_beep_on();  
  58.     return 0;  
  59. }  
  60.   
  61. static int beep_release(struct inode *inode, struct file *filep)  
  62. {  
  63.      fs4412_beep_off();  
  64.      return 0;  
  65. }  
  66.   
  67. #define BEPP_IN_FREQ 100000  
  68. static void beep_freq(unsigned long arg)  
  69. {  
  70.     writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0  );  
  71.     writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 );  
  72.   
  73. }  
  74.   
  75. static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)  
  76. {  
  77.     switch(cmd)  
  78.     {  
  79.         case BEEP_ON:  
  80.             fs4412_beep_on();  
  81.             break;  
  82.         case BEEP_OFF:  
  83.             fs4412_beep_off();  
  84.             break;  
  85.         case BEEP_FREQ:  
  86.             beep_freq( arg );  
  87.             break;  
  88.         default :  
  89.             return -EINVAL;  
  90.     }  
  91.     return 0;  
  92. }  
  93.   
  94. static struct file_operations beep_ops=  
  95. {  
  96.     .open     = beep_open,  
  97.     .release = beep_release,  
  98.     .unlocked_ioctl      = beep_ioctl,  
  99. };  
  100.   
  101. static int beep_probe(struct platform_device *pdev)  
  102. {  
  103.     int ret;      
  104.     printk("match ok!");  
  105.       
  106.     gpd0con = ioremap(pdev->resource[0].start,pdev->resource[0].end - pdev->resource[0].start);  
  107.     timer_base = ioremap(pdev->resource[1].start, pdev->resource[1].end - pdev->resource[1].start);  
  108.   
  109.     devno = MKDEV(major,minor);  
  110.     ret = register_chrdev(major,"beep",&beep_ops);  
  111.   
  112.     cls = class_create(THIS_MODULE, "myclass");  
  113.     if(IS_ERR(cls))  
  114.     {  
  115.         unregister_chrdev(major,"beep");  
  116.         return -EBUSY;  
  117.     }  
  118.   
  119.     test_device = device_create(cls,NULL,devno,NULL,"beep");//mknod /dev/hello  
  120.     if(IS_ERR(test_device))  
  121.     {  
  122.         class_destroy(cls);  
  123.         unregister_chrdev(major,"beep");  
  124.         return -EBUSY;  
  125.     }  
  126.       
  127.     fs4412_beep_init();  
  128.       
  129.     return 0;  
  130. }  
  131.   
  132. static int beep_remove(struct platform_device *pdev)  
  133. {  
  134.     beep_unmap();  
  135.     device_destroy(cls,devno);  
  136.     class_destroy(cls);   
  137.     unregister_chrdev(major,"beep");  
  138.   
  139.     return 0;  
  140. }  
  141.   
  142.   
  143. static struct platform_driver beep_driver=  
  144. {  
  145.     .driver.name = "bigbang",  
  146.     .probe = beep_probe,  
  147.     .remove = beep_remove,  
  148. };  
  149.    
  150.   
  151. static int beep_init(void)  
  152. {  
  153.     printk("beep_init");  
  154.       
  155.     return platform_driver_register(&beep_driver);  
  156. }  
  157.   
  158. static void beep_exit(void)  
  159. {  
  160.     printk("beep_exit");  
  161.     platform_driver_unregister(&beep_driver);  
  162.       
  163.     return;  
  164. }  
  165.   
  166.   
  167. MODULE_LICENSE("GPL");  
  168. module_init(beep_init);  
  169. module_exit(beep_exit);  

3、makefile   

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. ifneq  ($(KERNELRELEASE),)  
  2. obj-m:=device.o driver.o  
  3. $(info "2nd")  
  4. else  
  5. #KDIR := /lib/modules/$(shell uname -r)/build  
  6. KDIR := /home/fs/linux/linux-3.14-fs4412  
  7. PWD:=$(shell pwd)  
  8. all:  
  9.     $(info "1st")  
  10.     make -C $(KDIR) M=$(PWD) modules  
  11. clean:  
  12.     rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  
  13. endif  

4、test.c
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5.   
  6. main()  
  7. {  
  8.     int fd,i,lednum;  
  9.   
  10.     fd = open("/dev/beep",O_RDWR);  
  11.     if(fd<0)  
  12.     {  
  13.         perror("open fail \n");  
  14.         return ;  
  15.     }  
  16.       
  17.     sleep(10);  
  18.     close(fd);  
  19. }  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402201.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python爬虫入门四urllib库的高级用法

1.设置headers 有些网站不会同意程序直接用上面的方式进行访问&#xff0c;如果识别有问题&#xff0c;那么站点根本不会响应&#xff0c;所以为了完全模拟浏览器的工作&#xff0c;我们需要设置一些 Headers 的属性。 首先&#xff0c;打开我们的浏览器&#xff0c;调试浏览器…

进程上下文、中断上下文及原子上下文

谈论进程上下文 、中断上下文 、 原子上下文之前&#xff0c;有必要讨论下两个概念&#xff1a; a -- 上下文 上下文是从英文context翻译过来&#xff0c;指的是一种环境。相对于进程而言&#xff0c;就是进程执行时的环境&#xff1b; 具体来说就是各个变量和数据&#xff0c;…

Linux 文件系统与设备文件系统 (二)—— sysfs 文件系统与Linux设备模型

提到 sysfs 文件系统 &#xff0c;必须先需要了解的是Linux设备模型&#xff0c;什么是Linux设备模型呢&#xff1f; 一、Linux 设备模型 1、设备模型概述 从2.6版本开始&#xff0c;Linux开发团队便为内核建立起一个统一的设备模型。在以前的内核中没有独立的数据结构用来让内…

Python爬虫入门七正则表达式

已经搞定了怎样获取页面的内容&#xff0c;不过还差一步&#xff0c;这么多杂乱的代码夹杂文字我们怎样把它提取出来整理呢&#xff1f;下面就开始介绍一个十分强大的工具&#xff0c;正则表达式 1.了解正则表达式 正则表达式是用来匹配字符串非常强大的工具&#xff0c;在其…

Linux 文件系统与设备文件系统 (一)—— udev 设备文件系统

一、什么是Linux设备文件系统 首先我们不看定义&#xff0c;定义总是太抽象很难理解&#xff0c;我们先看现象。当我们往开发板上移植了一个新的文件系统之后&#xff08;假如各种设备驱动也移植好了&#xff09;&#xff0c;启动开发板&#xff0c;我们用串口工具进入开发板&a…

情人节,教大家使用css画出一朵玫瑰花。

情人节到了&#xff0c;给大家来一朵高端的玫瑰花。 在网上看到的一个canvas实现的玫瑰花&#xff0c;效果很好&#xff0c;但是代码被压缩过&#xff0c;也没有注释&#xff0c;看的云里雾里的。 今天我教大脚用CSS来实现一朵玫瑰花。 先看效果 首先我们画出一个花瓣 1、画出一…

Linux 字符设备驱动开发基础(六)—— VFS 虚拟文件系统解析

一、VFS 虚拟文件系统基础概念 Linux 允许众多不同的文件系统共存&#xff0c;并支持跨文件系统的文件操作&#xff0c;这是因为有虚拟文件系统的存在。虚拟文件系统&#xff0c;即VFS&#xff08;Virtual File System&#xff09;是 Linux 内核中的一个软件抽象层。它通过一些…

vim使用—实现程序的自动补齐(C语言)

使用过Source Insight的人一定对它的自动补全功能印象深刻&#xff0c;在很多的集成开发环境中&#xff0c;也都支持自动补全。vim做为一个出色的编辑器&#xff0c;这样的功能当然少不了。至于如何实现程序自动补全&#xff0c;网上教程很多。这里&#xff0c;我将自己配置过程…

[C#]Attribute特性(3)——AttributeUsage特性和特性标识符

相关文章 [C#]Attribute特性 [C#]Attribute特性(2)——方法的特性及特性参数 AttributeUsage特性 除了可以定制自己的特性来注释常用的C#类型外&#xff0c;您可以用AttributeUsage特性来定义您想怎样使用这些特性。AttributeUsage特性采用如下的调用惯例&#xff1a; 1 [Attri…

Linux 命令 ——less命令

less 工具也是对文件或其它输出进行分页显示的工具&#xff0c;应该说是linux正统查看文件内容的工具&#xff0c;功能极其强大。less 的用法比起 more 更加的有弹性。在 more 的时候&#xff0c;我们并没有办法向前面翻&#xff0c; 只能往后面看&#xff0c;但若使用了 less …

android闹钟实现原理

闹钟的原理可用下面我自己画的一幅图来概括&#xff1a;&#xff08;不对的地方&#xff0c;尽管吐槽&#xff09; 我们来看看新建闹钟到闹钟响铃的步骤&#xff1a; 1、新建一个闹钟&#xff1a; ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22…

将openstack的Token认证信息存储在memcache中

公司线上的openstack环境运行了5个多月的时间&#xff0c;keystone库的token表已经增长到了31GB&#xff0c;这个数据量还是很大的&#xff0c;对于以后的数据库备份很不方便。每次管理openstack的时候&#xff0c;都会产生一个新的token验证&#xff0c;而历史token信息其实都…

Linux 下shell编程

什么是shell?Shell是一个命令解析器&#xff0c;是介于Linux操作系统的内核(kernel)与用户之间的一个绝缘层。shell脚本就是讲各类命令预先放入其中&#xff0c;方便一次性执行的一个程序文件&#xff0c;主要用于方便管理员进行设置或者管理。 序员的角度来看&#xff0c; Sh…

linux 目录/sys 解析

今天学习Linux目录时&#xff0c;遇到/sys这个目录&#xff0c;老师怎么讲的&#xff0c;不太清楚&#xff0c;先对/sys目录知识进行一个整理 首先&#xff0c;对 /sys目录下的各个子目录进行具体说明&#xff1a; /sys下的子目录 内容 /sys/devices 该目录下…

南下事业篇——深圳 深圳(回顾)

2019独角兽企业重金招聘Python工程师标准>>> 二0一二年三月二十三号记录了下面的一篇日志&#xff0c;现在回味一下觉得自己有点惭愧&#xff0c;但不后悔&#xff0c;知道的越多就越了解自己的无知&#xff0c;工作之后渐渐磨灭了许多锐气&#xff0c;变得平滑低调…

php中花括号的使用

一、界定变量名 注&#xff1a;花括号内若左侧出现空格&#xff0c;则会当做普通花括号来解析。 二、界定表达式 1.获取字符串中某个字符 如&#xff1a;$strabcdefg; echo $str{0};//a 效果等同于$str[0]; 2.作为表示下标的方法定义数组 如&#xff1a;$arr []; $arr{10}4;…

游戏开发--开源软件8--cyclone2D(手机引擎+设计工具)

2019独角兽企业重金招聘Python工程师标准>>> Cyclone2D (飓风软件)是集成的手机游戏设计工具以及开源的引擎&#xff0c;工具提供了强大的动画、地图、数值、脚本等设计功能&#xff0c;开源引擎提供了一体化的模块加载与管理&#xff0c;并提供了详细的API文档以及…

python编码

https://www.cnblogs.com/xiao-xue-di/p/11283496.html 《Python中的Unicode编码和UTF-8编码》 《字符串和编码》 《python编码转换(unicode / utf8 / gbk / 内部编码)》 字符编码 最早127个字母被编码到计算机里&#xff0c;也就是大小写英文字母、数字和一些符号&#xff0…

Linux中vi显示中文乱码的问题

linux 下编程&#xff0c;用到的编程工具是VI&#xff0c;编辑编译都方便&#xff0c;但经常出现中文乱码问题&#xff0c;下面可完美解决这个问题 由于在windows下默认是gb编码&#xff0c;而我的vim默认是utf-8&#xff08;gedit默认也是utf-8&#xff09;&#xff0c;所以打…

WIFI vs 无线网

大家好多人都在使用无线设备上网&#xff0c;好多人对一些名词充满了好奇&#xff0c;比如WLAN和WIFI的区别是什么? WIFI无线上网和WLAN无线上网是什么意思? 这篇文章中我们为大家介绍什么是WIFI无线上网?大家可能会有这样的疑问&#xff0c;听说最多的应该是WLAN无线上网&a…