【教程】迅为iTOP-4412精英版exynos4412开发板搭建原生Linux最小系统(三)

上一篇文章中,我们编译了linux内核、设备树和busybox根文件系统,成功启动了我们的最小Linux系统。接下来我们在这个最小Linux系统下运行一个Hello World C程序试试看。

运行Hello World C程序

将SD卡插到电脑虚拟机里面,进入ext4分区的root目录,以sudo方式创建一个hello文件夹,然后把这两个文件夹的所有者和所属组改成Linux虚拟机的当前用户,我们在文件管理器里面就有操作这个文件夹的权限了。

[oct1158@fedora root]$ sudo mkdir hello
[sudo] password for oct1158: 
[oct1158@fedora root]$ sudo chown oct1158 hello
[oct1158@fedora root]$ sudo chgrp oct1158 hello
[oct1158@fedora root]$ ls -l
total 8
drwxr-xr-x. 2 oct1158 oct1158 4096 Aug 28 14:48 hello
-rwxr-xr-x. 1 root    root     256 Jan  1  2000 leds.sh

在hello文件夹里面建立hello.c,内容如下:

#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>

static void dump_data(const void *data, int len)
{
	const uint8_t *p = data;
	
	while (len--)
		printf("%02X", *p++);
	printf("\n");
}

int main(void)
{
	char str[50];
	int a = 0x89abcdef;
	struct tm tm;
	time_t t;
	
	printf("Hello World!\n");
	printf("2*sin(1)*cos(1)=%lf\n", 2 * sin(1) * cos(1));
	printf("sin(2)=%lf\n", sin(2));
	dump_data(&a, sizeof(a));
	
	time(&t);
	printf("t=%d\n", t);
	localtime_r(&t, &tm);
	strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", &tm);
	printf("Time: %s\n", str);
	return 0;
}

我们用交叉编译器编译这个程序:/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc hello.c -static -o hello

由于我们在制作根文件系统的时候,没有把编译器的库复制过去,并且busybox本身也是静态链接的方式编译的。所以这里我们在编译hello程序的时候,也要用静态链接方式(-static),不然程序无法运行。
编译完成后,用file命令可以查看这个程序的运行平台。

[oct1158@fedora hello]$ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, with debug_info, not stripped

将SD卡从电脑上拔出,插到开发板上,运行一下/root/hello/hello程序试试看:

我们的第一个C应用程序在板子上运行成功了。

我们编译器的路径比较长,每次都这样编译很麻烦。我们可以制作一个Makefile文件,把编译器的路径写到CC变量里面,把编译选项写到CFLAGS里面,连接选项写到LDFLAGS里面,然后只需要执行一下make hello,我们就能编译了。

Makefile文件内容:

CC=/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc
LDFLAGS=-static

只需要定义好这两个变量就行了,然后在命令行里面执行make hello,就能编译了,是不是很简单?

[oct1158@fedora hello]$ make hello -B
/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc   -static  hello.c   -o hello

这里-B的意思是强制编译,也就是不管编没编译过都要再编译一次。

如果我们在Makefile里面再加一行all: hello,那就只需要执行make就行了:

CC=/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc
LDFLAGS=-static

all: hello
[oct1158@fedora hello]$ make
make: Nothing to be done for 'all'.
[oct1158@fedora hello]$ make -B
/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc   -static  hello.c   -o hello

我们在不改变Makefile的情况下,想用电脑上的gcc编译, 在电脑上事先测试一下程序是否能运行,可以执行:

[oct1158@fedora hello]$ make -B CC=gcc LDFLAGS=
gcc     hello.c   -o hello

移植电脑Linux系统里面的软件包

Fedora系统里面有一个名叫sl的软件包(Ubuntu里面也有),通过sudo dnf install sl安装后,执行sl命令,可以看到一辆带汽笛的蒸汽小火车,如下图所示。

那么,这种通过软件包管理器安装的软件,我们应该如何移植到开发板的最小Linux系统上运行呢?
首先,我们先用sudo dnf download --source sl命令,把这个软件包的源代码下载下来。
Ubuntu系统下相应的命令是sudo apt-get source sl。CentOS系统下没有sl软件包,需要自己去网上下载。

oct1158@fedora sl]$ pwd
/home/oct1158/Downloads/sl
[oct1158@fedora sl]$ sudo dnf download --source sl
[sudo] password for oct1158: 
enabling fedora-modular-source repository
enabling updates-modular-source repository
enabling updates-source repository
enabling fedora-source repository
Fedora Modular 34 - Source                       55 kB/s | 193 kB     00:03    
Fedora Modular 34 - Updates Source               99 kB/s | 239 kB     00:02    
Fedora 34 - Updates Source                      550 kB/s | 1.6 MB     00:02    
Fedora 34 - Source                              1.0 MB/s | 7.3 MB     00:07    
sl-5.02-15.fc34.src.rpm                          33 kB/s |  14 kB     00:00    
[oct1158@fedora sl]$ ls
sl-5.02-15.fc34.src.rpm

下载下来的压缩包的文件名为sl-5.02-15.fc34.src.rpm,我们需要安装压缩包管理器才能解压这个压缩包。
去Fedora软件商店里面搜索File Roller(也就是GNOME Archive Manager)并安装。软件商店的搜索按钮是窗口左上角的放大镜。

有了File Roller(GNOME Archive Manager)这个软件,我们就能直接打开sl-5.02-15.fc34.src.rpm这个压缩包,然后点左上角的Extract按钮解压文件了。

源文件解压出来后,我们打开Makefile文件,把交叉编译器的路径填进去,CFLAGS里面要加上-static静态链接参数。

这个时候还编译不了,因为我们还没有ncurses这个依赖项。

[oct1158@fedora sl-5.02]$ make
/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc -static -O -o sl sl.c -lncurses
sl.c:39:10: fatal error: curses.h: No such file or directory
   39 | #include <curses.h>
      |          ^~~~~~~~~~
compilation terminated.
make: *** [Makefile:13: sl] Error 1

于是,我们把ncurses这个软件包的源代码也下载下来。一般来说软件开发工具包的名称都以-devel结尾,也就是development的缩写。

[oct1158@fedora sl]$ sudo dnf download --source ncurses-devel
[sudo] password for oct1158: 
enabling fedora-modular-source repository
enabling updates-modular-source repository
enabling updates-source repository
enabling fedora-source repository
Last metadata expiration check: 0:42:56 ago on Sat 28 Aug 2021 17:55:57.
ncurses-6.2-4.20200222.fc34.src.rpm             1.6 MB/s | 3.3 MB     00:02

下载下来的压缩包的文件名为ncurses-6.2-4.20200222.fc34.src.rpm,用Archive Manager解压出来。

可以看到里面有一个configure脚本。这说明这个软件包采用的自动编译系统不是make,而是automake。
automake是比make更复杂的自动编译系统,Linux系统下绝大部分软件包都是用automake做出来的,只有少数功能非常简单的软件包才是make做的。
编译用automake做的软件包之前,我们需要用configure脚本配置编译环境。我们新建一个config.sh脚本,内容如下:

#!/bin/sh
TOOLCHAIN=/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin
./configure --host=arm-none-linux-gnueabihf CC=$TOOLCHAIN/arm-none-linux-gnueabihf-gcc CXX=$TOOLCHAIN/arm-none-linux-gnueabihf-g++

