首页 > 代码库 > 树莓派玩耍笔记4 -- 树莓派ssh党必备的配置

树莓派玩耍笔记4 -- 树莓派ssh党必备的配置

1. 关闭桌面显示

对于ssh 党,当然不需要系统花费资源在显示上。

所以我们先在 "raspi-conifg" 下选择默认启动为Text 启动(这好像也是Raspbian 的默认配置)。



其次,我们还可以直接关闭HDMI 接口, 使用" /opt/vc/bin/tvservice -o " (直接使用 tvservice -o 亦可,如想了解更多请使用 tvservice --help 查阅此命令更多地使用方法)命令即可轻易做到。

pi@long ~ $ /opt/vc/bin/tvservice -o
Powering off HDMI

pi@long ~ $ tvservice -o
Powering off HDMI

而且,这个命令只是关闭当前,如想重启后仍然奏效,那么你需要将上面命令添加至“/etc/rc.local ”文件中的exit 语句之前即可。


2. 使用看门狗防死机[1]

watchdog, 相信很多人都对这个概念不陌生,中文译为“看门狗”。工作原理很简单:在系统运行以后也就启动了看门狗的计数器,看门狗就开始自动计数,如果到了一定的时间还不去清看门狗(俗称喂狗),那么看门狗计数器就会溢出从而引起看门狗中断,造成系统复位(如果你想学习更多的关于看门狗的东西,请下载内核源码,看门狗相关的文档可见"kernel-version/Document/watchdog/" 目录下的相关内容)。

那么,树莓派下我们可以使用看门狗来不断检测板卡的一些状态,如果状态不符合看门狗配置文件中设置的(比如温度过高、内存不足等),系统就会复位,从而保证系统/板卡的安全。


首先,我们确保系统中的看门狗模块已运行:

pi@long ~ $ lsmod  | grep dog
bcm2708_wdog            3613  1

如果发现你的系统中没有上面的模块在运行,那么你需要运行“ sudo modprobe bcm2708_wdog ”来加载看门狗模块。


其次,我们需要运行看门狗守护进程,他的功能就是每间隔一定时间向看门狗硬件模块发送一个状态,如果失败,则触发硬件看门狗让树莓派重启:

$ sudo apt-get install chkconfig watchdog    # 其中 chkconfig 用于一些系统配置,可能已安装,可能被一些系统精简掉,这里添加上,如果已安装apt-get 会自动不关心。

$ chkconfig watchdog on              # 设定看门狗程序随系统启动自动运行

现在我们可以查看watchdog 是否已运行:

pi@long ~ $ chkconfig -A | grep watchdog              # chkconfig -A 还会显示很多其他的状态,有兴趣可以看看
watchdog                on


最后,我们需要对看门狗的配置文件“ /etc/watchdog.conf ”进行如下配置:

$ sudo vim /etc/watchdog.conf 
(1)
watchdog-device = /dev/watchdog 前的注释#号,让他监控的设备指向CPU的硬件看门狗 
(2)
max-load-1 = 24前的注释#号,当1分钟load进程超过24个的时候(一般5个就是超高负载了,更高可以认为是死机,这在遭遇DDOS攻击的时候很容易遇到)就触发重启)
(3)检查板卡温度:
首先去掉如下两行前的注释号 "#"
#temperature-device =
#max-temperature = 120
再将这两行修改为:
temperature-device = /sys/class/thermal/thermal_zone0/temp
max-temperature = 75000  

注:树莓派的工作温度为
 -40C ~ 85℃, 温度一般不超过85度就不会损坏芯片,/sys/class/thermal/thermal_zone0/temp记录的是实时的温度,单位为千分之一摄氏度,所以这里设置75000就是75℃,已是很保险的温度了。而且我一般使用温度保持在60以下,还是很不错的。
(4)还可以设置内存耗尽就重启,如min-memory =1 前的注释#号去掉
(5)还可以设置监控的间隔,如 interval = 1 前的注释#号去掉,该1为任意数字,单位是秒,默认是10秒一次健康检查


