学习笔记:树莓派用户指南(第3版)

Linux系统管理

GUI与控制台切换

加载GUI: startx

控制台:Ctrl+Alt+F1

使用外部存储设备

1. 查看已连接驱动器列表: sudo fdisk -l

/dev/sdXN中,X是驱动器号,N是分区编号

2. 创建挂载点: sudo mkdir /media/externaldrive

3. 开放用户访问权限:

1
2
3
4
# 设置该文件夹所归属的组
chgrp -R group /media/externaldrive
# 给组分配该文件夹的写权限
chmod -R g+w /media/externaldrive

4. 挂载USB存储设备:

1
mount /dev/sdXN /media/externaldrive -o=rw

磁盘物理分布

第一个分区很小,为VFAT格式,与Windows使用的分区一致,可被windows识别。该分区挂载在/boot目录下

第二个分区大很多,是EXT4格式(Linux原生文件系统)

软件管理

  • 更新apt缓存: apt-get update

  • 查找软件: apt-cache search keyword

  • 安装软件: apt-get install app

  • 卸载软件: apt-get remove appapt-get purge app

remove与purge区别:

remove 留下软件配置文件
purge 删除所有相关文件

  • 升级软件,有两种方法:

全部更新:apt-get upgrade
升级单个软件包,再次安装该软件即可

故障排查

鼠标与键盘

常见问题:键盘重复某些字符

可能原因:

  • 自身需要较大功率
  • 内部芯片与电脑USB接口电路冲突

解决方法:更换合适的键盘鼠标

供电

供电诊断方法:在Model B+ 中可以用电源指示灯做电压测试,如果指示灯闪烁或熄灭,表明当前电源供电不足4.65V

启动

大多数情况下不能启动是由于SD卡的问题。

如果接上micro-USB供电后,电源指示灯亮,而ACT指示灯不亮,说明SD卡存在问题。

网络

诊断工具:ifconfig

  • Link encap:网络封装类型,物理网络端口显示为Ethernet
  • Hwaddr: Mac地址
  • inet addr: IP地址
  • Mask:网络掩码,用于控制网络用户的最大量

关闭和重启网络端口:

1
2
3
4
# 关闭网络端口
ifdown eth0
# 重启网络端口
ifup eth0

测试网络,通过发送数据到远程计算机并等待一个相应:

1
ping -c -l www.raspberrypi.org

紧急内核

树莓派有两个内核:

  • 默认内核:/boot/kernal.img
  • 紧急内核:/boot/kernal-emergency.img

加载紧急内核的方法:编辑/boot/config.txt,在文件中添加以下命令

1
kernal=kernal-emergency.img

网络配置

有线网络

在某些情况下,树莓派所在的网络中不包含DHCP服务器,此时若要使用树莓派,就要手动配置树莓派网络。主要是编辑/etc/network/interfaces文件:

我们要编辑的那行以iface eth0 inet开始,首先用static替换该行最后的dhcp,然后回车开始新的一行,并将以下内容格式黏贴进来:

1
2
3
address xxx.xxx.xxx.xxx # 指定的静态IP地址
netmask xxx.xxx.xxx.xxx # 子网掩码
gateway xxx.xxx.xxx.xxx # 网关,即路由器或调制解调器的IP地址

然后重启网络生效:

1
2
ifdown eth0
ifup eth0

如果要重新启用DHCP服务自动获取IP地址,则恢复回/etc/network/interfaces原来状态,然后重启网络即可

手动配置网络,设为静态IP后,将无法获得完整的DNS解析服务,即无法将网址解析为IP,这个时候需要手动修改设置DNS服务器,即修改/etc/resolv.conf,按照以下格式写入域名与IP的对应信息:

1
nameserver 8.8.8.8

无线网络

通过终端接入无线网路

扫描周边的无线接入点:

1
$ iwlist scan

用iwconfig检查当前网络状态。和ifconfig不同,iwconfig专门为无线网路而设计

  • 网路设备连接名称:树莓派默认的无线连接名称为wlan0
  • 无线标准:无线网卡支持的无线标准,图中支持的标准为IEEE 802.11bgn
  • ESSID:无线网卡连接到的网络名称。如果未连接,则显示为off/any
  • Mode:网卡当前模式。可选模式有:
    • Managed:常规模式
    • Ad-Hoc:设备到设备无线网络
    • Monitor:监听网络传输的特殊模式,常用于网络查错
    • Repeater:增强网络信号的中继模式
    • Secondary:Repeater模式的一种,此时网卡作为备用中继器使用

将树莓派连如无线网络,需要在/etc/network/interfaces文件中加入以下几行:

