2007 年 1 月 15 日
通过修改内核以在受震动导致内核出现紧急情况时自动重置 Linux 膝上型计算机,让您的计算机处于受保护的状态。在内核空间和用户空间中实现震动检测算法,从而执行自动关闭并在特定动力条件得到满足时重新启动。2003 年,IBM® 开始销售在商业操作系统中集成了加速度传感器及相关软件的 ThinkPad 膝上型计算机,以便在 ThinkPad 坠地时对硬盘进行保护。来自 IBM 和其他地方的富有魄力的计算机程序高手们已经为 Linux 内核开发了利用这些传感器的模块。膝上型计算机的屏幕显示方向、桌面切换、甚至是游戏控制和实时 3D 模型等特性均已实现。
2006 年中期,适用于 Linux 膝上型计算机的基于敲击的命令开始可用于用户空间 Perl 脚本(与内核空间中的基于 C 的代码相对),允许用户运行基于特定敲击序列的随机命令。本文描述了修改 Linux 内核以添加经常需要的功能的过程:对物理输入做出反馈。当 Linux 内核出现紧急情况时,用户可以震动计算机(或对膝上型计算机执行开发人员可配置的任意次数的物理移动),计算机将重置。
本文还介绍了在非紧急情况模式下执行正常关闭的方法。例如,如果用户不注意将计算机放在尚未拉好的计算机包中,则需要计算机检测到正常行走或开车的动作,并关闭计算机。
先决条件
硬件
许多在 2003 年以后(包括 2003 年)制造的 IBM ThinkPad 都配有 HDAPS 硬件。如果不能确定硬件配置,可以到 Lenovo 的 Web 站点中查看详细的技术信息。要运行以下代码,必须有 ThinkPad。某些 MacBook 配有加速度传感器及同样的通过内核访问这些传感器的通用方法。但是,此处的代码并未在 Apple 硬件上做测试,而是基于两个 IBM ThinkPad T42p 型号开发及测试的。有关如何查找在物理上支持膝上型计算机所需的 ThinkPad 硬件的链接,请参阅 参考资料。
软件
本文假定您熟悉内核构建过程,并了解内核编译所带来的发行版间的不一致性。有关内核构建过程的简介,以及一些优秀的入门示例,请参阅 参考资料。
从 kernel V2.6.15 起,HDAPS 驱动程序已经包含在 Linux 内核中。为了简单起见,请获取最新的内核程序。为了便于开发及管理,本文是基于 Fedora Core V5 开发的。下面用于设置内核构建环境的指导信息是专门针对 Fedora Core 的,但是一般原理适用于所有 Linux 发行版。
内核开发设置
内核配置、编译和测试
要修改内核,需要按照版本信息中的指导信息进行操作。打开 Web 浏览器并开始按照第 8.6 节:“Preparing for Kernel Development” 中的指导信息进行操作。执行到第 2 部分时,可能会在执行第二条命令 su -c 'yumdownloader --source kernel' 时遇到问题。如果该命令未能把 kernel-2.6.15-1.2054_FC5.src.rpm 软件包下载下来,请使用 wget 命令 wget FTP://ftp.linux.ncsu.edu/pub/fedora/linux/core/5/source/SRPMS/kernel-2.6.15-1.2054_FC5.src.rpm 来获取该软件包。
在执行步骤 5 时,请用 cp configs/kernel-2.6.15-i686.config .config 命令选择基本的 i686 默认配置。确保将 makefile 中的 EXTRAVERSION 部分从 -prep 更改为 -1.2054_FC5。用 make oldconfig 更新构建配置。然后用 su -c "yum install kernel-devel" 命令安装内核开发模块。该模块将用于编译紧急情况触发模块。
我们现在已经完成了 Fedora Core V5 Release Notes 文档中介绍的内核配置的相关部分。其余步骤都是所有内核构建过程通用的标准步骤。建议现在构建并安装新内核、模块和 RAM 磁盘设置以确保一切按预期运行。如果您对自己的新内核配置很有信心,可以跳过以下步骤并直接转到内核修改部分。
用 make 命令构建新内核。成功构建内核后,请用 su -c "cp ./arch/i386/boot/bzImage /boot/vmlinuz-2.6.15hdaps" 命令将其复制到 /boot 目录。您需要使用 su -c "make modules_install" 命令构建模块。最后一个构建步骤是用 su -c "/sbin/mkinitrd hdapsInitrd.img 2.6.15-1.2054_FC5" 命令为 HDAPS 内核创建 RAM 磁盘。用 su -c "cp hdapsInitrd.img /boot/" 命令将这个新的 RAM 磁盘复制到引导区。引导时,用以下行更新 grub.conf 文件:
清单 1. grub 配置
title Fedora Core (2.6.15hdaps) root (hd0,0) kernel /vmlinuz-2.6.15hdaps ro root=/dev/VolGroup00/LogVol00 rhgb quiet initrd /hdapsInitrd.img修改 panic.c 和 hdaps.c
现在已经准备好开始一些可快速完成的内核配置。确保位于 ~/rpmbuild/BUILD/kernel-2.6.15/linux-2.6.15.i686 目录。一定要先包含 hdaps 模块作为内核的内置组件,从而准备好在计算机的运行模式中提供对各处的震动检查。
使用 make menUConfig 命令并选择 Device Drivers > Hardware Monitoring Support。键入 Y 以包含 Hardware Monitoring Support 模块,因为 HDAPS 模块依赖于此模块。滚动到清单底部,并在 IBM Hard Drive Active Protection System (hdaps) 条目的旁边再次键入 Y。退出菜单并保存配置。
打开 drivers/hwmon/hdaps.c 文件,然后将以下文本添加到 include 部分中:#include <linux/reboot.h>。并紧挨着 hdaps_read_pair 子程序后面添加下面的新子程序:
清单 2. 来自 hdaps.c 的完整的 panicShake 子程序
/* * panicShake - reboot the machine if shaken */extern void panicShake(void){ int ret, x, y; // return value and x,y from hdaps int int baseX = -5000; // off scale default values int baseY = -5000; int totalDev = 0; // running total of deviations from rest (shaking total) int devThreshold = 4000; // larger threshold for more shaking int dimShiftX = 150; // in case your users shake more in a certain dimension int dimShiftY = 150; while(1) { ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); if (!ret) { if( x != 0 && y != 0 ) { // if its a successful read and not a zero read if( baseX == -5000 ) { baseX = x; baseY = y; } if( abs(baseX - x) > dimShiftX abs(baseY - y) > dimShiftY ) { totalDev += abs(baseX - x); totalDev += abs(baseY - y); } if( totalDev > devThreshold ) { printk(KERN_EMERG "ok, ok! you're shaking my substrate - restarting"); emergency_restart(); } }//if not a zero value }//if successful read of hdaps data }//infinite while}//panicShake震动检测程序就绪后,需要在系统出现紧急情况时调用该检测程序。打开 kernel/panic.c 文件,并在紧挨着 panicBlink 条件部分之前的位置上放置一个对 panicShake(); 子程序的调用。发出 make 命令。趁着对内核进行重新构建时,让我们复查一下震动检测代码。首先,设置一些变量:
清单 3. panicShake 变量
int ret, x, y; // return value and x,y from hdaps int baseX = -5000; // off scale default values int baseY = -5000; int totalDev = 0; // running total of deviations from rest (shaking total) int devThreshold = 4000; // larger threshold for more shaking int dimShiftX = 150; // in case your users shake more in a certain dimension int dimShiftY = 150;
需要特别注意的是偏差阈值参数和空间移位参数。这些参数可能需要根据尝试检测的动作的独特性质做出调整。例如,如果感觉迫切需要像完成篮球传球动作一样震动计算机,可尝试减少 dimShiftX 参数以更轻松地检测垂直于计算机屏幕的动作。反过来,如果震动脉冲触发剧烈的锯齿状动作,则考虑减少 dimShiftY 参数,以便快速地检测平行于屏幕的震动并在发生进一步的损害之前重置计算机。空间参数选择 150 及总偏差选择 4000 都旨在检测一般用户的典型震动动作。要立即响应输入,请尝试将空间移位参数减少到 10 或更少,并将总偏差参数减少到 10 或更少。这些值将导致其他类型的输入被立即识别出来,例如猛击键盘或拍打显示器外壳。
接下来,考虑无限循环语句和条件。
清单 4. panicShake hdaps 读取和基本设置
while(1) { ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); if (!ret) { if( x != 0 && y != 0 ) { // if its a successful read and not a zero read if( baseX == -5000 ) { baseX = x; baseY = y; }代码运行如下:其余时间里,从 Hdaps 传感器中读取当前加速度传感器读数。读数经常是不成功的或者两个值都等于 0,0,这是不能用的数据。需要避免这些虚假的 0,0 读数,来自传感器各个方向上的每 10 个读数中就会有一个读数是 0,0 —— 无效的数据,确实如此。如果是首次成功读取,则将基本参数设为第一个 x 值和 y 值。如果计算机被放在不平的表面(例如人的膝盖上)时发生紧急情况,这将允许我们更有力地检测震动或其他动作。
子程序的其余部分实现简单的震动检测算法。
清单 5. panicShake 震动检测
if( abs(baseX - x) > dimShiftX abs(baseY - y) > dimShiftY ) { totalDev += abs(baseX - x); totalDev += abs(baseY - y); baseX = x; baseY = y; } if( totalDev > devThreshold ) { printk(KERN_EMERG "ok, ok! you're shaking my substrate - restarting"); emergency_restart(); } }//if not a zero value }//if successful read of hdaps data }//infinite while如果在任意方向上的空间移位大于我们先前设定的阈值,就按照两个方向上移动的量增加总偏差。然后将当前的基数设为现有的加速度级别。这样重复地重新初始化基数值要求用户持续超出空间移位值以增加检测到的总偏差。这允许用户在紧急模式下移动并存储 ThinkPad,从而安全地将机器送到系统管理员那里。如果仅需要侧立、倾斜和持拿 ThinkPad 以触发重新启动,请删除重新初始化设定。
测试 panicShake() 内核
要发动一种紧急情况,需要在内核中调用紧急情况子程序。创建以下 makefile:obj-m := panicCall.o,程序 panicCall.c 在编译时将使用该文件:
清单 6. panicCall.c 内核模块源代码
/* * panicCall.c - Instigate a kernel panic */#include <linux/module.h> /* Needed by all modules */#lincude <linux/kernel.h> /* Needed for KERN_INFO */static char *pMesgStr = "PANIC SHAKE AND BAKE";int init_module(void){ printk(KERN_INFO,"panicCall module loaded\n"); panic(pMesgStr); return(0);}void cleanup_module(void){ printk(KERN_INFO,"panicCall module unloaded, beyond possible");}以超级用户身份用 make -C /lib/modules/$(uname -r)/build SUBDIRS=$PWD modules 命令编译 panicCall 模块。现在就有了一个可调用的模块可以使用 insmod panicCall.ko 命令触发紧急情况。如果还没有该模块则重新引导(以激活 hdaps 紧急情况下触发启用震动的内核),并运行 insmod panicCall.ko。应当会看到类似以下内容:
清单 7. 内核紧急情况堆栈
panicCall: module license 'unspecified' taints kernel.Kernel panic - not syncing: PANIC SHAKE AND BAKE ACTIVE [<c011a32e>] panic+0x3e/0x174 [<f8a97017>] init_module+0xb/0xc [panicCall] [<c013050a>] sys_init_module+0x1382/0x1514 [<c0152413>] do_sync_read+0xb8/0xf3 [<c012a17f>] autoremove_wake_function+0x0/0x2d [<c01c0672>] _atomic_dec_and_lock+0x22/0x2c [<c0169c32>] mntput_no_expire+0x11/0x6d [<c0102bc1>] syscall_call+0x7/0xb现在拿起计算机,然后用力地晃动它,计算机将打印出 “shaking substrate” 消息并执行重新启动。如果您不希望晃动可能活动的磁盘驱动器,请以超级用户身份发出以下命令:
清单 8. RAM 磁盘创建和模块复制
mkdir /tmp/ramdisk0mke2fs /dev/ram0mount /dev/ram0 /tmp/ramdisk0/cp /root/panicCall.ko /tmp/ramdisk0/cp /sbin/insmod /tmp/ramdisk0/现在有了将模块插入位于 RAM 磁盘的内核所需的两个文件。用以下部分更新 /etc/init.d/halt 脚本,将其放在刚好位于 fsck check 部分之下 halt execute 部分之上的位置:
清单 9. 修改 /etc/init.d/halt
echo "disks now mounted in readonly mode, spin down in 5 seconds";/sbin/hdparm -S 1 /dev/hdaecho "spin down hda called, waiting 10 seconds";sleep 10echo "calling panic from ramdisk location";/tmp/ramdisk0/insmod /tmp/ramdisk0/panicCall.ko
以超级用户身份执行命令 init 0 以将计算机转入关闭模式中。在调用关闭程序前,计算机将把紧急情况触发模块装入内核,并调用震动检测程序。 如果在系统关闭时仔细听硬盘的声音,可以听到明显比以往更长的喀哒声,然后多普勒磁盘将随着机械臂逐渐下降的 “积载” 位置而降低并且磁盘旋转停止。再过大约五秒钟后,将从 RAM 磁盘执行紧急情况模块,而物理磁盘头仍停止不动。现在,您可以随心所欲地晃动 ThinkPad 而无需考虑磁盘的运行状况。
用户空间关闭和动作检测
很多 IT 管理员都十分怀念能够随时获知硬件物理历史记录的功能。使用同一个简单的震动检测算法、一个 Perl 脚本和一种监视策略,管理员将能够更好地跟踪硬件的状态。例如,使用下面的 Perl 脚本在计算机遭到用户震动时平稳地关闭计算机。根据用户对 ThinkPad 的操作发送一封电子邮件、闪烁 “ThinkLight” 或播放一个声音文件,这些都可以轻松地完成。
清单 10. 用于检测震动的 Perl 脚本,第 1 部分
#!/usr/bin/perl -w # shakeShutdown.pl - shutdown (or other command) when the computer is shakenuse strict;my $file = "/sys/devices/platform/hdaps/position";my $baseX = -5000;my $baseY = -5000;my $totalDev = 0;if( @ARGV != 1 ){ die "specify a threshold value" }my $devThreshold = $ARGV[0];my $dimShiftX = 150;my $dimShiftY = 150;while(1){ open(HD,"$file") or die "can't open file"; my $line = <HD>; chomp($line); $line =~ s/\(//g; $line =~ s/\)//g; $line =~ s/\,/ /g; my( $x, $y ) = split " ", $line;正如您所见,初始的程序设置几乎与 hdaps 内核代码完全相同。正则表达式和 split 命令仅将 x 值和 y 值从 (5,4) 更改为 5 和 4。程序的其余部分实质上也是相同的:
清单 11. 用于检测震动的 Perl 脚本,第 2 部分
if( $x != 0 >> $y != 0 ) { if( $baseX == -5000 ) { $baseX = $x; $baseY = $y; } if( abs($baseX - $x) > $dimShiftX abs($baseY - $y) > $dimShiftY ) { $totalDev += abs($baseX -$x); $totalDev += abs($baseY -$y); $baseX = $x; $baseY = $y; } if( $totalDev > $devThreshold ) { print "threshold passed $totalDev\n"; my $res=`/sbin/shutdown -h 1`; } } close(HD);}请注意 shutdown -h 1 命令。这将给用户提供 60 秒的时间更改方法并发出关闭中止。更改此命令以运行您最喜欢的邮件程序,用户滥用设备时可以让系统管理员知道此情况。将消息记录到系统日志中,或让 PC 扬声器发出声响以便对获得的物理输入发出即时用户反馈。用 perl shakeShutdown.lp 1000 命令运行脚本。偏差阈值变得更小,因为加速度传感器的每次读取间隔与内核空间的每次读取间隔比较而言减少了。
修改空间移位参数和偏差阈值可以提供有益的对内核空间外部的物理活动的额外监视。例如,要采集 “行走” 行为的数据,需要将空间移位参数设为大约 20,并将偏差阈值设为大约 5000。这将检测到大约 63 个双坐标轴空间移位,这与膝上型计算机在处于运行状态时被放在典型的单肩背膝上型计算机包中的情况一致。检测到这种长距离行走后(不同于从办公位置到会议室的短距离行走),计算机将进入关闭程序以避免过热,因为空气在背包中不流通。修改空间移位参数使其具有高灵敏度,任何较大的撞击、坠落或震动都会被记录下来。
结束语
通过使用针对用户空间和内核级代码的这些简单算法,现在能够检测、记录和响应来自用户的各种物理输入。使用这些代码示例,从根据连续的加速度计算出来的高度修改硬盘性能参数,到丈量从办公位置到会议室的距离,并将其用邮件发送给空间规划师,您都可以应对自如。
(出处:http://www.sheup.com)