最后我们只需要将看门狗允许起来即可:/etc/init.d/watchdog start 

最后如果你想测试一下你的看门狗能不能正常工作,可以运行一下 shell 炸弹" :(){ :|:& };: ",shell 炸弹会迅速耗尽系统资源,然后直接导致死机。如果你的看门狗奏效,树莓派会在10秒后重启~~然后你就又可以ssh 链接了。

注:到这里,看门狗就已经配置完成(会开机自启动),妈妈再也不用担心你的树莓派死机后链接不上了!


2.1 龙哥小课堂: 简析shell 炸弹

对于上面这个shell 炸弹:(){ :|:& };: ",整个语句13个字符(包括两个空格),那么它如何做到迅速耗尽系统资源直接导致死机的呢?

还是挺有意思的,如果你有兴趣,我在这里做个简单的介绍害羞

整个语句根据shell 语法,可以分成两个语句“ :(){ :|:& } ”和“ : ”,其中分号和C语言一样,作为语句的分隔符!那么就很简单了:前面的语句是定义了一个“: ”函数,而第二个语句是运行这个函数。(根据shell 语法,函数定义可以使用function func_name() { ... } 这种的利用关键字function 指示清楚,亦可直接使用func_name() { ... } 这种简单方便的定义)

那么为什么这几个字符定义的函数能够这么牛的耗尽资源?我们分开来看 :() 这个部分指示了函数名为且不需要传递参数。后面的函数定义部分由符号{}包括在内!所以函数“: ”所做的工作就是“ :|:& ”, 其中“:|:” 部分管道号"| "前后分别两次调用函数“ ”函数本身,而后面的“ & ”指示后台运行。


所以串起来此语句的工作流程就是:

1. 前半部分“ :(){ :|:& } ”定义了一个“ ”函数,这个函数会后台调用它本身“ ”,而调用的“ ”又会继续调用,这样不断地死循环下去。

2. 前面这只是定义了“ ”函数,后半部分“ ”则是直接调用运行这个函数“ ”。

所以不断地调用自身...,直至耗尽资源直接导致死机!


3. 如何检查树莓派的使用状况

这里写了一个简单的shell 脚本,可以用来查询CPU 温度,CPU利用率,GPU温度,内存状况以及磁盘使用状态。很简单,不多解释直接贴代码:

#!/usr/bin/python
import os


def get_CPU_temp():
    f = file("/sys/class/thermal/thermal_zone0/temp")
    temp = float(f.read().strip("\n"))/1000
    return "%3.1f " % temp


def get_CPU_use():
    return os.popen("top -n1 | awk '/Cpu\(s\):/ {print $2}'").read().strip("\n")


def get_GPU_temp():
    status = os.popen("/opt/vc/bin/vcgencmd measure_temp").read().strip("\n")
    return status.split("=")[1].replace("\'C", "")


def getRAMinfo():
    p = os.popen('free')
    i = 0
    while 1:
        i = i + 1
        line = p.readline()
        if i==2:
            return(line.split()[1:4])


def getDiskSpace():
    p = os.popen("df -h /")
    i = 0
    while 1:
        i = i +1
        line = p.readline()
        if i==2:
            return(line.split()[1:5])
if __name__ == '__main__':
    (RAM_total, RAM_used, RAM_free) = getRAMinfo()
    (DISK_total, DISK_used, DISK_free, DISK_perc) = getDiskSpace()
    print """Current status:
\tCPU Temperature : %s\'C
\tCPU Use : %s%c\n
\tGPU Temperature : %s\'C\n
\tRAM Total : %.1fMB
\tRAM Used  : %.1fMB
\tRAM Free  : %.1fMB\n
\tDISK Total : %s 
\tDISK Used  : %s
\tDISK Used  Percentage : %s """ % (get_CPU_temp(), get_CPU_use(), 0x25, get_GPU_temp(), float(RAM_total)/1000, float(RAM_used)/1000, float(RAM_free)/1000, DISK_total, DISK_used, DISK_perc)