1
2
3
4
auto wlan0 # 这一行是设定开机启动wifi连接,非必须项
iface wlan0 inet dhcp
# 指定配置文件,如果已经存在,则指向它,如果不存在则先指向一个空文件,随后再创建该文件
wpa-conf /etc/wpa.conf

接着编辑wpa-conf指定的配置文件,在文件中添加以下要接入的无线网络的信息:

1
2
3
4
5
network={
ssid="your_ssid"
key_mgmt=WPA-PSK
psk="your_passwd"
}

然后连接无线网络。

1
2
ifdown wlan0
ifup wlan0

接入外网

我们的目的是将它作为一台永久运行的个人服务器,随时随地可以远程登录它。

要解决的问题实际上就是如何让外网访问局域网内的主机。通过一根网线把一台路由器和外网相连,路由器发射信号,手机、电脑等设备通过有线或无线的方式连接路由器,这些设备与路由器构成一个局域网。路由器与外网直接相连,被分配了一个公网IP,所以可被外网中的设备检测到,而局域网内的设备被分配的IP是由路由器生成的,不能被外网的设备检测到,所以外网设备无法访问在内网中搭建的服务器。

简单来说,就是对外网设备来说,有公网IP的路由器可见,而无公网IP的路由器内网不可见。解决方法就是:利用路由器的虚拟服务器功能将内网的服务器端口映射到路由器的公网IP上,这样外网设备通过路由器的公网IP找到了路由器,然后再根据端口找到了连接在该路由器下的内网服务器,从而实现服务器对外开放。

1.路由器静态分配内网IP

不同的路由器,设置方法会有差异,我的路由器为MERCURY(水晶)。

设置静态分配。添加一个静态分配的规则,添加树莓派的MAC地址和静态分配的IP(要在局域网的网段,通常是192.168.1.XXX,而且不要在路由器的地址池范围内),我使用的是192.168.1.20.

2.外网IP和端口映射到内网

如果想要从外网登陆的话,还需要做一步映射。因为路由器分出了很多ip,如果外网访问某一端口的话,到底是访问了哪一个ip呢?

这时要添加映射规则。此处添加服务器的IP地址、端口以及映射的协议类型,请根据您的服务器实际端口填写。

内网ssh服务器的端口为22,所以内部端口应该设为22。 而对于外部端口的设置,由于部分运营商可能会屏蔽80等常用端口,导致无法访问对应服务器,所以建议修改9000以上,外网用户使用修改后的服务端口访问服务器。实际中我将外部端口与内部端口都设为22。

路由器设置好后,需要重启路由器才能让刚才的设置生效。

这样就可以用 路由器IP + 映射对应端口 在公网中成功访问局域网服务器了。

3. 向外网开放多个端口

内网服务器的一个端口对应着一种服务,比如如果想向外网开放ssh服务,则用ssh的22端口做端口映射,实现对外开放ssh服务;如果想开放Web服务,则需要用Web服务器的80端口做端口映射。

如果只用22端口做端口映射,对外开放的只是ssh服务器服务,而无法获取Web服务。

但是如果想同时对外网开放ssh服务和Web服务,怎么办呢?同时开启两个端口映射任务,将两个端口同时映射到同一个内网服务器IP:

这样,就可以分别用 路由器IP:9000 访问Web服务器,用 路由器IP:22 访问SSH服务器

用户与权限管理

创建新用户

先创建一个用户应该归属的组:

1
$ sudo groupadd Guest

创建一个归属于Guest组的新用户:

1
2
$ sudo useradd -d /home/guest1 -m -s /bin/sh guest1
$ sudo passwd guest1

useradd选项

选项 助记 说明 文件及字段
-c Comment 注释信息 /etc/passwd, 5
-d Directory 账户的家目录 /etc/passwd, 6
-e Expire 账号终止日期(YYYY-MM-DD) /etc/shadow, 8
-f 帐号过期几日后永久停用 /etc/shadow, 7
-g Gid 初始默认组 /etc/passwd, 4
-G Groups 附属次要组 /etc/group, 4
-m 家目录不存在时自动创建
-M 强制不创建家目录
-s Shell 默认 shell /etc/passwd, 7
-u Uid 指定用户 UID /etc/passwd, 3

SUDOERS(sudo用户)

树莓派上默认的pi用户是一个sudoer(sudo用户)。这种类型的用户在命令行上输入命令之前先输入sudo就能拥有root权限执行命令,也能够通过sudo su完全切换到root用户。

