基于Linux内核时钟的简单闹钟应用

您所在的位置:网站首页 闹钟的指针的安装顺序 基于Linux内核时钟的简单闹钟应用

基于Linux内核时钟的简单闹钟应用

2024-07-16 17:33:05| 来源: 网络整理| 查看: 265

目录 技术路线应用程序模块设计 总体功能以及设计流程图 代码Makefiletimer.calarm.c 编译过程及编译结果清理工程编译工程 运行或测试结果导入模块调用应用程序 参考文章

技术路线

在本实例中,我们采用应用程序以及内核模块的结合以达到使用动态定时器定时的目的。 其中使用了应用程序中调用了两个系统调用函数,而系统调用函数使用模块的方式进行导入。下面分别对应用程序和内核模块功能进行介绍:

应用程序

应用程序部分,首先接受来自用户输入的参数,并且对命令进行检测,如果有误,对用户进行提醒,如果正确,将调用第一个系统调用函数,将用户输入的定时时间传入内核函数中,进行定时器的定时,并且在后面通过轮询去检测定时完成标志,其中检测的函数也是一个系统调用函数,当定时完成标志被置一后,调用声音驱动函数,发出闹铃的响声并提示用户。

模块设计

模块设计部分,主要是方便应用程序对内核的动态定时器进行管理。其中在导入模块的时候,为了修改内存中的表项,首先修改寄存器的保护位,然后修改映射在内存中的系统调用表,把空闲的系统调用表项指向自己写的模块函数,并且保留原始的系统调用表,方便卸载模块的时候恢复系统调用表。模块中包含两个系统调用函数,其中一个接收来自应用程序中用户输入的参数,根据此参数设置定时器的基本定时,并在定时器的回调函数中打印倒计时,当到达定时时间的时候将计时完成标志置位。而第二个系统调用函数则是向应用程序返回计时完成标志,以达到通知应用程序的目的。

总体功能以及设计

获取到命令行中用户输入的定时参数,要能够对参数进行检查以及对输错的参数进行提示等,引导用户了解命令的使用方法。在正确得到用户输入后,解析命令,计时开始,并将用户设定计时信息输出在命令行中,方便用户进行确认。在系统内核日志中打印倒计时,在计时完成后,响起铃声,通知用户,并在命令行中输出完成信息。

流程图

在这里插入图片描述

