关于嵌入式开发的一些信息汇总:开发模型以及自托管开发(二)
关于嵌入式开发的一些信息汇总:开发模型及自托管开发(二)
这篇文章是关于嵌入式开发的一些基本信息,供想入行的人参考。有一些作者本人的想法,以及来自外网的大拿的文章翻译而来,原文链接在此Learning Linux for embedded systems,再次感谢,支持原创。
2 自托管开发
自托管开发是在主机系统上开发的一个分支,类似于我们在本系列前几期中对 VM 安装所做的工作,但是也有一些差异,因为目标系统与大多数嵌入式系统一样,内存有限且处理器相对较慢。
2.2 构建 Raspberry Pi 内核
首先我们将在 Raspberry Pi 上构建内核,然后在运行速度更快的主机系统上构建它。使用单独的主机进行开发是交叉开发模型的一个示例。
上一次在 RPi上构建内核,我们使用rpi-source 脚本下载并安装在 RPi 上构建内核模块所需的头文件。创建该脚本的同一位开发人员notro还创建了一个rpi-build 脚本,可以轻松构建内核。可以在GitHub 页面上找到说明:
$ wget https://raw.githubusercontent.com/notro/rpi-build/master/rpi-build
$ sudo mv rpi-build /usr/bin/rpi-build
$ sudo chmod +x /usr/bin/rpi-build
虽然rpi-source 脚本是用 Python 编写的,但rpi-build 脚本是用 Ruby 编写的,在我们运行它之前需要安装它:
$ sudo apt-get update
$ sudo apt-get install ruby
第一次运行rpi-build时,它将检查缺少的依赖项。当系统询问您是否要安装这些依赖项时,回答 Y。在此之后,我们可以使用此脚本构建内核:
$ rpi-build use [stdlib] linux install
需要等一段时间。在脚本下载文件并开始编译内核文件的同时,让我们启动运行 Fedora 的 VM,看看在更快的系统上构建 Raspberry Pi 内核。
我们将使用 git 源代码控制系统从 github.com 上的 Rasbberry Pi 存储库复制交叉工具链和内核。首先我们需要以root身份安装git:
$ su
密码:
# yum install git
然后我们就可以下载工具链和内核源码了:
$ git clone –depth 1 git://github.com/raspberrypi/tools.git
$ git clone –depth 1 git://github.com/raspberrypi/linux.git
让我们准备好构建 Linux 内核:
$ cd ~/linux
$ make mrproper
$ export CCPREFIX=~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-
这个指令将选择 GCC-4.8.3 的 Linaro 版本作为我们的交叉编译器。交叉编译器要在一个系统上运行,在本例中我们的 VM 在 X86-64 主机系统上运行,并生成将在不同处理器上执行的目标代码,在本例中为 ARM,特别是用于 Broadcom BCM2708 处理器树莓派。
我们将为 Raspberry Pi 使用默认的 Linux 配置:
$ cp arch/arm/configs/bcmrpi_defconfig .config
$ make ARCH=arm CROSS_COMPILE=${CCPREFIX} oldconfig
或者,我们可以从 Raspberry Pi 复制配置,它可以在/proc/config.gz中找到。使用 gunzip 解压缩此文件并将其从config重命名 为.config ,然后再运行make oldconfig ,如上所示。
执行此操作时,系统会询问您有关已添加到内核,但未在您使用的.config文件中提及的新选项。我只是为所有这些(许多)选项点击返回,选择默认值。
我们现在准备在我们的主机 X86 系统上构建 ARM 目标:
$ make ARCH=arm CROSS_COMPILE=${CCPREFIX}
$ make ARCH=arm CROSS_COMPILE=${CCPREFIX} module
在 X86 主机上构建,甚至是虚拟机,都需要不到一个小时。在 Raspberry Pi 上,内核构建大约需要 6.5 小时。
2.3 安装内核
在树莓派上,我们可以将新建的内核复制到引导目录并重命名:
#cp /linux/arch/arm/boot/Image /boot/kernel_rpi.img
在主机系统上,事情有点复杂。关闭 Raspberry Pi 并取出 SD 卡。将 SD 卡插入主机系统上的读卡器。从主机上的 SD 卡挂载引导分区。现在我们可以将在主机上构建的内核复制到 SD 卡上的正确位置。我们也可以将模块安装到临时目录中,然后将它们合并到 SD 卡上的 /lib 目录中。
$ cp ~/linux/arch/arm/boot/Image /run/media/eager/boot/kernel_host.img
$ make ARCH=arm CROSS_COMPILE=${CCPREFIX} INSTALL_MOD_PATH=~/modules modules_install
$ sudo cp -r ~/modules /lib /run/media/eager/*/lib
在我们从主机系统中取出 SD 卡并将其重新插入 Raspberry Pi 之前的最后一件事。编辑内核命令行以指定我们要启动在主机上构建的内核。在编辑器中打开/boot/config.txt 并进行以下更改:
#kernel=kernel.img
kenel=kernel_host.img
如果缺少默认的kernel=kernel.img 行,只需在文件底部添加第二行。从 SD 卡上卸载文件系统并将其从主机系统中删除。将其重新插入树莓派并打开电源。如果一切都正确完成,我们的新 Linux 内核将在 Rpi 上启动。我们可以通过查看登录时显示的构建日期来确认这一点。
2.4 总结
Raspberry Pi 是一款功能中等的单板计算机,因此可以在目标上构建内核,即使需要几个小时。构建包含所有用户库和应用程序的整个文件系统可能需要数天时间。
我们已经开始研究经常与嵌入式系统一起使用的跨平台开发。在此模型中,我们使用跨开发工具链构建强大的主机系统,为目标生成目标代码。在这个例子中,我们从目标系统中物理地移动了包含根文件系统的 SD 卡,并将其挂载到主机上。
3 连接目标板
- 当我们在另一个系统上开发时,我们必须使用交叉编译器和目标环境的副本,这使得问题有点复杂,但是交叉开发环境通常比嵌入式目标快得多,具有更多的磁盘和内存空间。
- 对于许多嵌入式 Linux 项目,根本不可能在目标上构建。
我们从开发系统中复制内核并将其写入我们插入树莓派的 SD 卡中。这行得通,但是将 SD 卡从开发系统移到目标系统,运行一些测试,然后在我们想要进行更改时将其移回,然后在我们想要测试这些更改时移回目标系统是很尴尬的。 - 令人高兴的是,Raspberry Pi 和几乎所有目标板都可以通过串行端口或以太网与外界通信。我们将使用它来将文件从开发系统传输到目标,甚至从开发主机控制目标。
3.1 Raspberry Pi 上的网络设置
RPi 的 Raspbian发行版预装了一套完整的网络工具。在 RPi 控制台上,输入命??令“ ip addr show ”,您应该会看到如下内容:
$ ip addr show
1: lo: mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever
2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether b8:27:eb:bc:d0:81 brd ff:ff:ff:ff:ff:ff inet 192.168.20.112/24 brd 192.168.20.255 scope global eth0 valid_lft forever preferred_lft forever
这就告诉了您开发板已连接到网络,并且外部连接的地址 eth0是 192.168.20.112,那是我的 DHCP 服务器分配给开发板的地址,您可能会有不同的地址。
我将以下行添加到我的开发系统上的/etc/hosts 文件中,这样我就可以使用名称“rpi”而不是使用完整的 IP 地址:
192.168.20.112 raspberrypi rasbpberrypi.eagercon.com rpi
3.2 Ssh、rsh、rlogin 和 telnet 连接到目标
1) SSH 代表 Secure Shell,它是使用加密连接连接到远程系统的标准方式。SSH 是包括ssh 和scp在内的程序的集合,它默认安装在 Raspberry Pi 上,我们将通过在我们的开发主机上输入以下命令来使用它连接到目标:
$ ssh pi@192.168.20.112
要么
$ ssh pi@rpi
系统将要求您提供用户“pi”的密码,如果你没有改变它,它就是“raspberry”。一旦接受,您将进入目标的 bash shell。您可以从这个 shell 执行从 Raspberry Pi 控制台执行的任何操作。(您还可以使用共享密钥设置目标,这样每次连接时都可以绕过密码提示。)
第一次使用 ssh 时,系统会要求您验证要连接的系统是否是您想要的系统。你可以回答“是”。如果您碰巧使用 ssh 连接到使用 Internet 的系统并且您意外收到此消息,比如在您之前验证了系统身份之后,可能有几个原因。一是你没有连接到你想要的系统,最坏的情况是,因为有人拦截了连接。更有可能的是远程系统上的密钥已重新生成。
默认情况下,ssh 启动运行 bash 的终端会话。如果在行尾添加一条命令,则该命令将在远程系统上运行,然后终止。例如:
$ ssh
pi@rpi df
pi@rpi's password: Filesystem
1K-blocks Used Available Use% Mounted onrootfs
6240824 4518044 1387688 77% //dev/root
6240824 4518044 1387688 77% /devtmpfs
219768 0 219768 0% /devtmpfs
44788 224 44564 1% /runtmpfs
5120 0 5120 0% /run/locktmpfs
89560 0 89560 0% /run/shm/dev/mmcblk0p5
57288 25688 31600 45% /boot$
如果您在 ssh 命令上指定“-X”选项,SSH 还允许您远程运行图形程序。该程序将在目标上运行,但窗口将显示在您的主机系统上。
2) RSH 是一个包括 rsh 和 rcp 程序的包。与 ssh 类似,rsh 允许您通过以太网连接到远程系统。在大多数发行版中默认情况下通常不安装 RSH 的不同之处在于,没有对您输入的数据或远程系统返回的数据进行加密。您可能会发现 rsh 是未安装 ssh 的别名。使用 rsh 连接的命令与 ssh 的连接命令类似,只是您必须在命令行选项中指定用户 ID:
$ rsh -l pi rpi
与 rsh 类似,rlogin 还允许您连接到目标:
$ rlogin -l pi rpi
3) Telnet 是一种通过串行线路或网络与另一个系统通信的旧方法。它在大多数应用程序中已失宠,因为所有通信都是纯文本,而 SSH 对所有消息进行加密。对于使用SSH连接到您的开发系统的嵌入式 Linux 系统进行开发,几乎没有理由担心有人会监视您的通信。Telnet 是一个简单得多的程序,并且由于它不必加密或解密消息,因此它使用较少的CPU 资源,这对于低性能目标可能很重要。没有足够 CPU 能力或内存来运行 SSH 的嵌入式 Linux 系统可以轻松支持 telnet。您可能必须在您的开发主机上安装 telnet 包,因为默认情况下大多数 Linux 发行版不安装Telnet。
ssh 和 telnet 都允许您从主机控制目标系统,即使是没有连接显示器或控制台的主机。或者,正如撰写本文时所发生的那样,显示器突然停止工作。
4) 使用 ssh 和 rsh 复制文件
ssh包包含一个命令 scp,它允许您在开发主机系统和远程系统之间复制文件。它的工作原理类似于 cp 命令,但允许您添加远程系统的名称。让我们将在上一期中构建的 kernel.img 文件复制到 Raspberry Pi 目标的/boot目录中:
$ cd ~/linux/arch/arm/boot
$ scp Image
pi@rpi's password:
Image 100% 6226KB 778.3KB/s 00:00
$
最后一行将在执行传输时更新,告诉您已复制了多少数据以及完成传输需要多长时间。
将文件复制到RPi 上的/tmp 后,我们可以使用我们打开的 shell 到 RPi 目标将其复制到/boot 目录:
$ sudo cp /tmp/Image /boot/kernel.img
我们无法将文件直接复制到Raspberry Pi 上的/boot ,因为它归 root 所有。安装在 Raspberry Pi 上的 Debian 发行版没有 root 密码,因此复制文件的唯一方法是使用上面显示的“sudo”命令。与 ssh 一样,数据在发送前加密,在接收端解密。特别是在低性能目标上,这会使数据传输变慢。
rsh 包中包含 rcp 命令,它与 scp 类似,只是数据未加密。这意味着它比 scp 更高效,并且可以支持更快的数据传输,即使在低性能目标系统上也是如此。您需要将其安装在 Raspberry Pi 目标上:
$ sudo apt-get install xinetd rsh-server
您还需要修改/etc/hosts.equiv 文件以包含如下一行:
$ sudo cat “ ” >> /etc/hosts.equiv
代替 和 使用您的开发系统的主机名(hostname)和您的用户 ID。
在主机系统上,我们现在可以像上面使用 scp 一样使用 rcp:
$ rcp Image rpi:/tmp
请注意,我们在使用 rcp 时不需要指定用户 ID 或输入密码。
与目标板交谈并不是世界上最令人兴奋的事情,但如果不能,这可能是最令人沮丧的事情之一。
4 应用程序开发
让我们来看看嵌入式 Linux 系统(我们的 Raspberry Pi)的软件开发。我们首先要编写传统的“Hello World”程序并在树莓派上编译,然后我们将在开发主机上做同样的事情,使用我们安装的交叉开发工具来构建 Linux 内核。
4.1 在目标板上编译
在系统控制台上登录到您的 RPi 系统,或者像我一样,在我的开发系统的窗口中使用ssh 。
在主目录下创建一个名为“projects”的目录,然后cd 进入该目录。
现在我们可以使用编辑器(vi 或 nano)创建名为“hello.c”的源文件:
#include <stdio.h>
int main (void)
{
printf ("Hello World!n");
return 0;
}
我们可以使用以下命令编译它: $ gcc -o hello hello.c
这表示运行 gcc,GNU C 编译器,使用名为“hello.c”的源文件创建一个名为“hello”的可执行程序。如果一切正常,会收到提示。与许多其他 Linux 命令一样,如果没有错误,gcc 什么也不会说。我们可以从命令行运行此命令,它将生成预期的输出。
$ ./hello Hello World!
您需要指定“hello”程序的完整或部分路径。在这种情况下,我们输入“./”表示可以在当前目录中找到该程序。如果没有指定完整路径或部分路径,bash 命令 shell 将在 $PATH 环境变量中列出的目录中搜索该程序。在这种情况下,bash 会说“找不到命令”。
这看起来很简单,但在幕后却发生了很多事情。如果将“-v”选项添加到gcc 命令,它将列出编译、汇编和链接程序所需的所有步骤。GCC 知道在哪里可以找到“stdio.h”包含文件以及包含“printf”函数和创建可执行程序所需的其他函数的库。如果您想查看 gcc 将搜索包含文件的目录,请运行以下命令:
$ cpp -Wp,-v /dev/null
您将看到,除其他外,gcc 将搜索 /usr/include 以找到 stdio.h。可以通过使用 -print-search-dirs 选项运行 gcc 来列出搜索到的库。
4.2 在主机上编译
让我们在开发系统上做同样的事情:创建项目目录并创建“hello.c”源文件。(或者,您可以使用scp 将文件从 RPi 复制到您的开发系统。)
确保您已将 PATH 环境变量设置为包括我们之前使用的 Raspberry Pi 工具链目录,“~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x86/bin”。完成此操作后,我们可以使用交叉编译器编译源文件:
$ arm-linux-gnueabihf-gcc -o hello hello.c
(如果您的手指厌倦了输入 gcc 编译器的长名称,您可以通过输入命令“ alias arm-gcc arm-linux-gnueabihf-gcc ”来创建一个较短的别名)
我们可以使用“ scp hello pi@rpi:/tmp ”将可执行文件复制到 RPi 系统上的 /tmp 目录。在 RPi 系统上,我们可以运行“ /tmp/hello ”并获得与本地编译版本相同的结果。
如果您列出 arm-gcc 将搜索的目录以查找包含文件或库,您将看到列出的是工具链目录下的目录,而不是开发系统的目录,这个很重要;
我们想为 ARM 系统使用 include 文件和库,而不是 x86 主机上使用的那些。
我们需要确保这两个环境,原生开发环境和交叉开发环境是同步的。当它们不同步时,可能是通过使用为不同版本的目标库构建的交叉编译器,或者可能是在更新目标库后没有相应更新交叉开发库,奇怪的事情就会发生。程序可能无法在目标上执行,或者运行时出现错误,或者调试时出现混乱。
4.2.1 必要的工具
开始之前,要确保在 Linux 系统上安装了所有必要的工具。需要一个编译器,即 gcc、binutils 包和一个文本编辑器或一个 IDE。要选择文本编辑器还是某种 IDE 很大程度上取决于您的偏好。根据所使用的 Linux 发行版和安装选项,您可能已经安装了必要的工具。
这里有一个小脚本来帮助您查看是否安装了所有必需的开发工具:
#!/bin/sh
gcc -v
if [ $? != 0 ]; then
echo "GCC is not installed!"
fi
ld -v
if [ $? != 0 ]; then
echo "Please install binutils!"
fi
将此脚本保存为 devtoolscheck.sh,运行它:
$ sh devtoolscheck.sh
在Fedora机器上,可以得到以下输出:
$ sh devtools.sh
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.6.1/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.6.1-4' --with-bugurl=
file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++,go
--prefix=/usr --program-suffix=-4.6 --enable-shared --enable-multiarch
[config options snipped]
Thread model: posix
gcc version 4.6.1 (Debian 4.6.1-4)
GNU ld (GNU Binutils for Debian) 2.21.52.20110606
为什么需要 binutils 二进制文件和 gcc,您很快就会看到。现在让我们稍微关注一下“编辑器与 IDE”的问题。
在这方面,我们唯一会建议您的是“使用您觉得舒服的东西,而不要理会别人告诉您的东西”。这件事是非常主观的,它取决于很多因素。例如,如果您在其他操作系统上开发(或曾经开发过),您可能会习惯使用 IDE。您会在 Linux 上找到许多不错的 IDE,包括 Eclipse、Geany、KDevelop 或 Anjuta。尝试安装它们,看看你觉得哪个更合适。另一方面,如果您想采用简单的编辑器方式,这里也有很多选项:vi(m)、emacs、kate、nano、jed 等等。通过搜索 Internet,您会发现很多关于什么是最好的编辑器的讨论。建议您安装其中的几个,然后找出最适合您的。你是唯一的判断者,它将是你经常使用的工具,所以慢慢来,使用它,阅读并熟悉它。无论您作何选择,我们都假定您已就编辑工具做出选择并且熟悉其使用。
4.2.2 编译过程
简而言之,这个过程是从您编写的源代码开始的,如果一切顺利,结果就是可执行二进制文件或库。你现在不需要记住所有的概念,随着所做的工作这些概念会逐渐清晰,在这个阶段,重要的是了解总体思路。
假设我们已经编写了源代码,现在我们希望编译器对其进行处理并为我们提供可执行二进制文件。此过程的工作流程如右图所示。
请注意,它仅适用于 C,C是一种编译语言,与解释语言(Perl、Python、Shell)相反,我们将在这份指南的其余部分严格参考 gcc 和它的相关文件。如上图所示,预处理器 (cpp) 获取您的源代码,查找预处理器指令(在 C 语言中,它们以散列开头)如果一切正常,则结果是编译器可以理解的输出。编译器 (gcc) 完成了所有艰巨的工作,包括底层硬件的代码优化(如果您对编译器理论或交叉编译感兴趣,有很多关于该主题的好书,但我们在这里假定初学者水平更高).预处理编译的结果是汇编代码,非常接近机器语言,将从中生成二进制文件(工具也是如此)。最后,根据选项和代码,“ld”会将可执行文件链接到所有必要的库,瞧!最终结果:就是你的程序。
如果您想查看所有生成的中间文件,gcc 标志 -save-temps as 将帮助您这样做。我们建议您至少简要地阅读 gcc 手册页,并确保您的编译器是最新的。
4.2.3 示例 C 程序
每个编程教程都是以“Hello, world”程序开始的。
下面这个程序除了打印“Hello, world!”之外什么都不做,信息打印在屏幕上,然后退出,用于说明程序的基本结构和一些基本概念。
#include <stdio.h>
/* This is a comment */
int main()
{
printf("Hello, world!\n");
return 0;
}
现在,让我们逐行剖析程序,看看每一行代表什么。
-
第一个是预处理器指令,它要求提供stdio.h文件,该文件提供printf函数的定义。头文件是通常包含各种定义(函数、变量……)并使 .c 文件不那么混乱的文件。源文件 (.c) 所需要的只是一条#include语句,可能还有一个链接器参数。包含的头文件中定义的所有内容都将在您的源代码中可用。
-
main()是每个 C 程序中的必备函数。顾名思义,无论您定义了多少函数,主要活动都将在这里发生。int main()意味着这个函数没有任何参数(空括号)并且它返回一个整数(初始int)。所有这些都将在后面讨论。这里最重要的是printf函数,它将我们的文本作为参数并显示它。“ \n” 表示“换行符”,相当于使用 Enter 键(或 ^M)。它被称为转义序列,C 中的所有转义序列都以“\”开头。例如,为了更好地理解转义序列是什么,假设您正在编写 HTML 代码并且您需要打印一个“<”字符。HTML 的语法使用尖括号来定义 HTML 标记,因此您的括号很可能会被解释为 HTML 代码,而不是被显示出来。那么该怎么办?我们用“<”转义它 它会正确显示。同样,如果你想插入一个换行符,你不能直接输入它,因为编译器可能不太关心你是否在一行上编写你的程序,因此你需要转义你的换行符“ \n ”。
-
return 0告诉编译器一切正常并且main()函数的执行到此结束。这是因为 0 是成功执行的代码,而大于 0 的值(整数)表示出现了问题。开始和结束 main 函数的花括号界定了它的执行块,也就是说,在 main() 中发生的事情保留在main()中。您可能已经注意到语句末尾的分号:它们是强制性的,作为当前语句在那里结束的标志,但它们不能在预处理器指令中用作#include。
4.2.4 汇编
本指南接下来的部分将更详细地讨论编译。但为了完整起见,这里有一个简单的命令行示例,说明如何编译和执行我们的第一个“Hello World”C 程序:
$ gcc -o hello hello.c
$ ./hello
Hello, world!
4.2.5 基本 I/O
无论操作系统如何,只要是某种 Unix,以下这些信息都是有效的,但是如果您偶然发现了Linux特有的东西,您就会知道。我们将处理标准输入、输出和错误、深入的 printf() 和文件访问等概念。
正如您将看到的,标准 C 库为此定义了一系列函数,并且在阅读了一些内容之后您会发现,没有它您将很难进行开发工作,除非您为了好玩而重写这些函数。最好从一开始就清楚本章所讨论的功能本身并不是 C 语言的一部分;正如我所说,标准 C 库提供了这些接口。
输入是在电传打字机上进行的(顺便说一下,设备名称 tty 就是由此而来的),这个过程缓慢而笨拙。任何类 Unix 系统仍然有一些关于 I/O 的历史遗留问题,不仅仅是 I/O,对于本文的其余部分,我们将把 stdin 视为键盘,将 stdout/stderr 视为屏幕。您知道可以使用 shell 提供的“>”运算符重定向到文件,但我们暂时对此不感兴趣。
在我们最后开始这篇文章之前,要提醒一点:Mac OS 版本 9 具有一些关于我们主题的独特功能,促使我在开始开发之前阅读一些文档。例如,在所有 Unix(类)系统上,Enter 键都会生成一个 LF(换行符)。在 Windows 上它是 CR/LF ,在 Apple 直到 Mac OS 9 上它是 CR 。简而言之,每个商业 Unix 供应商都试图通过添加功能使他们的操作系统“独一无二”。
我们在之前的文章中看过 printf() 以及如何在屏幕上打印文本。我们还看到 scanf() 是从用户那里获取文本的一种方式。对于单个字符,您可以依靠 getchar() 和 putchar()。我们现在将从标准库中包含的头文件中看到一些有用的函数。我们要讨论的第一个标头是ctype.h,它包含用于检查字符大小写或更改字符的函数。请记住,每个标准头文件都有一个手册页,解释可用的函数,而所述函数又有手册页,详细说明返回类型、参数等。
下面是一个使用 tolower() 将字符串中的每个字符转换为小写的示例。
你将如何达到相反的目的?
#include <stdio.h>
#include <ctype.h>
int main()
{
int c; /* 读取的字符 */
while ((c = getchar()) != EOF)
putchar (tolower(c));
return 0;
}
另一个问题是:应该以何种方式修改代码,以便它仅在句子后打印小写结果?也就是说,前提是句子总是以点和空格结尾。
4.2.6 在官方 Debian 存储库中获取一个包
gcc 手册很大而且肯定会让人头疼,但是当你遇到问题时,阅读手册,然后先在网上搜索才是正确的方法 ,没有例外。
要进入实际操作,需要的第一个工具显然是编写程序所用语言的编译器。
或者,如果程序是用某种解释语言编写的,请确保解释器(Perl, Python、Ruby…) 作为依赖存在。
我们将专注于 C 方面,因为这毕竟是一个 C 开发文章系列,并且会给你一个非详尽的实用程序列表,你最好在你的开发机器上安装:
- auto tools* (autoconf, automake, …)
- debhelper and dh-make – Debian-specific
- devscripts, fakeroot – same, see the Guide for details
- a VCS of your choice, depending on the situation at hand – we prefer to take no sides here
- gnupg – for digitally signing your packages, mandatory in Debian
- lintian – the name is a combination of lint and Debian, so it’s self-explanatory
- patch - you should know by know why you’d need it
- pbuilder – for creating a chroot
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!