1 网络连接环境搭建
1.1 配置Ubuntu虚拟机网络
1.1.1 Windows下使用VMware安装Ubuntu虚拟机
安装方法可在网络上搜索。参考链接
为提升虚拟机的运行速度,尽量安装无桌面版本的ubuntu镜像,下载地址Index of /ubuntu-releases/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror,下载 live-server
版
注意事项:
1、Windows下有很多虚拟机软件,目前市面上流行的有VMware和VirtualBox。VMware分为收费专业版Workstation Pro和非商用免费版Workstation Player。Workstation Player 只能开启一个虚拟机,而且非常卡。
2、安装完VMware后需重启电脑进入BIOS,开启Virtualization选项。 参考链接
1.2.3 虚拟机与主机之间的网络配置
(1)VMware中的虚拟机网络适配器选择,如果开发板可以有独立IP地址,建议主机和虚拟机之间使用NAT模式;不然则需使用桥接模式。
(2)如果选择桥接模式,在虚拟网络编辑器的桥接模式中选择自动,如果是主机连接的是无线网络,需选择主机正在使用的无线网卡。
(3)使用ifconfig查看虚拟机是否成功获取IP。验证主机和虚拟机是否在同一网段、是否可以ping通。
注意事项:
(1)关于VMware的三种网络连接方式的介绍:《VMware网络连接方式》
(2)NAT模式也可以实现主机和虚拟机联通。使用NAT模式的情况下,开发板需要有自己的真实IP,才能与虚拟机通讯。
1.2 配置开发板网络
1.2.1 有独立IP地址
将开发板接上网线。通过串口连接到开发板,配置IP地址、掩码、网关、DNS服务器,新建并编辑 /etc/systemd/network/eth1.network
[Match]
Name=eth1
[Network]
Address=172.16.4.111/16
Gateway=172.16.0.254
DNS=202.102.128.68
重启systemd-networkd服务
sudo systemctl restart systemd-networkd
注意事项:
建议系统中只启用一种网络管理服务,多种网络管理服务可能会发生冲突。上述使用的
systemd-networkd
,如果有其他的可以禁用,如ifupdown
/connmanctl
等
1.2.2 没有独立IP地址
将开发板和电脑主机用网线连接,使其在同一局域网内,通过串口连接配置好IP地址即可以联通。主机可通过无线或其他网口访问外网,开发板的网络可由主机网线或OTG口转发。
1.3 远程连接
1.3.1 串口连接开发板
使用USB转串口连接开发板和主机,通过主机的设备管理器确认连接的端口号,使用MobaXterm、Xshell或SecureCRT等工具与开发板建立串口连接。
1.3.2 ssh远程连接
(1)确认虚拟机和开发板中是否安装openssh,安装命令(开发板需先用串口连接之后才能设置):(sshpass
来自动处理密码输入,这对自动化脚本非常有用)
sudo apt-get install openssh-server sshpass
(2)主机上使用MobaXterm、Xshell或SecureCRT等工具与虚拟机和开发板建立远程SSH连接;
1.4 文件共享
1.4.1 Samba实现主机与虚拟机之间共享文件
(1)安装samba
sudo apt-get install samba
(2)创建共享文件夹,并添加权限
mkdir ~/smb_share
chmod 777 ~/smb_share
(3)管理员权限修改配置文件/etc/samba/smb.conf
sudo vi /etc/samba/smb.conf
快捷键shift+g跳转到文尾,添加配置如下:
[share]
# 说明
comment = share folder
# 共享的samba目录
path = /home/xud/smb_share/
# 允许访问
available = yes
# 可以浏览
browseable = yes
# 公开
public = yes
# 可写
writable = yes
# 当外部创建新文件时的权限
create mask = 0777
directory mask = 1777
valid users = xud
(4)设置samba的用户密码
sudo smbpasswd -a xud
(5)重启samba服务器
sudo service smbd restart
(6)在windows上映射网络驱动器,输入“\虚拟机Ubuntu的IP\samba配置的共享名称”,点击完成后,输入用户名和设置的密码即可连接。
1.4.2 NFS实现虚拟机与开发板之间共享文件
(1)安装NFS服务
sudo apt-get install nfs-kernel-server
(2)添加NFS服务导出的工作目录,使用管理员权限用vi编辑
sudo vim /etc/exports
将以下信息添加到文件中,其中[nfs_share_dir]为要共享的文件夹
[nfs_share_dir] *(rw,nohide,insecure,no_subtree_check,async,no_root_squash)
(3)重启NFS服务
sudo service nfs-kernel-server restart
(4)可以在ubuntu中挂载目录到/mnt,测试NFS服务
sudo mount -t nfs 127.0.0.1:<nfs_share_dir> /mnt
(5)将nfs_rootfs文件夹挂载到开发板的指定目录。最后在虚拟机<nfs_share_dir>文件夹下创建文件,到开发板中<mount point>
目录验证一下。
确保你的系统已安装 NFS 客户端工具。在 Ubuntu 或 Debian 系统上,你可以使用以下命令来安装这些工具:
sudo apt update
sudo apt install nfs-common
mount -t nfs -o nolock <ip_addr>:<nfs_share_dir> <mount point>
其中[ip_addr]为虚拟机的IP地址,但如果虚拟机与主机之间通过NAT模式连接,则需要将虚拟机的nfs端口(一般为2049)映射到主机,通过主机的端口访问虚拟机的nfs,上述[ip_addr]即为主机的IP地址。端口映射的设置如下:
(6)开发板设置为开机自动挂载nfs目录,打开 /etc/fstab
文件,在末行添加
<ip_addr>:<nfs_share_dir> <mount point> nfs defaults 0 0
1.4.3 FTP方式实现主机与虚拟机之间文件传输
可使用FileZilla工具
1.5 使用VSCode的远程开发扩展进行远程开发
因为虚拟机不便于直接在上面进行开发,开发时可以选择在Windows主机上使用网络驱动器映射功能将Ubuntu开发机的Samba共享映射为一个驱动器,通过VSCode等软件编辑代码,通过MobaXterm等软件SSH远程连接进行编译、安装等操作。这样做有几个缺点:
- 代码需要依赖交叉编译工具链和其他库文件进行开发,配置了复杂的开发环境或依赖项。需要将这些也都放入Samba共享才能使VSCode在开发时不报错。
- 不能直接在VSCode上进行编译操作,需要切换到其他的SSH远程连接软件
比较便捷的方式是使用VSCode 的远程开发扩展通过 SSH 连接到 Ubuntu 虚拟机,进行开发和编译。这种方式可以避免复杂的文件共享设置,同时提供更加直接和高效的开发环境
使用 VSCode 的 Remote - SSH 扩展进行远程开发是一种非常有效的方式,特别是当你想在本地机器上开发但需要在远端服务器(例如 Linux 服务器)上运行和测试代码时。这个扩展允许你通过 SSH 直接连接到远端服务器,并在那里打开、编辑、运行和调试代码。以下是设置 VSCode Remote - SSH 进行远程开发的详细步骤:
-
步骤 1: 安装 Remote - SSH 扩展
- 打开 Visual Studio Code。
- 转到左侧工具栏,点击「扩展」(Extensions,快捷键是 Ctrl+Shift+X)。
- 在搜索栏中输入 “Remote - SSH”。
- 找到扩展,然后点击「安装」(Install)。
-
步骤 2: 设置 SSH 连接
-
打开命令面板:
- 使用快捷键 Ctrl+Shift+P 或 F1 打开命令面板。
- 输入
Remote-SSH: Open Configuration File
并选择该命令。
-
选择 SSH 配置文件:
- VSCode 会提示你选择一个配置文件来编辑,通常位于用户的
.ssh
目录下的config
文件。如果不存在,VSCode 可以为你创建一个。
- VSCode 会提示你选择一个配置文件来编辑,通常位于用户的
-
编辑 SSH 配置文件:
-
在打开的配置文件中,添加一个新的主机配置块,如下所示:
Host my-remote-server HostName example.com User your-username IdentityFile ~/.ssh/id_rsa
-
这里:
-
Host
是你为这个连接起的别名。-
HostName
是远程服务器的 IP 地址或域名。 -
User
是你的远程服务器用户名。 -
IdentityFile
是你的私钥文件路径,用于 SSH 登录(可选,如果你使用密码登录则不需要此行)。使用私钥时需要使用以下命令将私钥安装到远程服务器ssh-copy-id username@remote-host
-
-
-
-
-
步骤 3: 连接到远程服务器
- 打开命令面板(Ctrl+Shift+P 或 F1)。
- 输入
Remote-SSH: Connect to Host
并选择该命令。 - 选择你刚才设置的主机(例如
my-remote-server
)。 - 如果一切设置正确,VSCode 将通过 SSH 连接到远程服务器。你可能需要输入密码或确认安全性(如果使用密码登录)。
-
步骤 4: 开始远程开发
- 一旦连接成功,VSCode 的界面会更新为显示远程服务器的文件系统。你可以直接在远程服务器上打开、编辑文件,运行和调试程序就如同在本地机器上一样。
-
步骤 5: 管理 SSH 会话
- 使用 Remote - SSH 扩展,你可以随时断开连接并重新连接。VSCode 还支持保存多个远程服务器配置,使切换和管理多个远程环境变得简单。
1.6 windows主机下常用开发工具
(1)虚拟化工具:VMware、VirtualBox等
(2)源代码阅读:SourceInsight、VS Code等
(3)SSH远程连接:MobaXterm、Xshell、SecureCRT等
(4)文件传输:FileZilla等
(5)其他工具:Git、Notepad++等
2 编译环境搭建
2.1 安装交叉编译工具链
直接从Ubuntu的软件包管理器安装Linaro工具链。打开终端并运行以下命令:
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf gcc-arm-none-eabi
这些命令会安装针对ARMv7使用硬浮点的交叉编译器
- gcc/g++:
gcc
指 C 语言编译器,g++
指 C++ 语言编译器。 - arm:指的是目标编译平台的 CPU 架构,ARM 是一种广泛使用的处理器架构,特别是在嵌入式系统和移动设备中。
- linux/none:linux表明这个交叉编译器是用来编译运行在 Linux 操作系统上的程序,none表示其编译无操作系统的程序。
- gnueabihf:
- gnu:代表编译器使用了 GNU 的 libc(即 glibc),这是 GNU 项目的 C 标准库,为程序提供系统级别的 API 接口。
- eabi:代表嵌入式应用二进制接口(Embedded Application Binary Interface)。EABI 定义了在软件和操作系统之间的接口,旨在优化嵌入式环境下的软件性能和效率。
- hf:代表“硬浮点”(hard float),意味着这个编译器生成的代码使用硬件浮点运算单元进行浮点数计算,而不是通过软件模拟。硬浮点运算通常比软浮点(软件模拟的浮点运算)速度更快,更适合性能要求较高的应用。
2.2 安装其他辅助开发工具
sudo apt-get install make cmake gcc g++ bison flex libssl-dev dpkg-dev lzop libncurses-dev
- make:一个自动化构建工具,用来控制可执行文件的编译。它通过读取名为
Makefile
的文件来执行指定的编译指令,自动化构建过程。 - cmake:提供了跨平台构建和高级项目管理功能,用于生成标准构建文件,如 Unix 的 Makefile 或 Windows Visual Studio 的项目文件。它比
make
提供了更高级的配置功能。 - gcc/g++:
gcc
和g++
都是 GNU Compiler Collection (GCC) 的一部分,它们是用于编译不同编程语言的编译器。gcc
被用作编译 C 程序,也可以编译 C++ 程序,但编译 C++ 程序时可能需要手动链接 C++ 标准库。g++
专门用于编译 C++ 程序,并自动处理 C++ 程序的链接需求。 - 编译Uboot/Kernel/设备树的工具:
- bison:一个语法分析器生成器,它通常用于编译器的构建过程中。给定一组语法规则,
bison
能生成一个语法分析器,这个分析器可以识别符合这组规则的语言。 - flex:一个快速词法分析器生成器,用于生成程序的词法分析部分,即将输入文本分解成一系列的标记。
- libssl-dev:包含开发使用 OpenSSL 的应用所需的开发库和头文件。OpenSSL 是一个强大的安全库,提供了加密、解密、证书管理等功能。
- dpkg-dev:包含了 Debian 包开发的一些工具,这些工具用于创建、构建和上传 Debian 软件包。
- lzop:是一个快速的文件压缩程序,使用 LZO 压缩算法。它主要用于那些更重视压缩和解压速度而不是压缩率的场景。
- libncurses-dev:提供了开发使用
ncurses
库的应用所需的开发文件。ncurses
库支持字符界面下的图形界面编程,允许开发者在文本终端上创建用户界面。
- bison:一个语法分析器生成器,它通常用于编译器的构建过程中。给定一组语法规则,
3 调试环境搭建
3.1 使用gdb-multiarch调试
3.1.1 在开发机安装 gdb-multiarch
gdb-multiarch
支持对多种处理器架构的程序进行调试,是进行交叉调试的理想工具。
sudo apt-get install gdb-multiarch
3.1.2 在目标设备安装 gdbserver
可以通过包管理器进行安装:
sudo apt-get install gdbserver
3.1.3 启动调试
配置gdb: /etc/gdb/gdbinit
# 设置远程目标的架构 ARM
set architecture arm
# 设置 sysroot,这对于确保 GDB 能找到远程目标上相应的库文件非常有用
set sysroot /home/xud/my_software/mhgateway1.0
# 指定与远程目标相匹配的本地可执行文件,可在运行gdb-multiarch时指定
# file /path/to/your/local/executable
# 设置远程调试的目标地址和端口
target remote 172.16.4.111:2000
在目标设备上运行GDB服务器
在目标设备上,选择一个端口(例如 2000
)来运行 gdbserver
,并等待来自GDB的连接。同时指定需要调试的程序:
gdbserver :2000 ./my_program
这条命令会在目标设备上启动 gdbserver
,监听端口 2000
,等待远程GDB的连接,并准备调试 ./my_program
。
在开发PC上启动GDB
在开发PC上,使用GDB连接到目标设备上的GDB服务器。如果使用 gdb-multiarch
,可以这样做:
gdb-multiarch <local_program>
<local_program>为与远程目标相匹配的本地可执行文件
4 构建脚本
#!/bin/bash
smb_share_dir=/home/$USER/smb_share
smb_password=smbpass
dev_ip=172.16.4.139
nfs_share_dir=/home/$USER/nfs_share
board_ip=172.16.4.111
mount_point=/mnt/nfs_share
ssh_password=root
gdb_sysroot_dir=/home/$USER/mhgateway1.0
# 检查结果
function check_result()
{
if [ $1 -eq 0 ]; then
echo "$2 completed successfully."
else
echo "Error occurred during $2."
exit 1
fi
}
# 设置 Samba 用户密码并重启服务
function set_and_restart_samba() {
# 设置 Samba 用户密码
if ! echo -e "$smb_password\n$smb_password" | sudo smbpasswd -a $USER > /dev/null; then
echo "Failed to set Samba password."
exit 1
fi
# 重启 Samba
if ! sudo service smbd restart; then
echo "Failed to restart Samba."
exit 1
fi
}
# 配置 Samba
function configure_samba() {
# 检查 /etc/samba/smb.conf 是否已包含 [share] 配置
if grep -q "\[share\]" /etc/samba/smb.conf; then
echo "Samba is already configured. "
echo "Access address: //$(hostname -I | awk '{print $1}')/share"
read -p "Do you want to reconfigure Samba? (y/n): " response
if [ "$response" != "y" ]; then
echo "Skipping Samba configuration."
return
fi
fi
# 配置或更新 Samba 设置
# read -p "Please a path relative to your home directory for Samba share (e.g., smb_share): " smb_share
# read -p "Please enter the Samba password for user $USER: " samba_password
mkdir -p "$smb_share_dir" && chmod 777 "$smb_share_dir"
# 检查是否已配置 Samba
if grep -q "\[share\]" /etc/samba/smb.conf; then
# 更新现有的 [share] 配置
echo "Updating settings for existing Samba share..."
sudo sed -i "/^\[share\]$/,/^\[/!b; /^ path =/c\ path = $smb_share_dir" /etc/samba/smb.conf
sudo sed -i "/^\[share\]$/,/^\[/!b; /^ valid users =/c\ valid users = $USER" /etc/samba/smb.conf
else
# 添加完整的 [share] 配置
echo "Adding new Samba share configuration..."
echo "[share]
comment = Share folder
path = $smb_share_dir
available = yes
browseable = yes
public = yes
writable = yes
create mask = 0777
directory mask = 1777
valid users = $USER" | sudo tee -a /etc/samba/smb.conf
fi
set_and_restart_samba
check_result $? "Samba configuration"
}
# 将公钥添加到目标板
function add_public_key_if_needed() {
# 检查密钥文件是否已存在
if [ ! -f ~/.ssh/id_rsa.pub ]; then
echo "SSH key does not exist. Generating a new SSH key..."
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa.pub > /dev/null
echo "New SSH key generated."
else
echo "SSH key already exists. Using existing key..."
fi
# 将公钥添加到目标板
if sshpass -p "$ssh_password" ssh-copy-id -o LogLevel=ERROR "root@$board_ip" > /dev/null; then
echo "Public key added successfully."
else
echo "Failed to add public key."
exit 1
fi
}
# 尝试能否连接到ssh
function try_to_ssh_link() {
# 尝试无密码SSH连接
if ! ssh -q -o PasswordAuthentication=no "root@$board_ip" "exit"; then
# 如果连接失败,提示用户输入密码
echo "SSH without password failed. Trying with password."
# read -sp "Enter SSH password for root@$board_ip: " ssh_password
# echo ""
# 尝试使用密码进行连接
if ! sshpass -p $ssh_password ssh -q "root@$board_ip" "exit"; then
echo "Failed to connect to the target board with password."
exit 1
fi
add_public_key_if_needed
if ! ssh -q -o PasswordAuthentication=no "root@$board_ip" "exit"; then
echo "SSH without password failed. Public key is already added."
exit 1
fi
fi
}
# 配置目标板的nfs自动挂载
function configure_nfs_mount() {
if ssh -q root@$board_ip "
sudo sed -i '/ nfs /d' /etc/fstab > /dev/null # 删除关于nfs的现有配置
echo '$dev_ip:$nfs_share_dir $mount_point nfs defaults 0 0' | sudo tee -a /etc/fstab > /dev/null # 添加新的NFS挂载配置
if [ ! -d "$mount_point" ]; then
sudo mkdir -p $mount_point
fi
"; then
return
else
echo "Failed to update NFS configuration on target board."
exit 1
fi
}
# 配置或更新 NFS 设置
function configure_nfs() {
# 检查是否已配置 NFS
# 读取 /etc/exports 文件并提取已配置的 NFS 共享路径
configured_paths=$(awk '!/^\s*#/ && NF > 0 {print $1}' /etc/exports)
if [ -z "$configured_paths" ]; then
# 如果没有配置或文件全是注释
echo "No NFS shares configured."
else
# 如果存在已配置的路径
echo "Already configured NFS share paths include:"
echo "$configured_paths"
echo "If you want to add new configurations, continue to configure. To delete shares, please edit /etc/exports file."
# 询问用户是否要添加新的配置
read -p "Do you want to add a new NFS configuration? (y/n): " response
if [ "$response" != "y" ]; then
echo "Skipping NFS configuration."
return
fi
fi
# read -p "Please enter a path relative to your home directory for NFS share (e.g., nfs_share): " nfs_share_dir
# 检查 /etc/exports 文件是否已包含 nfs_share_dir 的配置
if grep -qE "^${nfs_share_dir}( |$)" /etc/exports; then
echo "NFS share configuration for $nfs_share_dir already exists in /etc/exports."
else
# 如果不存在,则创建目录
sudo mkdir -p "$nfs_share_dir"
# 添加 NFS 共享配置到 /etc/exports
echo "$nfs_share_dir *(rw,nohide,insecure,no_subtree_check,async,no_root_squash)" | sudo tee -a /etc/exports > /dev/null
# 重启 NFS 服务器以应用更改
sudo service nfs-kernel-server restart > /dev/null
check_result $? "NFS share configuration for $nfs_share_dir and NFS server restarted."
fi
# 询问用户是否要配置目标板的NFS
read -p "Do you want to configure NFS mount on the target board? (y/n): " response
if [ "$response" == "y" ]; then
# 配置目标板的 NFS 挂载点
# 获取目标板的 IP 地址/挂载点,开发设备的IP
# read -p "Enter the IP address of your development board: " board_ip
# read -p "Enter the development machine IP address: " dev_ip
# read -p "Enter the mount point of your development board: " mount_point
try_to_ssh_link
configure_nfs_mount
else
echo "Skipping configure NFS mount on the target board."
fi
check_result $? "NFS configuration"
}
# 安装目标板的gdbserver
function install_board_gdbserver() {
if ssh -q root@$board_ip "
sudo apt-get install gdbserver > /dev/null
"; then
return
else
echo "Failed to install gdbserver on target board."
exit 1
fi
}
# 配置或更新 gdb-multiarch 设置
function configure_gdb() {
GDB_INIT_FILE="/etc/gdb/gdbinit"
# 检查 gdbinit 文件是否已存在
if [ -f "$GDB_INIT_FILE" ]; then
# 文件存在,询问用户是否替换
echo "$GDB_INIT_FILE already exists."
read -p "Do you want to replace the existing gdbinit file? (y/n): " replace_response
if [ "$replace_response" != "y" ]; then
echo "Skipping gdb-multiarch configuration."
fi
fi
# 创建或替换 gdb 配置文件
cat << EOF | sudo tee $GDB_INIT_FILE > /dev/null
# 设置远程目标的架构 ARM
set architecture arm
# 设置 sysroot
set sysroot $gdb_sysroot_dir
# 设置远程调试的目标地址和端口
target remote $board_ip:2000
EOF
# 询问用户是否在目标机安装 gdbserver
read -p "Do you want to install gdbserver on the target device ($board_ip)? (y/n): " install_response
if [ "$install_response" == "y" ]; then
try_to_ssh_link
install_board_gdbserver
else
echo "Skipping configure NFS mount on the target board."
return
fi
check_result $? "GDB configuration"
# 提示用户启动 gdbserver
echo "Please run the following command on the target device:"
echo " gdbserver :2000 ./my_program"
echo "Make sure to replace './my_program' with your actual executable."
# 提示用户在开发机上启动 gdb-multiarch
echo "After starting gdbserver on the target device, run gdb-multiarch on the development machine:"
echo " gdb-multiarch /path/to/your/local/executable"
echo "Replace '/path/to/your/local/executable' with the path to your local version of the program being debugged."
echo "Setup complete. Ready to start debugging!"
}
# 更新系统包列表并安装软件包, 安装OpenSSH、Samba、安装交叉编译工具链、安装其他辅助开发工具
cd ~
# 定义需要检查的软件列表
software_list=("autoconf" "libtool" "git" "p7zip-full" \
"openssh-server" "sshpass" "samba" "nfs-kernel-server" \
"gcc-arm-linux-gnueabihf" "g++-arm-linux-gnueabihf" "gcc-arm-none-eabi" \
"make" "cmake" "gcc" "g++" "bison" "flex" "libssl-dev" "dpkg-dev" "lzop" \
"libncurses-dev" "gdb-multiarch")
echo "Software installing ..."
# 更新软件包信息(可选,根据需要取消注释)
sudo apt-get update -qq
# 检查并安装每个软件
for software in "${software_list[@]}"; do
# 使用 dpkg -l 检查软件是否已安装
if ! dpkg -l | grep -qw $software; then
echo "$software is not installed. Installing..."
sudo apt-get install -y $software -qq
fi
done
check_result $? "Software installation"
# 配置 Samba
echo ""
echo "Configuring Samba..."
configure_samba
# 配置 NFS
echo ""
echo "Configuring NFS..."
configure_nfs
# 配置 gdb-multiarch
echo ""
echo "Configuring GDB..."
configure_gdb
echo ""
echo ""
echo "All installations and configurations are complete."
echo "To access the Samba share from Windows, use:"
echo " \\\\$(hostname -I | awk '{print $1}')\\share with username: $USER and the specified password: $smb_password."
echo "To mounte the nfs share from your development board, use: "
echo " mount nfs dir: 'sudo mount -a' show nfs dir: 'cd $mount_point'"