其中,--host是目标程序运行环境,CC和CXX分别是C和C++编译器的路径。
脚本保存好后,添加执行权限,然后执行脚本,配置编译环境:

[oct1158@fedora ncurses-6.2-20200222]$ chmod +x config.sh
[oct1158@fedora ncurses-6.2-20200222]$ ./config.sh
configure: WARNING: If you wanted to set the --build type, don't use --host.
    If a cross compiler is detected then cross compile mode will be used.
checking for egrep... grep -E
Configuring NCURSES 6.2 ABI 6 (Mon Sep  6 23:38:07 CST 2021)
checking for package version... 6.2
checking for package patch date... 20200222
checking build system type... x86_64-pc-linux-gnu
checking host system type... arm-none-linux-gnueabihf
checking target system type... arm-none-linux-gnueabihf
Configuring for linux-gnueabihf
checking for prefix... /usr
checking for arm-none-linux-gnueabihf-gnatgcc... /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc
checking for C compiler default output... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... yes
checking for executable suffix... 
checking for object suffix... o
checking whether we are using the GNU C compiler... yes
checking whether /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc accepts -g... yes
checking version of /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc... 10.3
checking if this is really Intel C compiler... no
checking if this is really Clang C compiler... no
checking for /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc option to accept ANSI C... none needed
checking $CFLAGS variable... ok
checking $CC variable... ok
checking how to run the C preprocessor... /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc -E
checking whether /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc needs -traditional... no
checking whether /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc understands -c and -o together... yes
checking if you want to ensure bool is consistent with C++... yes
checking for arm-none-linux-gnueabihf-g++... /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-g++
checking whether we are using the GNU C++ compiler... yes
checking whether /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-g++ accepts -g... yes
checking if /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-g++ works... no
configure: WARNING: Ignore /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-g++, since it cannot compile hello-world.
configure: WARNING: You don't have any C++ compiler, too bad
checking if you want to build C++ binding and demo... no
checking if you want to build with Ada... yes
checking if you want to install terminal database... yes
checking if you want to install manpages... yes
checking if you want to build programs such as tic... yes
checking if you want to build test-programs... yes
checking if you wish to install curses.h... yes
checking for mawk... no
checking for gawk... gawk
checking for egrep... (cached) grep -E
checking for a BSD compatible install... /usr/bin/install -c
checking for lint... no
checking for cppcheck... no
checking for splint... no
checking whether ln -s works... yes
checking if ln -s -f options work... yes
checking for long file names... yes
checking if you want to use pkg-config... yes
checking for arm-none-linux-gnueabihf-pkg-config... no
checking for pkg-config... /usr/bin/pkg-config
checking for /usr/bin/pkg-config library directory... checking done... /usr/share/pkgconfig
checking if we should install .pc files for /usr/bin/pkg-config... no
checking if we should assume mixed-case filenames... auto
checking if filesystem supports mixed-case filenames... yes
checking whether make sets ${MAKE}... yes
checking for exctags... no
checking for ctags... ctags
checking for exetags... no
checking for etags... no
checking for ctags... yes
checking for etags... no
checking for makeflags variable... 
checking for arm-none-linux-gnueabihf-ranlib... no
checking for ranlib... ranlib
checking for arm-none-linux-gnueabihf-ld... no
checking for ld... ld
checking for arm-none-linux-gnueabihf-ar... no
checking for ar... ar
checking for arm-none-linux-gnueabihf-nm... no
checking for nm... nm
checking for arm-none-linux-gnueabihf-ar... ar
checking for options to update archives... -curvU
checking if you have specified an install-prefix... 
checking for gcc... gcc
checking for native build C compiler... gcc
checking for native build C preprocessor... ${BUILD_CC} -E
checking for native build C flags... 
checking for native build C preprocessor-flags... 
checking for native build linker-flags... 
checking for native build linker-libraries... 
checking if libtool -version-number should be used... yes
checking if you want to build libraries with libtool... no
checking if you want to build shared libraries... no
checking if you want to build static libraries... yes
checking if you want to build debug libraries... yes
checking if you want to build profiling libraries... no
checking for specified models...  normal debug
checking for default model... normal
checking if you want to have a library-prefix... auto
checking for PATH separator... :
checking if you want to build a separate terminfo library... no
checking if you want to build a separate tic library... no
checking for default loader flags... 
checking for an rpath option... -Wl,-rpath,
checking if release/abi version should be used for shared libs... auto
checking which /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc option to use... -fPIC
checking if you want to link with the GPM mouse library... maybe
checking for gpm.h... no
checking if you want to use PCRE2 for regular-expressions... no
checking if you want to disable library suffixes... no
checking if you wish to append extra suffix to header/library paths... 
checking if you wish to install ncurses overwriting curses... yes
checking if external terminfo-database is used... yes
checking which terminfo source-file will be installed... ${top_srcdir}/misc/terminfo.src
checking whether to use hashed database instead of directory/tree... no
checking for list of fallback descriptions... 
checking if you want modern xterm or antique... xterm-new
checking if xterm backspace sends BS or DEL... BS
checking for list of terminfo directories... /usr/share/terminfo
checking for default terminfo directory... /usr/share/terminfo
checking if big-core option selected... no
checking if big-strings option selected... yes
checking if you want termcap-fallback support... no
checking if ~/.terminfo is wanted... yes
checking if you want to use restricted environment when running as root... yes
checking for unistd.h... yes
checking for remove... yes
checking for unlink... yes
checking for link... yes
checking for symlink... yes
checking if tic should use symbolic links... no
checking if tic should use hard links... yes
checking if you want broken-linker support code... no
checking if tputs should process BSD-style prefix padding... no
checking if the POSIX test-macros are already defined... no
checking if this is the GNU C library... yes
checking if _DEFAULT_SOURCE can be used as a basis... yes
checking if _XOPEN_SOURCE=600 works with _DEFAULT_SOURCE... yes
checking if _XOPEN_SOURCE really is set... yes
checking if SIGWINCH is defined... yes
checking for nl_langinfo and CODESET... yes
checking if you want wide-character code... no
checking whether to enable _LP64 definition in curses.h... yes
checking for special C compiler options needed for large files... no
checking for _FILE_OFFSET_BITS value needed for large files... 64
checking for _LARGE_FILES value needed for large files... no
checking for _LARGEFILE_SOURCE value needed for large files... no
checking for fseeko... yes
checking whether to use struct dirent64... no
checking if you want tparm not to use X/Open fixed-parameter list... yes
checking if you want to suppress wattr* macros to help with ncurses5/ncurses6 transition... no
checking for X11 rgb file... ${exec_prefix}/lib/X11/rgb.txt
checking for type of bool... auto
checking for alternate terminal capabilities file... Caps
checking for type of chtype... uint32_t
checking for type of ospeed... short
checking for type of mmask_t... uint32_t
checking for size CCHARW_MAX... 5
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... (cached) yes
checking for signed char... yes
checking size of signed char... 1
checking if you want to use signed Boolean array in term.h... no
checking for type of tparm args... intptr_t
checking if RCS identifiers should be compiled-in... no
checking format of man-pages...  gzip
checking for manpage renaming... no
checking if manpage aliases will be installed... yes
checking if manpage symlinks should be used... yes
checking for manpage tbl... no
checking if you want to build with function extensions... yes
checking if you want to build with SCREEN extensions... yes
checking if you want to build with terminal-driver... no
checking for extended use of const keyword... yes
checking if you want to use extended colors... yes
configure: WARNING: This option applies only to wide-character library
checking if you want to use extended mouse encoding... yes
checking if you want to use extended putwin/screendump... yes
checking if you want $NCURSES_NO_PADDING code... yes
checking if you want SIGWINCH handler... yes
checking if you want user-definable terminal capabilities like termcap... yes
checking if you want to link with the pthread library... no
checking if you want reentrant code... no
checking if you want opaque curses-library structures... no
checking if you want opaque form-library structures... no
checking if you want opaque menu-library structures... no
checking if you want opaque panel-library structures... no
checking if you want all development code... no
checking if you want hard-tabs code... no
checking if you want limited support for xmc... no
checking if you do not want to assume colors are white-on-black... yes
checking if you want hashmap scrolling-optimization code... yes
checking if you want colorfgbg code... no
checking if you want interop bindings... yes
checking if you want experimental safe-sprintf code... no
checking if you want wgetch-events code... no
checking if you want to see long compiling messages... yes
checking if you want to install stripped executables... yes
checking if install accepts -p option... yes
checking if install needs to be told about ownership... no
checking if you want to see compiler warnings... 
configure: checking for /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc __attribute__ directives...
... scanf
... printf
... unused
... noreturn
checking if you want to work around bogus compiler/loader warnings... no
checking if you want to enable runtime assertions... no
checking if you want to use dmalloc for testing... no
checking if you want to use dbmalloc for testing... no
checking if you want to use valgrind for testing... no
checking if you want to perform memory-leak testing... no
checking whether to add trace feature to all models... no
checking if we want to use GNAT projects... yes
checking for gettimeofday... yes
checking if -lm needed for math functions... yes
checking for ANSI C header files... (cached) yes
checking for dirent.h that defines DIR... yes
checking for opendir in -ldir... no
checking whether time.h and sys/time.h may both be included... yes
checking for regcomp... yes
checking for regular-expression headers... regex.h
checking for fcntl.h... yes
checking for getopt.h... yes
checking for limits.h... yes
checking for locale.h... yes
checking for math.h... yes
checking for poll.h... yes
checking for sys/bsdtypes.h... no
checking for sys/ioctl.h... yes
checking for sys/param.h... yes
checking for sys/poll.h... yes
checking for sys/select.h... yes
checking for sys/time.h... yes
checking for sys/times.h... yes
checking for ttyent.h... yes
checking for unistd.h... (cached) yes
checking for wctype.h... yes
checking for unistd.h... (cached) yes
checking for getopt.h... (cached) yes
checking for header declaring getopt variables... unistd.h
checking if external environ is declared... no
checking if external environ exists... yes
checking for getenv... yes
checking for putenv... yes
checking for setenv... yes
checking for strdup... yes
checking if getenv returns consistent values... unknown
checking if sys/time.h works with sys/select.h... yes
checking for an ANSI C-conforming const... yes
checking for inline... inline
checking if /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc supports options to tune inlining... yes
checking for signal global datatype... volatile sig_atomic_t
checking if unsigned literals are legal... yes
checking if external errno is declared... yes
checking if external errno exists... no
checking if data-only library module links... unknown
checking for getcwd... yes
checking for getegid... yes
checking for geteuid... yes
checking for getopt... yes
checking for getttynam... yes
checking for issetugid... no
checking for localeconv... yes
checking for poll... yes
checking for putenv... (cached) yes
checking for remove... (cached) yes
checking for select... yes
checking for setbuf... yes
checking for setbuffer... yes
checking for setenv... (cached) yes
checking for setvbuf... yes
checking for sigaction... yes
checking for sigvec... no
checking for strdup... (cached) yes
checking for strstr... yes
checking for sysconf... yes
checking for tcgetpgrp... yes
checking for times... yes
checking for tsearch... yes
checking for vsnprintf... yes
checking for isascii... yes
checking whether sigaction needs _POSIX_SOURCE... no
checking if nanosleep really works... unknown
checking for termio.h... yes
checking for termios.h... yes
checking for unistd.h... (cached) yes
checking for sys/ioctl.h... (cached) yes
checking for sys/termio.h... no
checking whether termios.h needs _POSIX_SOURCE... no
checking for tcgetattr... yes
checking for vsscanf function or workaround... vsscanf
checking for unistd.h... (cached) yes
checking for working mkstemp... maybe
checking for mkstemp... yes
configure: WARNING: cross compiling: assume setvbuf params not reversed
checking for intptr_t... yes
checking for ssize_t... yes
checking for type sigaction_t... no
checking declaration of size-change... yes
checking for memmove... yes
checking if poll really works... unknown
checking for va_copy... yes
checking for pid_t... yes
checking for unistd.h... (cached) yes
checking for vfork.h... no
checking for fork... yes
checking for vfork... yes
checking for working fork... (cached) yes
checking for working vfork... (cached) yes
checking if fopen accepts explicit binary mode... unknown
checking for openpty in -lutil... yes
checking for openpty header... pty.h
checking if we should include stdbool.h... yes
checking for builtin bool type... no
checking for size of bool... unknown
configure: WARNING: Assuming unsigned for type of bool
checking for gnat... no
checking for gnatmake... no
checking for gprconfig... no
checking for gprbuild... no
checking for library subsets... ticlib+termlib+ext_tinfo+base+ext_funcs
checking default library suffix... 
checking default library-dependency suffix... .a
checking default object directory... objects
checking if linker supports switching between static/dynamic... no
checking where we will install curses.h... ${prefix}/include
checking for src modules... ncurses progs panel menu form
checking for defines to add to ncurses6-config script...  -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600
package: ncurses
checking for linker search path... /usr/lib /lib
configure: creating ./config.status
config.status: creating include/MKterm.h.awk
config.status: creating include/curses.head
config.status: creating include/ncurses_dll.h
config.status: creating include/termcap.h
config.status: creating include/unctrl.h
config.status: creating man/Makefile
config.status: creating include/Makefile
config.status: creating ncurses/Makefile
config.status: creating progs/Makefile
config.status: creating panel/Makefile
config.status: creating menu/Makefile
config.status: creating form/Makefile
config.status: creating test/Makefile
config.status: creating misc/Makefile
config.status: creating misc/run_tic.sh
config.status: creating misc/ncurses-config
config.status: creating man/ncurses6-config.1
config.status: creating Makefile
config.status: creating include/ncurses_cfg.h
Appending rules for normal model (ncurses: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for debug model (ncurses: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for normal model (progs: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for debug model (progs: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for normal model (panel: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for debug model (panel: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for normal model (menu: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for debug model (menu: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for normal model (form: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for debug model (form: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for normal model (test: ticlib+termlib+ext_tinfo+base+ext_funcs)
Appending rules for debug model (test: ticlib+termlib+ext_tinfo+base+ext_funcs)
creating headers.sh

** Configuration summary for NCURSES 6.2 20200222:

       extended funcs: yes
       xterm terminfo: xterm-new

        bin directory: /usr/bin
        lib directory: /usr/lib
    include directory: /usr/include
        man directory: /usr/share/man
   terminfo directory: /usr/share/terminfo

配置成功,这个时候我们可以执行make编译命令了,可以编译成功:

编译完成后,还需要执行make install DESTDIR=$(pwd)/_install安装,安装路径为_install文件夹。但是安装失败,提示strip程序异常退出:
/usr/bin/install -c -s tic     /home/oct1158/Downloads/sl/ncurses-6.2-20200222/_install/usr/bin/`echo tic|       sed 's/$//'|sed 's,x,x,'|sed 's/$//'`
strip: Unable to recognise the format of the input file `/home/oct1158/Downloads/sl/ncurses-6.2-20200222/_install/usr/bin/tic'
/usr/bin/install: strip process terminated abnormally

make[1]: Entering directory '/home/oct1158/Downloads/sl/ncurses-6.2-20200222/progs'
mkdir -p /home/oct1158/Downloads/sl/ncurses-6.2-20200222/_install/usr/bin
/usr/bin/install -c -s tic     /home/oct1158/Downloads/sl/ncurses-6.2-20200222/_install/usr/bin/`echo tic|       sed 's/$//'|sed 's,x,x,'|sed 's/$//'`
strip: Unable to recognise the format of the input file `/home/oct1158/Downloads/sl/ncurses-6.2-20200222/_install/usr/bin/tic'
/usr/bin/install: strip process terminated abnormally
make[1]: *** [Makefile:202: install.progs] Error 1
make[1]: Leaving directory '/home/oct1158/Downloads/sl/ncurses-6.2-20200222/progs'
make: *** [Makefile:121: install] Error 2

这是因为,install程序误调用了PC版gcc的strip程序,应该修改为交叉编译工具链的arm-none-linux-gnueabihf-strip工具。
打开progs/Makefile文件,修改INSTALL_PROG变量,在字符串的末尾添加--strip-program=/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-strip,如下图所示。

修改完之后,再make install DESTDIR=$(pwd)/_install,就能安装成功了(准确地说是在_install文件夹下生成安装文件)。_install文件夹里面出现了usr文件夹,usr文件夹里面有bin、include、lib和share文件夹。
通常情况下,安装文件生成后,我们是将_install文件夹里面的所有内容复制到根文件系统下。/usr不是user的缩写,是Unix Software Resource的缩写,也就是放置软件资源的目录。
/usr/bin存放的是软件里面的所有可执行的程序,在命令行里面直接输入程序名就可以使用,不需要加上“./”。
/usr/include存放的是gcc或g++编译程序时用到的头文件。
/usr/lib存放的是软件用到的库文件(.a静态库、.so动态库)。
/usr/share存放的是软件用到的其他资源文件。
由于我们采用静态链接方式(-static)编译sl,我们现在暂时不复制_install里面的文件,我们先看看直接运行sl程序会怎么样。

[oct1158@fedora _install]$ ls
usr
[oct1158@fedora _install]$ ls usr
bin  include  lib  share
[oct1158@fedora _install]$ ls usr/bin
captoinfo  infocmp    ncurses6-config  tabs  toe   tset
clear      infotocap  reset            tic   tput
[oct1158@fedora _install]$ ls usr/include
curses.h  form.h  nc_tparm.h     ncurses.h  termcap.h     term.h  unctrl.h
eti.h     menu.h  ncurses_dll.h  panel.h    term_entry.h  tic.h
[oct1158@fedora _install]$ ls usr/lib
libcurses.a  libform_g.a  libmenu_g.a   libncurses_g.a  libpanel_g.a
libform.a    libmenu.a    libncurses.a  libpanel.a      terminfo
[oct1158@fedora _install]$ ls usr/share
man  tabset  terminfo
[oct1158@fedora _install]$ ls usr/share/terminfo
1  3  5  7  9  A  c  e  f  h  j  l  m  n  o  P  Q  s  u  w  X
2  4  6  8  a  b  d  E  g  i  k  L  M  N  p  q  r  t  v  x  z
[oct1158@fedora _install]$ ls usr/share/terminfo/v
v200-nam   vi55          vs100-x10      vt100-w       vt300        vt520
v320n      vi550         vsc            vt100-w-am    vt300-nam    vt520ansi
v3220      vi603         vscode         vt100-w-nam   vt300-w      vt525
v5410      viewdata      vscode-direct  vt100-w-nav   vt300-w-nam  vt52-basic
vanilla    viewdata-o    vt100          vt102         vt320        vt52+keypad
vapple     viewdata-rv   vt100+         vt102+enq     vt320-k3     vt-61
vc103      viewpoint     vt100+4bsd     vt102-nsgr    vt320-k311   vt61
vc203      viewpoint3a+  vt100-am       vt102-w       vt320-nam    vt61.5
vc303      viewpoint60   vt100-bm       vt125         vt320nam     vte
vc303a     viewpoint90   vt100-bm-o     vt131         vt320-w      vte-2007
vc403a     vip           vt100-bot-s    vt132         vt320-w-nam  vte-2008
vc404      vip7800-H     vt100+enq      vt200         vt330        vte-2012
vc404-s    vip7800-Hw    vt100+fnkeys   vt200-8       vt340        vte-2014
vc414      vip7800-w     vt100+keypad   vt200-8bit    vt400        vte-2017
vc414h     vip-H         vt100-nam      vt200-js      vt400-24     vte-2018
vc415      vip-Hw        vt100nam       vt200-old     vt420        vte-256color
venix      vip-w         vt100-nam-w    vt200-w       vt420f       vte-direct
versaterm  visa50        vt100-nav      vt220         vt420+lrmm   vte+pcfkeys
vi200      visual603     vt100-nav-w    vt220-8       vt420pc      vtnt
vi200-f    vitty         vt100+pfkeys   vt220-8bit    vt420pcdos   vt-utf8
vi200-rv   vk100         vt100-putty    vt220d        vt50         vv100
vi300      vp3a+         vt100-s        vt220-js      vt50h        vwmterm
vi300-old  vp60          vt100-s-bot    vt220+keypad  vt510
vi50       vp90          vt100-s-top    vt220-nam     vt510pc
vi500      vremote       vt100-top-s    vt220-old     vt510pcdos
vi50adm    vs100         vt100-vb       vt220-w       vt52

回到sl文件夹里面,把include路径和lib路径添加到Makefile里面,sl就能编译成功了:

把编译出来的sl程序拷贝到SD卡里面,然后在板子上运行:

/root/sl # ls -l
total 3360
-rwxrwxr-x    1 1000     1000       3438288 Aug 28  2021 sl
/root/sl # ./sl
Error opening terminal: vt102.

 提示Error opening terminal: vt102。这是因为我们刚才没有将安装文件夹里面的usr/share/terminfo/v/vt102复制到SD卡里面。

[oct1158@fedora sl]$ cd /run/media/oct1158/df9ab0e3-4168-4efe-8175-fb6eb4af4e03
[oct1158@fedora df9ab0e3-4168-4efe-8175-fb6eb4af4e03]$ sudo mkdir -p usr/share/terminfo/v
[oct1158@fedora df9ab0e3-4168-4efe-8175-fb6eb4af4e03]$ sudo cp /home/oct1158/Downloads/sl/ncurses-6.2-20200222/_install/usr/share/terminfo/v/vt102 usr/share/terminfo/v
[oct1158@fedora df9ab0e3-4168-4efe-8175-fb6eb4af4e03]$ ls -l usr/share/terminfo/v
total 4
-rw-r--r--. 1 root root 1184 Aug 28 19:06 vt102

复制了之后,开发板上也能成功运行sl了:

./sl还可以带4种参数:./sl -a、./sl -F、./sl -l或./sl -c。
例如运行./sl -a命令,可以看到小火车上面多了两个Help! Help!:

编写内核驱动模块并调用GPIOLIB操作GPIO端口

迅为的Linux3.0内核里面的drivers/char目录下提供了三个GPIO例程(itop4412_buzzer、itop4412_leds、itop4412_relay)和一个ADC例程(itop4412_adc)。但遗憾的是,3.0版本是不支持设备树的老版本,而我们的4.14版本是支持设备树的版本。迅为的例程里面用到了mach和plat头文件,在带设备树的内核版本里面是无法使用的。设备树的出现,目的就是要淘汰掉mach和plat目录下的一大堆文件。对GPIO来说, EXYNOS4_GPL2()这样的宏已经没有了,linux-4.14.2/arch/arm/mach-s3c24xx/include/mach/gpio-samsung.h里面虽然有S3C2410_GPL()这样的宏,但是计算的结果并不正确。linux-4.14.2/arch/arm/plat-samsung/gpio-samsung.c和adc.c也根本没有参与编译,生成.o文件。要想让这两个文件参与编译,必须要在menuconfig里面,System Type ---> ARM system type选择Samsung S3C24XX SoCs,然后System Type ---> Samsung Common options ---> ADC common driver support要勾选上。使得.config里面:

[oct1158@fedora linux-4.14.2]$ grep "\(MULTIPLATFORM\|ATAGS\|GPIO_SAMSUNG\|S3C_ADC\)" .config
# CONFIG_ARCH_MULTIPLATFORM is not set
CONFIG_SAMSUNG_ATAGS=y
CONFIG_S3C_ADC=y
CONFIG_GPIO_SAMSUNG=y
CONFIG_ATAGS=y
# CONFIG_BATTERY_S3C_ADC is not set

这样设置了之后,虽然gpio-samsung.c和adc.c会参与编译了,但是内核却无法启动了。所以我们自己要写ADC、GPIO(复用功能选择)、PWM等驱动的话,可能就要自己去操作寄存器了。
不过,简单的GPIO电平的输入输出操作,可以调用内核的GPIOLIB API来完成。让我们来编写一个简单的GPIO LEDs驱动。

leds.c(内核驱动模块):
ioctl里面,cmd=0是输出低电平,cmd=1是输出高电平,cmd=-1是读取当前输出的电平,所以case 1那个分支没有写break。

/* 参考资料: https://www.kernel.org/doc/html/v4.17/driver-api/gpio/legacy.html */
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/module.h>

#define GPL20 120
#define GPK11 91

static struct gpio leds_pins[2] = {
	{GPL20, GPIOF_OUT_INIT_LOW, "LED2"},
	{GPK11, GPIOF_OUT_INIT_LOW, "LED3"}
};

static int leds_open(struct inode *inode, struct file *file)
{
	int ret;
	
	ret = gpio_request_array(leds_pins, ARRAY_SIZE(leds_pins));
	if (ret != 0)
		printk(KERN_ERR "gpio_request_array() failed!");
	return ret;
}

static int leds_release(struct inode *inode, struct file *file)
{
	gpio_free_array(leds_pins, ARRAY_SIZE(leds_pins));
	return 0;
}

static long leds_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	if (cmd >= 0 && cmd < ARRAY_SIZE(leds_pins))
	{
		switch (arg)
		{
			case 0:
			case 1:
				gpio_set_value(leds_pins[cmd].gpio, arg);
			case -1:
				return gpio_get_value(leds_pins[cmd].gpio);
		}
	}
	return -EINVAL;
}

static struct file_operations leds_fops = {
	.open = leds_open,
	.release = leds_release,
	.unlocked_ioctl = leds_unlocked_ioctl
};

static struct miscdevice leds_misc = {
	.name = "leds-demo", // /dev目录下的设备文件名
	.minor = MISC_DYNAMIC_MINOR, // 由内核自动分配可用的次设备号
	.fops = &leds_fops
};

static int __init leds_init(void)
{
	return misc_register(&leds_misc);
}

static void __exit leds_exit(void)
{
	misc_deregister(&leds_misc);
}

module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("GPL");

leds_test.c(应用程序):

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define LED0 0
#define LED1 1
#define OFF 0
#define ON 1

int main(void)
{
	int a, b;
	int fd, i, ret;
	
	fd = open("/dev/leds-demo", O_RDWR);
	if (fd == -1)
	{
		printf("Failed to open device!\n");
		return -1;
	}
	
	for (i = 0; i < 20; i++)
	{
		printf("i=%d\n", i);
		if (i & 1)
		{
			printf("LEDs ON\n");
			a = ioctl(fd, LED0, ON);
			b = ioctl(fd, LED1, ON);
		}
		else
		{
			printf("LEDs OFF\n");
			a = ioctl(fd, LED0, OFF);
			b = ioctl(fd, LED1, OFF);
		}
		printf("a=%d b=%d, errno=%d\n", a, b, errno);
		sleep(1);
	}
	
	close(fd);
	return 0;
}

Makefile:

CC = /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc
KERNEL_DIR = /home/oct1158/Downloads/linux-4.14.2

obj-m += leds.o 

all: leds.ko leds_test

leds.ko: leds.c
	make -C $(KERNEL_DIR) M=$(PWD) ccflags-y="-Wno-missing-attributes"
	
leds_test: leds_test.c
	$(CC) $^ -static -o $@

用make命令编译后,内核驱动模块leds.c生成的文件是leds.ko。应用程序leds_test.c生成的文件是leds_test。

程序里面的GPIO号120和91是怎么来的呢? 我们用dmesg | head -n 200命令看一下内核的启动日志:

[    0.428680] gpio gpiochip0: (gpa0): added GPIO chardev (254:0)
[    0.428696] gpiochip_setup_dev: registered GPIOs 0 to 7 on device: gpiochip0 (gpa0)
[    0.428961] gpio gpiochip1: (gpa1): added GPIO chardev (254:1)
[    0.428972] gpiochip_setup_dev: registered GPIOs 8 to 13 on device: gpiochip1 (gpa1)
[    0.429242] gpio gpiochip2: (gpb): added GPIO chardev (254:2)
[    0.429253] gpiochip_setup_dev: registered GPIOs 14 to 21 on device: gpiochip2 (gpb)
[    0.429507] gpio gpiochip3: (gpc0): added GPIO chardev (254:3)
[    0.429518] gpiochip_setup_dev: registered GPIOs 22 to 26 on device: gpiochip3 (gpc0)
[    0.429777] gpio gpiochip4: (gpc1): added GPIO chardev (254:4)
[    0.429788] gpiochip_setup_dev: registered GPIOs 27 to 31 on device: gpiochip4 (gpc1)
[    0.430076] gpio gpiochip5: (gpd0): added GPIO chardev (254:5)
[    0.430087] gpiochip_setup_dev: registered GPIOs 32 to 35 on device: gpiochip5 (gpd0)
[    0.430350] gpio gpiochip6: (gpd1): added GPIO chardev (254:6)
[    0.430360] gpiochip_setup_dev: registered GPIOs 36 to 39 on device: gpiochip6 (gpd1)
[    0.430611] gpio gpiochip7: (gpf0): added GPIO chardev (254:7)
[    0.430622] gpiochip_setup_dev: registered GPIOs 40 to 47 on device: gpiochip7 (gpf0)
[    0.430883] gpio gpiochip8: (gpf1): added GPIO chardev (254:8)
[    0.430893] gpiochip_setup_dev: registered GPIOs 48 to 55 on device: gpiochip8 (gpf1)
[    0.431155] gpio gpiochip9: (gpf2): added GPIO chardev (254:9)
[    0.431165] gpiochip_setup_dev: registered GPIOs 56 to 63 on device: gpiochip9 (gpf2)
[    0.431430] gpio gpiochip10: (gpf3): added GPIO chardev (254:10)
[    0.431441] gpiochip_setup_dev: registered GPIOs 64 to 69 on device: gpiochip10 (gpf3)
[    0.431693] gpio gpiochip11: (gpj0): added GPIO chardev (254:11)
[    0.431704] gpiochip_setup_dev: registered GPIOs 70 to 77 on device: gpiochip11 (gpj0)
[    0.431957] gpio gpiochip12: (gpj1): added GPIO chardev (254:12)
[    0.431968] gpiochip_setup_dev: registered GPIOs 78 to 82 on device: gpiochip12 (gpj1)
[    0.434354] gpio gpiochip13: (gpk0): added GPIO chardev (254:13)
[    0.434367] gpiochip_setup_dev: registered GPIOs 83 to 89 on device: gpiochip13 (gpk0)
[    0.434623] gpio gpiochip14: (gpk1): added GPIO chardev (254:14)
[    0.434634] gpiochip_setup_dev: registered GPIOs 90 to 96 on device: gpiochip14 (gpk1)
[    0.434896] gpio gpiochip15: (gpk2): added GPIO chardev (254:15)
[    0.434907] gpiochip_setup_dev: registered GPIOs 97 to 103 on device: gpiochip15 (gpk2)
[    0.435158] gpio gpiochip16: (gpk3): added GPIO chardev (254:16)
[    0.435169] gpiochip_setup_dev: registered GPIOs 104 to 110 on device: gpiochip16 (gpk3)
[    0.435430] gpio gpiochip17: (gpl0): added GPIO chardev (254:17)
[    0.435441] gpiochip_setup_dev: registered GPIOs 111 to 117 on device: gpiochip17 (gpl0)
[    0.435691] gpio gpiochip18: (gpl1): added GPIO chardev (254:18)
[    0.435702] gpiochip_setup_dev: registered GPIOs 118 to 119 on device: gpiochip18 (gpl1)
[    0.435964] gpio gpiochip19: (gpl2): added GPIO chardev (254:19)
[    0.435975] gpiochip_setup_dev: registered GPIOs 120 to 127 on device: gpiochip19 (gpl2)
[    0.436237] gpio gpiochip20: (gpm0): added GPIO chardev (254:20)
[    0.436248] gpiochip_setup_dev: registered GPIOs 128 to 135 on device: gpiochip20 (gpm0)
[    0.436501] gpio gpiochip21: (gpm1): added GPIO chardev (254:21)
[    0.436512] gpiochip_setup_dev: registered GPIOs 136 to 142 on device: gpiochip21 (gpm1)
[    0.436855] gpio gpiochip22: (gpm2): added GPIO chardev (254:22)
[    0.436866] gpiochip_setup_dev: registered GPIOs 143 to 147 on device: gpiochip22 (gpm2)
[    0.437129] gpio gpiochip23: (gpm3): added GPIO chardev (254:23)
[    0.437140] gpiochip_setup_dev: registered GPIOs 148 to 155 on device: gpiochip23 (gpm3)
[    0.437401] gpio gpiochip24: (gpm4): added GPIO chardev (254:24)
[    0.437412] gpiochip_setup_dev: registered GPIOs 156 to 163 on device: gpiochip24 (gpm4)
[    0.437664] gpio gpiochip25: (gpy0): added GPIO chardev (254:25)
[    0.437675] gpiochip_setup_dev: registered GPIOs 164 to 169 on device: gpiochip25 (gpy0)
[    0.437934] gpio gpiochip26: (gpy1): added GPIO chardev (254:26)
[    0.437945] gpiochip_setup_dev: registered GPIOs 170 to 173 on device: gpiochip26 (gpy1)
[    0.438198] gpio gpiochip27: (gpy2): added GPIO chardev (254:27)
[    0.438209] gpiochip_setup_dev: registered GPIOs 174 to 179 on device: gpiochip27 (gpy2)
[    0.438485] gpio gpiochip28: (gpy3): added GPIO chardev (254:28)
[    0.438496] gpiochip_setup_dev: registered GPIOs 180 to 187 on device: gpiochip28 (gpy3)
[    0.438757] gpio gpiochip29: (gpy4): added GPIO chardev (254:29)
[    0.438768] gpiochip_setup_dev: registered GPIOs 188 to 195 on device: gpiochip29 (gpy4)
[    0.439029] gpio gpiochip30: (gpy5): added GPIO chardev (254:30)
[    0.439039] gpiochip_setup_dev: registered GPIOs 196 to 203 on device: gpiochip30 (gpy5)
[    0.439291] gpio gpiochip31: (gpy6): added GPIO chardev (254:31)
[    0.439302] gpiochip_setup_dev: registered GPIOs 204 to 211 on device: gpiochip31 (gpy6)
[    0.439563] gpio gpiochip32: (gpx0): added GPIO chardev (254:32)
[    0.439574] gpiochip_setup_dev: registered GPIOs 212 to 219 on device: gpiochip32 (gpx0)
[    0.439826] gpio gpiochip33: (gpx1): added GPIO chardev (254:33)
[    0.439837] gpiochip_setup_dev: registered GPIOs 220 to 227 on device: gpiochip33 (gpx1)
[    0.440134] gpio gpiochip34: (gpx2): added GPIO chardev (254:34)
[    0.440145] gpiochip_setup_dev: registered GPIOs 228 to 235 on device: gpiochip34 (gpx2)
[    0.440399] gpio gpiochip35: (gpx3): added GPIO chardev (254:35)
[    0.440410] gpiochip_setup_dev: registered GPIOs 236 to 243 on device: gpiochip35 (gpx3)
[    0.443346] gpio gpiochip36: (gpz): added GPIO chardev (254:36)
[    0.443357] gpiochip_setup_dev: registered GPIOs 244 to 250 on device: gpiochip36 (gpz)
[    0.443408] genirq: irq_chip COMBINER did not update eff. affinity mask of irq 94
[    0.444553] gpio gpiochip37: (gpv0): added GPIO chardev (254:37)
[    0.444565] gpiochip_setup_dev: registered GPIOs 251 to 258 on device: gpiochip37 (gpv0)
[    0.444818] gpio gpiochip38: (gpv1): added GPIO chardev (254:38)
[    0.444830] gpiochip_setup_dev: registered GPIOs 259 to 266 on device: gpiochip38 (gpv1)
[    0.445090] gpio gpiochip39: (gpv2): added GPIO chardev (254:39)
[    0.445101] gpiochip_setup_dev: registered GPIOs 267 to 274 on device: gpiochip39 (gpv2)
[    0.445351] gpio gpiochip40: (gpv3): added GPIO chardev (254:40)
[    0.445362] gpiochip_setup_dev: registered GPIOs 275 to 282 on device: gpiochip40 (gpv3)
[    0.445622] gpio gpiochip41: (gpv4): added GPIO chardev (254:41)
[    0.445633] gpiochip_setup_dev: registered GPIOs 283 to 284 on device: gpiochip41 (gpv4)

可以看到,gpl2的编号是120~127,所以gpl20的编号就是120。gpk1的编号是90~96,所以gpk11的编号就是91。 

现在我们把leds.ko和leds_test拷贝到开发板上运行。首先,用insmod命令插入leds.ko模块:

/ # cd /root/leds
/root/leds # ls -l
total 2924
-rw-rw-r--    1 1000     1000        101784 Aug 30  2021 leds.ko
-rwxrwxr-x    1 1000     1000       2888988 Aug 30  2021 leds_test
/root/leds # insmod leds.ko
[   31.325814] leds: loading out-of-tree module taints kernel.
/root/leds # ls -l /dev/leds-demo
crw-------    1 0        0          10,  59 Jan  1 00:00 /dev/leds-demo

内核模块插入成功,生成了/dev/leds-demo这个设备文件。接下来运行leds_test程序,但是却提示gpio_request_array函数执行失败:

/root/leds # ./leds_test
[  137.683082] gpio_request_array() failed!

这是因为设备树里面声明了led2和led3,被linux内核自带的GPIO LED驱动绑定了,我们的驱动就申请不了了。
我们用rmmod命令,把leds.ko模块卸载了。卸载前,要确保/lib/modules/4.14.2文件夹是存在的,不然rmmod命令是无法使用的。

/root/leds # mkdir -p /lib/modules/4.14.2
/root/leds # rmmod leds.ko
/root/leds # ls /dev/leds-demo
ls: /dev/leds-demo: No such file or directory

卸载模块后,设备文件就消失了。
打开设备树文件myexynos4412-itop-elite.dts,将leds语句块里面的compatible属性值改成其他名字,就能和内核的GPIO LED驱动解绑。

用make dtbs命令编译设备树,生成myexynos4412-itop-elite.dtb文件,把这个dtb文件复制到U盘里面。然后把U盘插到开发板上,mount U盘,我们可以直接在开发板上用dd命令烧写设备树:dd if=myexynos4412-itop-elite.dtb of=/dev/mmcblk0 seek=2048

/mnt/myusbdisk # ls
05_a_tr.xml                  AC6102_WEB_SERVER_GMII.rar
05_p_tr.xml                  AIS????
20210223_2.zip               AISMOB-5.6.rar
20210423                     HX2AF01_20210310.zip
20210423????.rar             HX2AF01_20210315.zip
20210423_2.zip               KA??
20210719                     Ka??
20210818                     Quartus
????                         SFP.rar
????.rar                     System Volume Information
?????? v2.32                 V5.0??APP&????&????2???.rar
???????(rn_log)              ddr2_20210422_failed.zip
????????20210507             ddr2_nios_test_20210422.zip
??????Schematic Prints?.zip  myexynos4412-itop-elite.dtb
??????_20210425.rar          simulate_memory.rar
A703-35T.part1.rar           stm32f107rc_02A5.zip
A703-35T.part2.rar           xxx
A703-35T.part3.rar           yyyy
/mnt/myusbdisk # dd if=myexynos4412-itop-elite.dtb of=/dev/mmcblk0 seek=2048
109+1 records in
109+1 records out
55843 bytes (54.5KB) copied, 0.008095 seconds, 6.6MB/s

烧写完成后,重启开发板,执行ls /sys/class/leds,里面只有mmc0::了,说明我们解绑成功了。再来看看我们的程序能否运行:

/ # cd root/leds
/root/leds # insmod leds.ko
[  138.534792] leds: loading out-of-tree module taints kernel.
/root/leds # ./leds_test
i=0
LEDs OFF
a=0 b=0, errno=0
i=1
LEDs ON
a=1 b=1, errno=0
i=2
LEDs OFF
a=0 b=0, errno=0
i=3
LEDs ON
a=1 b=1, errno=0
i=4
LEDs OFF
a=0 b=0, errno=0
i=5
LEDs ON
a=1 b=1, errno=0
i=6
LEDs OFF
a=0 b=0, errno=0
i=7
LEDs ON
a=1 b=1, errno=0
i=8
LEDs OFF
a=0 b=0, errno=0
i=9
LEDs ON
a=1 b=1, errno=0
i=10
LEDs OFF
a=0 b=0, errno=0
i=11
LEDs ON
a=1 b=1, errno=0
i=12
LEDs OFF
a=0 b=0, errno=0
i=13
LEDs ON
a=1 b=1, errno=0
i=14
LEDs OFF
a=0 b=0, errno=0
i=15
LEDs ON
a=1 b=1, errno=0
i=16
LEDs OFF
a=0 b=0, errno=0
i=17
LEDs ON
a=1 b=1, errno=0
i=18
LEDs OFF
a=0 b=0, errno=0
i=19
LEDs ON
a=1 b=1, errno=0

板子上的灯闪烁起来了,我们的程序运行成功了! 

动态链接程序

之前我们所有的应用程序,包括busybox根文件系统本身,我们都是采用的静态链接,编译时加了-static选项,编译出来的程序占用的空间很大。如果我们不加-static选项,采用默认的动态链接方式,编译出来的程序就会大大缩小。那么,动态链接方式编译出来的程序,我们应该如何在开发板上运行呢?

我们把之前的hello程序的文件夹复制出来,改名为hello_dynamic:

[oct1158@fedora root]$ ls -l
total 20
-rwxr-xr-x. 1 root    root     170 Jan  1  2000 adc.sh
drwxr-xr-x. 2 oct1158 oct1158 4096 Aug 28 17:01 hello
drwxr-xr-x. 2 oct1158 oct1158 4096 Aug 30 22:21 leds
-rwxr-xr-x. 1 root    root     256 Jan  1  2000 leds.sh
drwxr-xr-x. 2 oct1158 oct1158 4096 Aug 28 18:58 sl
[oct1158@fedora root]$ sudo cp -r hello hello_dynamic
[oct1158@fedora root]$ sudo chown -R oct1158 hello_dynamic
[oct1158@fedora root]$ sudo chgrp -R oct1158 hello_dynamic
[oct1158@fedora root]$ ls -l
total 24
-rwxr-xr-x. 1 root    root     170 Jan  1  2000 adc.sh
drwxr-xr-x. 2 oct1158 oct1158 4096 Aug 28 17:01 hello
drwxr-xr-x. 2 oct1158 oct1158 4096 Sep  1 22:47 hello_dynamic
drwxr-xr-x. 2 oct1158 oct1158 4096 Aug 30 22:21 leds
-rwxr-xr-x. 1 root    root     256 Jan  1  2000 leds.sh
drwxr-xr-x. 2 oct1158 oct1158 4096 Aug 28 18:58 sl

修改Makefile文件,去掉LDFLAGS里面的-static:

CC=/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc

all: hello

然后make编译程序:

[oct1158@fedora root]$ cd hello_dynamic/
[oct1158@fedora hello_dynamic]$ make
make: Nothing to be done for 'all'.
[oct1158@fedora hello_dynamic]$ make -B
/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc     hello.c   -o hello
[oct1158@fedora hello_dynamic]$ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, with debug_info, not stripped

用file命令可以看到程序是动态链接(dynamically linked)的。在板子上现有的文件系统上直接运行,会提示not found的错误:

/root/hello_dynamic # ./hello
-/bin/sh: ./hello: not found

这是因为我们没有把编译器的lib库复制到根文件系统里面。目前根文件系统的/lib目录下只有modules文件夹:

编译器的库所在的文件夹为arm-none-linux-gnueabihf/libc/lib。我们只需要把这个文件夹下的所有文件复制到根文件系统的/lib目录下就行了。由于这个文件夹里面有很多软链接,所以在用cp命令复制的时候,一定要带上-r选项,否则这些软链接会消失:

[oct1158@fedora lib]$ sudo cp -r /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/lib/* .
[sudo] password for oct1158:

cp的最后一个参数是目标目录,“.”表示当前目录,也就是SD卡根文件系统里面的lib目录。
复制完成后,lib文件夹里面有很多绿色正方形的库文件,也有很多蓝色正方形、带快捷方式箭头的软链接。

现在,动态链接的hello程序就可以运行成功了:

/root/hello_dynamic # ls
Makefile  hello     hello.c
/root/hello_dynamic # ./hello
Hello World!
2*sin(1)*cos(1)=0.909297
sin(2)=0.909297
EFCDAB89
t=946684811
Time: 2000-01-01 00:00:11

目前busybox根文件系统本身仍然是静态链接的格式。我们把busybox也重新编译成动态链接。
进入busybox源文件目录后,先make clean一下,删除所有的旧的编译结果,但不删除配置设置。然后,make menuconfig arch=ARM,进入菜单配置,取消勾选Busybox Settings里面的Build BusyBox as a static binary (no shared libs)。

保存配置,执行make编译,然后make install生成新的根文件系统。这下用file命令可以看到bin/busybox是动态链接(dynamically linked)方式编译的了:

[oct1158@fedora busybox-1.26.2_dynamic]$ file _install/bin/busybox
_install/bin/busybox: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, stripped

_install文件夹下,除了bin/busybox以外,其余文件全部都是软链接。所以,我们只需要复制bin/busybox这个文件到SD卡中,覆盖掉原有的静态链接版本,就可以了:

[oct1158@fedora busybox-1.26.2_dynamic]$ sudo cp _install/bin/busybox /run/media/oct1158/df9ab0e3-4168-4efe-8175-fb6eb4af4e03/bin
[sudo] password for oct1158:

SD卡插回到板子上,启动板子,系统能够启动成功:

/ # cd bin
/bin # ls -lh busybox
-rwxr-xr-x    1 0        0         651.0K Sep  1  2021 busybox
/bin # cd /root
/root # ls -lh hello/hello
-rwxrwxr-x    1 1000     1000        3.2M Aug 28  2021 hello/hello
/root # ls -lh hello_dynamic/hello
-rwxrwxr-x    1 1000     1000       13.5K Sep  1  2021 hello_dynamic/hello

静态链接版本的busybox大小为1.3MB,动态链接版本的busybox大小为651KB。体积大约减小了一半。
静态链接版本的hello程序大小为3.2MB,动态链接版本的hello程序大小为13.5KB,体积只有原来的0.4%。

经过笔者测试,现在动态链接了pthread线程库(-lpthread)和数学库(-lm)的程序都能运行成功,使用了long double(80位浮点数)和complex(复数)的程序也能运行成功,包括之前编译的sl程序(-lncurses)也能运行成功。
但是用arm-none-linux-gnueabihf-g++编译的C++程序却无法运行成功,提示:

/root/libtest # ./cpptest
./cpptest: error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory

解决办法是将arm-none-linux-gnueabihf/libc/usr/lib里面的文件复制到根文件系统的/usr/lib里面。(因为里面有很多指向../../lib的符号链接,所以必须复制到/usr/lib里面)

[oct1158@fedora df9ab0e3-4168-4efe-8175-fb6eb4af4e03]$ sudo cp -r /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/usr/lib/* usr/lib
[sudo] password for oct1158: 

其中就有我们需要的libstdc++.so.6库:

现在,C++程序就能运行成功了。

测试用的程序

cpptest.cpp

#include <iostream>
#include <string>

using namespace std;

int main(void)
{
	string str = "hello world";
	
	cout << str << endl;
	cout << "len=" << str.length() << endl;
	
	return 0;
}

说明:声明string变量时赋初值(如string str = "xxx")调用的是string::string(const char *)构造函数,后面再对str重新赋值(如str = "yyy")则是调用的是string::operator=(const char *)运算符重载函数。

mathtest.c

#include <complex.h>
#include <math.h>
#include <stdio.h>

int main(void)
{
	long double complex z = 2 + 11i;
	
	printf("z=%llg+%llgi\n", creall(z), cimagl(z));
	z = cpowl(z, 1.0L / 3);
	printf("z=%llg+%llgi\n", creall(z), cimagl(z));
	
	return 0;
}

threadtest.c

#include <pthread.h>
#include <stdio.h>

static void *thread_func(void *arg)
{
	printf("Hello Thread! arg=%s\n", arg);
	return "end";
}

int main(void)
{
	pthread_t thread;
	void *ret;
	
	printf("Hello World!\n");
	pthread_create(&thread, NULL, thread_func, "busybox");
	pthread_join(thread, &ret);
	printf("ret=%s\n", ret);
	return 0;
}

Makefile

CC=/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc
CXX=/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-g++

all: mathtest cpptest threadtest

mathtest: mathtest.c
	$(CC) $^ -o $@ -lm
	
threadtest: threadtest.c
	$(CC) $^ -o $@ -lpthread

使用make命令编译后,三个程序都能编译成功,采用的是动态链接方式。在板子上运行程序,结果如下:

/ # cd root/libtest
/root/libtest # ls -l
total 64
-rw-r--r--    1 1000     1000           356 Sep  2  2021 Makefile
-rwxr-xr-x    1 1000     1000         15100 Sep  2  2021 cpptest
-rw-r--r--    1 1000     1000           187 Sep  2  2021 cpptest.cpp
-rwxr-xr-x    1 1000     1000         13776 Sep  2  2021 mathtest
-rw-r--r--    1 1000     1000           250 Sep  2  2021 mathtest.c
-rwxr-xr-x    1 1000     1000         13892 Sep  2  2021 threadtest
-rw-r--r--    1 1000     1000           338 Sep  2  2021 threadtest.c
/root/libtest # ./cpptest
hello world
len=11
/root/libtest # ./mathtest
z=2+11i
z=2+1i
/root/libtest # ./threadtest
Hello World!
Hello Thread! arg=busybox
ret=end
/root/libtest #

[下一篇]

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值