添加一个用户到sudoer用户组,需要使用一个sudo用户输入sudo visudo命令打开配置文件,找到文件中# User privilege specification下面的root ALL=(ALL:ALL) ALL。复制这一行并将其中的root替换为对应的用户名。为了免验证密码使用root权限,修改为NOPASSWD:ALL。下面的例子给予了bob用户免密码访问sudo权限:

1
2
3
# User privilege specification  
root ALL=(ALL:ALL) ALL
bob ALL = NOPASSWD: ALL

保存并退出以完成修改。千万小心,因为这些操作可能会意外移除你自己的sudo权限。

搭建Web服务器

LAMP

大部分的现代Web服务器是运行在Linux操作系统、Apache、MySQL和PHP的一个组合,简称LAMP

  • Linux 底层操作系统
  • MySQL 后台数据库
  • Apache Web服务器
  • PHP 动态网页脚本语言

安装LAMP

1
$ sudo apt-get install apache2 php5 php5-mysql mysql-server

MySQL在安装过程中会要求你输入密码,然后才继续安装。

测试Apache是否安装成功。如果你和你的Web服务器处于同一个局域网内,则可以在浏览器中输入Web服务器的IP,就会进入Apache的默认界面

Web服务器的默认工作目录由 /etc/apache2/sites-enabled/*.conf 配置文件设定,可以修改该文件中的 DocumentRoot 一项来自定义工作目录,修改后需要重启服务器才能生效。

接着确认PHP脚本模块是否在Apache服务器正确装载,在Web服务器的默认工作目录下创建一个phptest.php文件: <?php phpinfo(); ?>。然后在浏览器中访问该php文件:http://ip/phptest.php

WordPress

WordPress是目前世界上最流行的开源博客平台之一。WordPress建立在PHP和JavaScript的基础上,并提供一个美观的、用于创建各种网站的、基于Web的界面。

安装:

1
$ sudo apt-get install wordpress

为了让Apache服务器能找到需要的文件,需要将WordPress的安装目录 /usr/share/wordpress 链接到Apache的工作目录下。

1
$ sudo ln -s /usr/share/wordpress /var/www/wordpress

接下来,使用下面一行命令运行WordPress的MySQL配置脚本:

1
2
$ sudo gunzip /usr/share/doc/wordpress/examples/setup-mysql.gz
$ sudo bash /usr/share/doc/wordpress/examples/setup-mysql -n wordpress localhost

这个命令添加了一个新的数据库到MySQL,以提高WordPress使用的数据库。此数据库存储你的用户账户、文章、评论和其他相关信息。

一旦这个脚本完成,你将被告知在浏览器中访问 http://localhost 来继续完成安装。这个地址其实不完整,应该是 http://localhost/wordpress。你也可以在内网中的另外一台计算机的浏览器地址栏输入 http://raspi-ip/wordpress

将Web服务器接入外网

将树莓派Web服务器接入外网的方法与 上文 一样。

不过

Python编程

贪食蛇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/user/bin/env python

import pygame, sys, time, random
from pygame.locals import *

# 启动pygame
pygame.init() # pygame初始化
fpsClock = pygame.time.Clock() # 控制游戏的速度

# 创建一个pygame游戏显示层
playSurface = pygame.display.set_mode((640, 480))
pygame.display.set_caption('Raspberry Snake')

# 定义一些颜色
redColour = pygame.Color(255, 0, 0)
blackColour = pygame.Color(0, 0, 0)
whiteClour = pygame.Color(255, 255, 255)
greyColour = pygame.Color(150, 150, 150)

# 初始化一些程序中用到的变量
snakePosition = [100,100]
snakeSegments = [[100,100],[80,100],[60,100]]
raspberryPosition = [300,300]
raspberrySpawned = 1
direction = 'right'
changeDirection = direction

# 定义游戏结束game over时的任务:用大号字体将“Game Over”打印在屏幕上,停留5秒,然后退出
def gameOver():
gameOverFont = pygame.font.font('freesansbold.ttf', 72)
gameOverSurf = gameOverFont.render('Game Over', True, greyColour)
gameOverRect = gameOverSurf.get_rect()
gameOverRect.midtop = (320, 10)
playSurface.blit(gameOverSurf, gameOverRect)
pygame.display.flip()
time.sleep(5)
pygame.quit()
sys.exit()

while True:
# 获取用户的键盘输入,用小键盘或WASD键来输入方向
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_RIGHT or event.key == ord('d'):
changeDirection = 'right'
if event.key == K_LEFT or event.key == ord('a'):
changeDirection = 'left'
if event.key == K_UP or event.key == ord('w'):
changeDirection = 'up'
if event.key == K_DOWN or event.key == ord('s'):
changeDirection = 'down'
if event.key == K_ESCAPE:
pygame.event.post(pygame.event.Event(QUIT))

# 判断用户输入方向的合法性,若合法则更新当前运动方向
if changeDirection == 'right' and not direction == 'left':
direction = changeDirection
if changeDirection == 'left' and not direction == 'right':
direction = changeDirection
if changeDirection == 'up' and not direction == 'down':
direction = changeDirection
if changeDirection == 'down' and not direction == 'up':
direction = changeDirection

# 蛇运动,即更新蛇头位置
if direction == 'right':
snakePosition[0] += 20
if direction == 'left':
snakePosition[0] -= 20
if direction == 'up':
snakePosition[1] -= 20
if direction == 'down':
snakePosition[1] += 20

# 使蛇身体增长,若未吃到草莓则去掉尾,若吃到草莓则保留尾,实现蛇的运动
snakeSegments.insert(0,list(snakePosition))
if snakePosition[0] == raspberryPosition[0] and snakePosition[1] == raspberryPosition[1]:
raspberrySpawned = 0
else:
snakeSegments.pop()

# 若草莓被吃掉,则在随机位置再生成一个草莓
if raspberrySpawned == 0:
x = random.randrange(1,32)
y = random.randrange(1,24)
raspberryPosition = [int(x*20),int(y*20)]
raspberrySpawned = 1

# 在界面上画东西
playSurface.fill(blackColour) # 填充背景为黑色
for position in snakeSegments: # 画蛇
pygame.draw.rect(playSurface,whiteColour,Rect(position[0], position[1], 20, 20))
pygame.draw.rect (playSurface,redColour,Rect(raspberryPosition[0], raspberryPosition[1], 20, 20)) # 画草莓
pygame.display.flip() # 更新界面,呈现新的画面

# 判断蛇是否出界
if snakePosition[0] > 620 or snakePosition[0] < 0:
gameOver()
if snakePosition[1] > 460 or snakePosition[1] < 0:
gameOver()

# 判断蛇是否碰到自己
for snakeBody in snakeSegments[1:]:
if snakePosition[0] == snakeBody[0] and snakePosition[1] == snakeBody[1]:
gameover()

fpsClock.tick(30) # 控制游戏速度

网络连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/env python
# IRC Channel Checker, written for the
Raspberry Pi User Guide by Tom Hudson

import sys, socket, time

# IRC状态码
RPL_NAMREPLY = '353'
RPL_ENDOFNAMES = '366'

# 设置连接服务器需要的变量
irc = {
'host' : 'chat.freenode.net',
'port' : 6667,
'channel' : '#raspiuserguide',
'namesinterval' : 5
}

# 设置存储用户信息的变量
user = {
'nick' : 'botnick',
'username' : 'botuser',
'hostname' : 'localhost',
'servername' : 'localhost',
'realname' : 'Raspberry Pi Names Bot'
}

# 创建一个socket对象,提供程序所需的网络连接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接IRC服务器
print 'Connecting to %(host)s:%(port)s...' % irc
try:
s.connect((irc['host'], irc['port']))
except socket.error:
print 'Error connecting to IRC server %(host)s:%(port)s' % irc
sys.exit(1)

# 向IRC服务器发送用户的身份信息,来鉴定用户身份
s.send('NICK %(nick)s\r\n' % user)
s.send('USER %(username)s %(hostname)s %(servername)s :%(realname)s\r\n' % user)
s.send('JOIN %(channel)s\r\n' % irc)
s.send('NAMES %(channel)s\r\n' % irc)

# 从socket获取数据
read_buffer = '' # 初始化缓冲区
names = []
while True:
read_buffer += s.recv(1024) # 一次性读入1kb数据
lines = read_buffer.split('\r\n') # 以\r来分割字符串
read_buffer = lines.pop(); # 保留缓冲区中的最后一行,可能为不完整行
for line in lines: # 从这些行中提取需要的信息
response = line.rstrip().split(' ', 3)
response_code = response[1]
if response_code == RPL_NAMREPLY:
names_list = response[3].split(':')[1]
names += names_list.split(' ')
if response_code == RPL_ENDOFNAMES:
# Display the names
print '\r\nUsers in %(channel)s:' % irc
for name in names:
print name
names = []

# 设置每个请求之间的等待间隔,防止IRC服务器在短时间内收到太多的请求而导致被强制断开连接
time.sleep(irc['namesinterval'])
s.send('NAMES %(channel)s\r\n' % irc)

参考资料:

(1) 树莓派用户指南(第3版)

(2) [MW315R V1] 如何设置虚拟服务器?

(3) 树莓派用户管理