当前位置: 首页 > news >正文

深圳设计网站有哪些最 的wordpress书

深圳设计网站有哪些,最 的wordpress书,如何分析对手网站关键词,网站仿制 个人嵌入式Linux应用开发-驱动大全-第一章同步与互斥① 第一章 同步与互斥①1.1 内联汇编1.1.1 C语言实现加法1.1.2 使用汇编函数实现加法1.1.3 内联汇编语法1.1.4 编写内联汇编实现加法1.1.5 earlyclobber的例子 1.2 同步与互斥的失败例子1.2.1 失败例子11.2.2 失败例子21.2.3 失败… 嵌入式Linux应用开发-驱动大全-第一章同步与互斥① 第一章 同步与互斥①1.1 内联汇编1.1.1 C语言实现加法1.1.2 使用汇编函数实现加法1.1.3 内联汇编语法1.1.4 编写内联汇编实现加法1.1.5 earlyclobber的例子 1.2 同步与互斥的失败例子1.2.1 失败例子11.2.2 失败例子21.2.3 失败例子3 第一章 同步与互斥① 1.1 内联汇编 要深入理解 Linux内核中的同步与互斥的实现需要先了解一下内联汇编在 C函数中使用汇编代码。 现代编译器已经足够优秀大部分的 C代码转成汇编码后效率都很高。但是有些特殊的算法需要我们手工优化这时就需要手写汇编代码或是有时需要调用特殊的汇编指令(比如使用 ldrex/strex实现互斥访问)这都涉及内联汇编。 实际上你完全可以不使用内联汇编单独写一个遵守 ATPCS规则的汇编函数让 C函数去调用它。但是在 C函数中写汇编代码可以不用另外新建一个汇编文件比较方便。 内联汇编的完整语法比较复杂可以参考这 3篇文章 ① GNU C扩展汇编 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html ② ARM GCC 内嵌inline汇编手册 http://blog.chinaunix.net/uid-20543672-id-3194385.html ③ C内联汇编 https://akaedu.github.io/book/ch19s05.html 这 3章文章写得细致而深入也有些难以理解。你跟着我们的视频或文档就可以掌握到足够的知识。 下面举 3个例子说明汇编函数、用 C函数中使用内联汇编的方法。 1.1.1 C语言实现加法 使用 GIT下载后源码在“07_驱动大全\source\01_inline_assembly\01_c_code\main.c” 01 #include stdio.h 02 #include stdlib.h 03 04 int add(int a, int b) 05 { 06 return ab; 07 } 08 09 int main(int argc, char **argv) 10 { 11 int a; 12 int b; 13 14 if (argc ! 3) 15 { 16 printf(Usage: %s val1 val2\n, argv[0]); 17 return -1; 18 } 19 20 a (int)strtol(argv[1], NULL, 0); 21 b (int)strtol(argv[2], NULL, 0); 22 23 printf(%d %d %d\n, a, b, add(a, b)); 24 return 0; 25 } 上面的 add函数代码最简单但是对应的汇编也挺复杂需要入栈、出栈等操作效率不算高。看看test.dis 266 00010404 add: 267 10404: b480 push {r7} 268 10406: b083 sub sp, #12 269 10408: af00 add r7, sp, #0 270 1040a: 6078 str r0, [r7, #4] 271 1040c: 6039 str r1, [r7, #0] 272 1040e: 687a ldr r2, [r7, #4] 273 10410: 683b ldr r3, [r7, #0] 274 10412: 4413 add r3, r2 // 真正实现加法的只有这条指令 275 10414: 4618 mov r0, r3 276 10416: 370c adds r7, #12 277 10418: 46bd mov sp, r7 278 1041a: f85d 7b04 ldr.w r7, [sp], #4 279 1041e: 4770 bx lr 280 1.1.2 使用汇编函数实现加法 使用 GIT下载后源码在“07_驱动大全\source\01_inline_assembly\02_assembly\add.S” 01 .text // 放在代码段 02 .global add // 实现全局函数 add 03 .thumb // 使用 thumb指令, main.c默认使用 thumb指令, 所以这里也使用 thumb指令 04 05 add: 06 add r0, r0, r1 07 bx lr 08 根据 ATPCS规则main函数调用 add(a, b)时会把第一个参数存入 r0寄存器把第二个参数存入 r1寄存器。 在上面第 06行里把 r0、r1累加后结果存入 r0根据 ATPCS规则r0用来保存返回值。 可以看到这个 add函数连栈都没有使用非常高效。 这只是一个很简单的例子我们工作中并不使用汇编来进行“加法优化”在计算量非常大的地方可以考虑单独编写汇编函数实现优化。 1.1.3 内联汇编语法 从上面例子可以看到我们完全可以新建一个汇编文件在 ATPCS规则之下编写代码这样 C函数就可以直接调用汇编函数。 但是需要新建汇编文件有点麻烦。 使用内联汇编可以在 C代码中内嵌汇编代码。 先看看内联汇编的语法。 内联汇编语法 ① asm 也可以写作“asm”表示这是一段内联汇编。 ② asm-qualifiers 有 3个取值volatile、inline、goto。 volatile的意思是易变的、不稳定的用来告诉编译器不要随便优化这段代码否则可能出问题。比如汇编指令“mov r0, r0”它把 r0的值复制到 r0并没有实际做什么事情你的本意可能是用这条指令来延时。编译器看到这指令后可能就把它去掉了。加上 volatile的话编译器就不会擅自优化。 其他 2个取值我们不关心也比较难以理解不讲。 ③ AssemblerTemplate 汇编指令用双引号包含起来每条指令用“\n”分开比如 “mov %0, %1\n” “add %0, %1, %2\n” ④ OutputOperands 输出操作数内联汇编执行时输出的结果保存在哪里。 格式如下当有多个变量时用逗号隔开 [ [asmSymbolicName] ] constraint (cvariablename) asmSymbolicName是符号名随便取也可以不写。 constraint表示约束有如下常用取值 constraint前还可以加上一些修饰字符比如“r”、“r”、“r”含义如下 variablenameC语言的变量名。 示例 1如下 [result] “r” (sum) 它的意思是汇编代码中会通过某个寄存器把结果写入 sum变量。在汇编代码中可以使用“%[result]”来引用它。 示例 2如下 “r” (sum) 在汇编代码中可以使用“%0”、“%1”等来引用它这些数值怎么确定后面再说。 ⑤ InputOperands 输入操作数内联汇编执行前输入的数据保存在哪里。 格式如下当有多个变量时用逗号隔开 [ [asmSymbolicName] ] constraint (cexpression) asmSymbolicName是符号名随便取也可以不写。 constraint表示约束参考上一小节跟 OutputOperands类似。 cexpressionC语言的表达式。 示例 1如下 [a_val]“r”(a), [b_val]“r”(b) 它的意思变量 a、b的值会放入某些寄存器。在汇编代码中可以使用%[a_val]、%[b_val]使用它们。 示例 2如下 “r”(a), “r”(b) 它的意思变量 a、b的值会放入某些寄存器。在汇编代码中可以使用%0、%1等使用它们这些数值后面再说。 ⑥ Clobbers 在汇编代码中对于“OutputOperands”所涉及的寄存器、内存肯定是做了修改。但是汇编代码中也许要修改的寄存器、内存会更多。比如在计算过程中可能要用到 r3保存临时结果我们必须在“Clobbers”中声明 r3会被修改。 下面是一个例子 : “r0”, “r1”, “r2”, “r3”, “r4”, “r5”, “memory” 我们常用的是有“cc”、“memory”意义如下 1.1.4 编写内联汇编实现加法 使用 GIT下载后源码在“07_驱动大全\source\01_inline_assembly\03_inline_assembly\main.c” 04 int add(int a, int b) 05 { 06 int sum; 07 __asm__ volatile ( 08 add %0, %1, %2 09 :r(sum) 10 :r(a), r(b) 11 :cc 12 ); 13 return sum; 所以第 08行代码就是把第 1、2个操作数相加存入第 0个操作数。也就是把 a、b相加存入 sum。 还可以使用另一种写法在 Linux内核中这种用法比较少见。 使用 GIT下载后源码在“07_驱动大全\source\01_inline_assembly\03_inline_assembly\main2.c” 1.1.5 earlyclobber的例子 OutputOperands的约束中经常可以看到“r”其中的“”表示 earlyclobber它是最难理解的。有 一些输出操作数在汇编代码中早早就被写入了新值 A在这之后汇编代码才去读取某个输入操作数这个输出操作数就被称为 earlyclobber(早早就被改了)。 这可能会有问题假设早早写入的新值 A写到了 r0寄存器后面读输入操作数时得到数值 B也可能写入 r0寄存器这新值 A就被破坏了。 核心原因就在于输出操作数、输入操作数都用了同一个 r0寄存器。为什么要用同一个因为编译器不知道你是 earlyclobber的它以为是先读入了所有输入操作数都处理完了才去写输出操作数的。按这流程没人来覆盖新值 A。 所以如果汇编代码中某个输出操作数是 earlyclobber的它的 constraint就要加上“”这就是告诉编译器给我分配一个单独的寄存器别为了省事跟输入操作数用同一个寄存器。 使用 GIT下载后源码在“07_驱动大全\source\01_inline_assembly\04_earlyclobber\main.c” 上面的代码中输出操作数%0对应的寄存器是 r3输入操作数%1对应的寄存器也是 r3。 第 8行更新了%0的值后第 9行修改%1的值由于%0、%1是同一个寄存器所以%0的值也被修改了。 最终返回的累加值是错的增加了 1如下图所示 怎么修改在第 11行加“”就可以了这是告诉编译器对于%0操作数它是 earlyclobber的不能跟其他操作数共用寄存器如下 从右边的反汇编码可以知道%0跟%1、%2使用不一样的寄存器所以后面第 9、10行无法影响到%0的值。 程序运行结果如下图所示 1.2 同步与互斥的失败例子 注意本节在 GIT上没有源码。 一句话理解同步与互斥我等你用完厕所我再用厕所。 什么叫同步就是条件不允许我要等等。 什么是互斥你我早起都要用厕所谁先抢到谁先用中途不被打扰。 同步与互斥经常放在一起讲是因为它们之的关系很大“互斥”操作可以使用“同步”来实现。我“等”你用完厕所我再用厕所。这不就是用“同步”来实现“互斥”吗 有时候看代码更容易理解伪代码如下 01 void 抢厕所(void) 02 { 03 if (有人在用) 我眯一会; 04 用厕所; 05 喂醒醒有人要用厕所吗; 06 } 假设有 A、B两人早起抢厕所A先行一步占用了B慢了一步于是就眯一会当 A用完后叫醒 BB也就愉快地上厕所了。 在这个过程中A、B是互斥地访问“厕所”“厕所”被称之为临界资源。我们使用了“休眠-唤醒”的同步机制实现了“临界资源”的“互斥访问”。 上面是一个有“味道”的例子回到程序员的世界一个驱动程序同时只能有一个 APP使用怎么实现 1.2.1 失败例子1 01 static int valid 1; 02 03 static ssize_t gpio_key_drv_open (struct inode *node, struct file *file) 04 { 05 if (!valid) 06 { 07 return -EBUSY; 08 } 09 else 10 { 11 valid 0; 12 } 13 14 return 0; //成功 15 } 16 17 static int gpio_key_drv_close (struct inode *node, struct file *file) 18 { 19 valid 1; 20 return 0; 21 } 22 看第 5行我们使用一个全局变量 valid来实现互斥访问。这有问题吗很大概率没问题但是并非万无一失。 注意编写驱动程序时要有系统的概念程序 A调用驱动程序时它可能被程序 B打断程序 B也去调用这个驱动程序。 下图是一个例子程序 A在调用驱动程序的中途被程序 B抢占了 CPU资源 程序 A执行到第 11行之前被程序 B抢占了这时 valid尚未被改成 0 程序 B调用 gpio_key_drv_open时发现 valid等于 1所以成功返回 0 当程序 A继续从第 11行执行时它最终也成功返回 0 这样程序 A、B都成功打开了驱动程序。 注意在内核态程序 A不是主动去休眠、主动放弃 CPU资源而是被优先级更高的程序 B抢占了这种行为被称为“preempt”(抢占)。 1.2.2 失败例子2 上面的例子是不是第 5行到第 11行的时间跨度大长了再优化一下程序行不行代码如下 01 static int valid 1; 02 03 static ssize_t gpio_key_drv_open (struct inode *node, struct file *file) 04 { 05 if (--valid) 06 { 07 valid; 08 return -EBUSY; 09 } 10 return 0; 11 } 12 13 static int gpio_key_drv_close (struct inode *node, struct file *file) 14 { 15 valid 1; 16 return 0; 17 } 18 第 5行先减 1再判断这样可以更大概率地避免问题但是还是不能确保万无一失。对数据的修改分为 3步读出来、修改、写进去。请看下图 进程 A在读出 valid时发现它是 1减 1后为 0这时 if不成立但是修改后的值尚未写回内存 假设这时被程序 B抢占程序 B读出 valid仍为 1减 1后为 0这时 if不成立最后成功返回 轮到 A继续执行它把 0值写到 valid变量最后也成功返回。 这样程序 A、B都成功打开了驱动程序。 1.2.3 失败例子3 前面 2个例子都是在修改 valid的过程中被别的进程抢占了那么在修改 valid的时候直接关中断不就可以了吗 01 static int valid 1; 02 03 static ssize_t gpio_key_drv_open (struct inode *node, struct file *file) 04 { 05 unsigned long flags; 06 raw_local_irq_save(flags); // 关中断 07 if (--valid) 08 { 09 valid; 10 raw_local_irq_restore(flags); // 恢复之前的状态 11 return -EBUSY; 12 } 13 raw_local_irq_restore(flags); // 恢复之前的状态 14 return 0; 15 } 16 17 static int gpio_key_drv_close (struct inode *node, struct file *file) 18 { 19 valid 1; 20 return 0; 21 } 第 06行直接关中断这样别的线程、中断都不能来打扰本线程了在它读取、修改 valid变量的过程中无人打扰。 没有问题了 对于单 CPU核的系统上述代码是没问题的但是对于 SMP系统你只能关闭当前 CPU核的中断别的CPU核还可以运行程序它们也可以来执行这个函数同样导致问题如下图 假设 CPU0上进程 A、CPU1上进程 B同时运行到上图中读出 valid的地方它们同时发现 valid都是 1减减后都等于 0在第 07行判断条件都不成立所以在第 14行都可以返回 0都可以成功打开驱动。
http://www.hkea.cn/news/14361735/