代码 Makefile 1. #obj-m表示编译生成可加载模块 2. obj-m:=timer.o 3. #需要编译的模块源文件地址 4. PWD:= $(shell pwd) 5. CC:= gcc 6. #指定内核源码的位置 7. KERNELDIR:= /lib/modules/$(shell uname -r)/build 8. #关闭gcc优化选项,避免插入模块出错 9. EXTRA_CFLAGS= -O0 10. 11. all: 12. make -C $(KERNELDIR) M=$(PWD) modules 13. $(CC) alarm.c -o alarm 14. clean: 15. make -C $(KERNELDIR) M=$(PWD) clean 16. rm -rf alarm 17. sudo modprobe pcspkr timer.c 1. #include 2. #include 3. #include 4. #include 5. #include 6. #include 7. #include 8. #include 9. #include 10. #include 11. /* 系统调用号 */ 12. #define __NR_syscall 335 13. #define __NR_pausecall 336 14. /*struct timer*/ 15. struct timer_list mytimer; 16. unsigned long * sys_call_table; 17. unsigned int clear_and_return_cr0(void); 18. void setback_cr0(unsigned int val); 19. static void B_timer(unsigned long arg); 20. static int myfunc(int timecnt); 21. /* 用来存储cr0寄存器原来的值 */ 22. int orig_cr0; 23. /*定义一个函数指针,用来保存一个系统调用*/ 24. unsigned long *sys_call_table = 0; 25. /*timeok*/ 26. unsigned char timeok = 0; 27. static int (*anything_saved)(void);/*定义一个函数指针,用来保存一个系统调用*/ 28. unsigned int n_timecnt; 29. //为了修改内存中的表项,还要修改寄存器的保护位 30. /* 31. * 设置cr0寄存器的第17位为0 32. */ 33. unsigned int clear_and_return_cr0(void) 34. { 35. unsigned int cr0 = 0; 36. unsigned int ret; 37. /* 前者用在32位系统。后者用在64位系统,本系统64位 */ 38. //asm volatile ("movl %%cr0, %%eax" : "=a"(cr0)); 39. /* 将cr0寄存器的值移动到rax寄存器中,同时输出到cr0变量中 */ 40. asm volatile ("movq %%cr0, %%rax" : "=a"(cr0)); 41. ret = cr0; 42. /* 将cr0寄存器的值移动到rax寄存器中,同时输出到cr0变量中 */ 43. cr0 &= 0xfffeffff; 44. //asm volatile ("movl %%eax, %%cr0" :: "a"(cr0)); 45. /* 读取cr0的值到rax寄存器,再将rax寄存器的值放入cr0中 */ 46. asm volatile ("movq %%rax, %%cr0" :: "a"(cr0)); 47. return ret; 48. } 49. 50. /* 读取val的值到rax寄存器,再将rax寄存器的值放入cr0中 */ 51. void setback_cr0(unsigned int val) 52. { 53. //asm volatile ("movl %%eax, %%cr0" :: "a"(val)); 54. asm volatile ("movq %%rax, %%cr0" :: "a"(val)); 55. } 56. //一秒到了后,调用的函数,进行定时参数的检查,若到达定时器指定时间 57. //将定时完成标志位置1 58. 59. static void B_timer(unsigned long arg) 60. { 61. if(n_timecnt > 0) 62. { 63. printk("time cnt is %d\n",n_timecnt); 64. n_timecnt = n_timecnt - 1; 65. mod_timer(&mytimer, jiffies + HZ); 66. } 67. else 68. { 69. printk("It is on time\n"); 70. timeok = 1; 71. } 72. } 73. 74. static int myfunc(int timecnt) 75. { 76. /****************time set**********************/ 77. //初始化内核定时器 78. timeok = 0; 79. init_timer(&mytimer); 80. //指定定时时间到后的回调函数 81. mytimer.function = B_timer; 82. add_timer(&mytimer); 83. //基本定时为1秒 84. mod_timer(&mytimer, jiffies + HZ); 85. n_timecnt = timecnt; 86. printk("Time start!!!\n"); 87. return timecnt; 88. } 89. //系统调用函数,获取定时器状态信息 90. static char Askfunc(void) 91. { 92. return timeok; 93. } 94. 95. static void Init_syscall(void) 96. { 97. //修改映射在内存中的系统调用表,把空闲的系统调用表项指向自己写的模块函数 98. /* 获取系统调用服务首地址 */ 99. sys_call_table = (unsigned long *)kallsyms_lookup_name("sys_call_table"); 100. //printk("sys_call_table: 0x%p\n", sys_call_table); 101. /* 保存原始系统调用 */ 102. anything_saved = (int(*)(void))(sys_call_table[__NR_syscall]); 103. /* 设置cr0可更改 */ 104. orig_cr0 = clear_and_return_cr0(); 105. /* 更改原始的系统调用服务地址 */ 106. sys_call_table[__NR_syscall] = (unsigned long)&myfunc; 107. sys_call_table[__NR_pausecall] = (unsigned long)&Askfunc; 108. /* 设置为原始的只读cr0 */ 109. setback_cr0(orig_cr0); 110. } 111. //导入模块的时候调用的函数 112. static int __init mytimer_init(void) 113. { 114. printk("Insmod Ok !\n"); 115. Init_syscall(); 116. return 0; 117. } 118. //卸载模块的时候调用的函数 119. static void __exit mytimer_exit(void) 120. { 121. del_timer(&mytimer); 122. /* 设置cr0中对sys_call_table的更改权限 */ 123. orig_cr0 = clear_and_return_cr0(); 124. /* 设置cr0可更改 */ 125. sys_call_table[__NR_syscall] = (unsigned long)anything_saved; 126. /* 恢复原有的中断向量表中的函数指针的值 */ 127. setback_cr0(orig_cr0); 128. /* 恢复原有的cr0的值 */ 129. printk("rmmod OK !\n"); 130. } 131. //module function 132. module_init(mytimer_init); 133. module_exit(mytimer_exit); 134. // 135. 136. //相关信息 137. MODULE_LICENSE("GPL"); 138. MODULE_AUTHOR("willpower"); 139. MODULE_DESCRIPTION("Demo for timer"); alarm.c 1. #include 2. #include 3. #include 4. #include 5. #include 6. #include 7. #include 8. #include 9. #include 10. #include 11. /*input alarm -t 30 or pause like alarm -h (like 30 )*/ 12. int timecnt,pausetime; 13. //sounds 14. void Buzzer(void); 15. //命令解析函数 16. char parse_command_line(char **argv, int *timecnt) 17. { 18. char *arg0 = *(argv++); 19. while ( *argv ) 20. { 21. //检查参数是否含有-t 22. if ( !strcmp( *argv,"-t" )) 23. { /*time*/ 24. //将输入的数字字符串转换为整形 25. int time = atoi ( *( ++argv ) ); 26. //检查输入定时器参数是否在正确范围内 27. if ( ( time 65535 ) ) 28. { 29. //输出错误提示 30. fprintf ( stderr, "Bad parameter: time must be from 1..65535\n" ); 31. exit (1) ; 32. } 33. else 34. { 35. *timecnt = time; 36. argv++; 37. } 38. return 1; 39. } 40. else 41. { 42. //command error output help 43. fprintf(stderr, "Bad parameter: %s\n", *argv); 44. printf("Please enter again!\n"); 45. printf("You can use -t for timecnt"); 46. exit(1); 47. } 48. } 49. } 50. int main(int argc, char **argv) 51. { 52. //parse_conmand 53. if(parse_command_line(argv, &timecnt) == 1) 54. { 55. printf("timecnt is %d\n", timecnt); 56. //系统调用,传入定时事件timecnt 57. timeset(timecnt); 58. while(1) 59. { 60. //对定时完成标志做检查 61. if(timeget() == 1) 62. { 63. //定时事件到达,响起铃声 64. Buzzer(); 65. break; 66. } 67. } 68. //输出完成调用提示 69. printf("timecnt action is successful!\n"); 70. } 71. else 72. { 73. printf("Error!"); 74. } 75. return 0; 76. } 77. void Buzzer(void) 78. { 79. int console_fd; 80. int i; 81. //open dev 82. if ( ( console_fd = open ( "/dev/console", O_WRONLY ) ) == -1 ) 83. { 84. //Error 85. fprintf(stderr, "Failed to open console.\n"); 86. perror("open"); 87. exit(1); 88. } 89. for(i = 0; i


【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