运行的效果如下:

pi@long ~ $ ./get.py
Current status:
	CPU Temperature : 48.7 'C
	CPU Use : 0.7%

	GPU Temperature : 48.7'C

	RAM Total : 448.0MB
	RAM Used  : 63.2MB
	RAM Free  : 384.8MB

	DISK Total : 7.2G 
	DISK Used  : 2.4G
	DISK Used  Percentage : 35%

这样,我们远程登陆时,可以随时运行此脚本查询一下当前的树莓派的状况了!


4. 开机后发送邮件告知 IP地址[3]

对于ssh 党,什么最重要?当然是小 pi 的IP 地址咯,那么前面我们说到 /etc/rc.local 文件是用来配置启动后执行的,我们可以在里面添加一些自定义的内容。

那么我们可以写一个脚本,获取本机IP然后将此地址发送到指定的邮箱中,这样我们除了能够获取IP 地址之外,还可以知道什么时候系统重启过(有可能是上面的看门狗发现系统状况不对后重启的系统!)。有了此步骤,即使我设置了dhcp 动态分配IP 也不怕找不到树莓派IP 了!!(当然,大神可以使用nmap 查找当前网段的IP,那么不在一个网段呢?如果树莓派在家里呢?)

我们可以先写一个python 脚本叫做 sendip.py:

#/bin/env python
# -*-coding:utf8-*-
import socket
import fcntl
import time
import struct
import smtplib
import urllib
import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage


#发送邮件的基本函数,参数依次如下
# smtp服务器地址、邮箱用户名,邮箱秘密,发件人地址,收件人地址(列表的方式),邮件主题,邮件html内容
def sendEmail(smtpserver, username, password, sender, receiver, subject, msghtml):
    msgRoot = MIMEMultipart('related')
    msgRoot["To"] = ','.join(receiver)
    msgRoot["From"] = sender
    msgRoot['Subject'] =  subject
    msgText = MIMEText(msghtml,'html','utf-8')
    msgRoot.attach(msgText)
    #sendEmail
    smtp = smtplib.SMTP()
    smtp.connect(smtpserver)
    smtp.login(username, password)
    smtp.sendmail(sender, receiver, msgRoot.as_string())
    smtp.quit()


# 检查网络连同性
def check_network():
    # 试验5次ping 百度,如果连通就返回True,否则返回False
    for i in range(0, 5):
        try:
            result=urllib.urlopen('http://baidu.com').read()
            #print result
            print "Network is Ready!"
            break
        except Exception , e:
            print e
            print "Network is not ready,Sleep 5s...."
            time.sleep(5)
    else:
        print "Sorry that pi isn't connectted to Internet now"
        return False
    return True


# 获得本级制定接口的ip地址
def get_ip_address():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("1.1.1.1",80))
    ipaddr=s.getsockname()[0]
    s.close()
    return ipaddr


if __name__ == '__main__':
    if check_network():
        ipaddr = get_ip_address()
        now = datetime.datetime.now()
        time_info = now.strftime('%Y-%m-%d %A %H:%M:%S')
        send_text = "Boot time: %s\nIP addr: %s" % (time_info, ipaddr)
        sendEmail('smtp.163.com','网易邮箱账号','网易邮箱密码','邮箱邮箱地址',['收件人地址'], 'Raspberry Pi boot status', send_text)
    else:
        print "Sorry that I can't help without network"


将上面的脚本倒数第三行中相应信息修改了即可!注意,收件人是列表形式,可以添加多个邮箱。建议创建一个新的邮箱,专门用作发送树莓派发送邮件之用。

那么最后我们将运行它的命令 “ /usr/bin/python /home/pi/send_ip.py ” 添加到文件“ /etc/rc.local  ”中(exit 语句之前!)让它开机自运行即可!


================

[1] http://www.eeboard.com/bbs/thread-6763-1-1.html

[2] 更多chkconfig 参见: http://www.cnblogs.com/panjun-Donet/archive/2010/08/10/1796873.html

[3] http://www.opstool.com/article/299