相关文章:

  • 做书籍封皮的网站wordpress首页错误
  • 泉州公司网站设计为什么建设网站
  • wordpress非常卡seo优化方式
  • 阿里巴巴国际站可以做网站吗软件工程师岗位职责
  • 哪个网站找住宿的便宜手机微网站怎么制作的
  • 网站后台用什么程序做医疗网站是否全部需要前置备案
  • 北京服装网站建设地址北京广告网站建设
  • Spring做网站和什么医院网站 整站源码
  • 怎样建设自己的商业网站云空间搭建网站
  • 专门做求职课程的网站网站备案主体负责人
  • 怎么做用户调研网站广州手机网站设计
  • 电影新网站如何做seo优化关键词优化是怎么做的
  • 手机网站整站模板下载工具邹平建设项目网站公示
  • 昆明网站制作维护手机版网站建设方案
  • 营销型网站建设测验题设计本官方网站下载
  • 杭州专业设计网站网站设计的基本步骤
  • 手机网站页面模板WordPress用rds云数据库
  • 网站百度搜不到如何制作自己想要的图片
  • 潜江哪里做网站电商创业新手怎么做
  • 做代理网站网站在线
  • 电商网站开发语言网站交易
  • 山西省住房城乡建设厅网站营销活动推广方案
  • 哪个地区的网站建设最好做网站和seo哪个好
  • 网站建设一般多少钱一年设计公司500强排名
  • 用html做登录网站深夜十大直播app软件
  • wordpress 3.8.1 exp 下载北京seo优化厂家
  • 徐州设计网站wordpress调取缩略图
  • 做服装要看国外哪些网站好饰品销售网站功能建设
  • 青岛网站建设排名杭州服装论坛网站建设
  • 汕头网站建设方法网站建设要托管服务器