显示标签为“make”的博文。显示所有博文
显示标签为“make”的博文。显示所有博文

2019-09-01

Linux-C


Linux-C

1. C程序
 1.1 最简单的C程序hello.c
 1.2 多个源码文件
 1.3 头文件(include)和目录
 1.4 C预处理器
 1.5 连接库
 1.6 共享库
  1.6.1 列出共享库的依赖关系
  1.6.2 ld.so怎样找到共享库
  1.6.3 环境变量LD_LIBRARY_PATH
2. 编译管理工具make
 2.1 make常用选项
 2.2 make常用变量
 2.3 makefile常见的宏
 2.4 makefile常规目标
 2.5 教程手册
 2.6 GNU autotools 安装步骤
 2.7 configure 常用的选项
 2.8 Autotools的基础知识(Gentoo开发人员手册)
 2.9 更多相关链接
3. 调试器gdb
4. Lex和Yacc
5. 脚本语言
6. Java

1. C程序

最基本的生成过程: 代码,编译,运行. 编译就会用到C编译器。
来自LLVM项目的新的C编译器clang越来越流行;但大部分主流的Unix系统上仍然是GNU C编译器gcc。

1.1 最简单的C程序hello.c

#include <stdio.h>

main() {
    printf("Hello, World. \n");
}

源码文件(.c); -o编译为可执行文件(.out); 运行
编译 $ cc hello.c
会产生 a.out 的可执行文件
指定文件名编译,增加-o选项
$ cc -o hello hello.c
运行 $ ./a.out

1.2 多个源码文件

多个源码文件(.c); -c编译为对象文件(.o); -o连接为可执行文件; 运行
main.c, aux.c ...
使用-c选项给每个文件生成对应的对象文件
$ cc -c main.c
$ cc -c aux.c
产生对象文件: main.o, aux.o ...
对象文件是一种二进制文件。
使用连接器将对象文件组合为可执行文件
$ cc -o myprog main.o aux.o

更复杂的项目, 更多个源码文件, 需要使用make

1.3 头文件(include)和目录

保存类型和函数声明的附加文件,比如stdio.h
Unix默认的include目录是/usr/include, 编译器一般就看这里, 除非你指定其他地方。
$ cc -c -I /usr/junk/include text.c
使用-I选项指定头文件路径

#include <stdio.h>
#include "myheader.h"
<*.h>头文件在系统路径;
"*.h"表示头文件不在系统的include目录中, 通常表示它与源码在同一目录。

1.4 C预处理器

并不是C编译寻找头文件, 而是C预处理器(C preprocessor, cpp)。
C预处理器是编译器在解析程序之前先在源码上运行的一个东西。
C预处理器会将源码重写成一种编译器能理解的形式,使源码更易读(并提供捷径)。

源码中的预处理器命令叫做指令(directive), 以#开头, 分为如下三种。
inculde文件: #inculde * 使预处理器将整个文件包含进来。
宏定义: #define BLAH something 预处理器会将源码中所有BLAH替换为something, 约定宏名为大写。
条件: 可用 #ifdef, #if, #endif 来对代码进行分快。
#ifdef MACRO 指令用于检查宏MACRO是否已定义;
#if condition 则检查condition 是否非零。当预处理器发现if语句后的条件为false时,就不会将#if 和 #endif 之间的代码交给编译器。

注:也可以不在源码中定义宏,而使用编译器的-D选项,(-DBLAH=something)(#define BLAH something)
C预处理器并不懂C的任何语法,变量,函数或其他元素,它只看宏和指令。
Unix上的C预处理器是cpp,也可以用 gcc -E 来运行,不过一般很少需要单独运行预处理器。

1.5 连接库

所谓C库,就是一些已编译好的,通用的,可让你添加到自己程序的函数。例如很多可执行程序会用到的数学库...
库主要实在连接的时候(连接器从对象文件产生可执行程序时)发挥作用。
默认的库文件路径(/usr/lib)

使用编译器的 -l 选项连接库文件。
例如这里用到一般的gobject库文件是libgobject.a, 而库的名字是gobject. 所以完整的连接和编译如下:
$ cc -o textp textp.o -lgobject

使用 -L 选项指定库文件路径
例如库文件不在常规位置,而在/usr/junk/lib/libcrud.a
$ cc -o testp testp.o -lgobject -L/usr/junk/lib -lcrud

在库中搜索特定的函数,使用nm命令,nm libjobject.a, (可能要用locate命令查找libjobject.a, 很多发行版会将库放在/usr/lib特定的子目录)

1.6 共享库

名称以.a结尾的库是静态库。
连接静态库时,连接器会将库文件中的机器码复制到程序中。最终的可执行程序不需要该库也能运行。
使用静态库的优点是简单方便,不依赖环境。缺点也很明显:
  •   过多使用静态库,会使文件越来越大,占用空间。
  •   若库里的函数更新了,原有程序不会改变,需要重新编译才能使用到最新的库函数。
共享库即可解决这些问题。引用共享库的程序只会在需要时才将该库加载到内存中。而且多个进程可以共享内存中同一个共享库。
使用共享库的的缺点是管理困难,连接复杂。但只需搞定如下问题:
  •   如何列出程序需要的共享库;
  •   程序如何查找共享库;
  •   如何让程序连接共享库;
  •   常见的共享库陷阱。

1.6.1 列出共享库的依赖关系

共享库和静态库通常放在同一个地方,Linux的两大标准库目录/lib, /usr/lib. 其中/lib是不应该包含静态库的。
共享库的名字后缀通常含有.so(意为共享对象)
$ ldd /bin/bash
linux-vdso.so.1 (0x00007fff77fe7000)
libreadline.so.8 => /usr/lib/libreadline.so.8 (0x00007f60035ec000)
libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f60035e7000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f6003424000)
libncursesw.so.6 => /usr/lib/libncursesw.so.6 (0x00007f60033b5000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f6003750000)
考虑到最佳性能和灵活性,可执行程序本身通常不知道它所用的共享库在哪里,只知道共享库名字,或只知道一点点寻找共享库的提示,如上左边是共享库名字。
ld.so(运行时动态连接器/加载器)可以为程序在运行时找到并加载共享库。如上ldd输出的右边是ld.so找到的库的位置。
最后一行显示了ld.so的实际位置。

1.6.2 ld.so怎样找到共享库

通常的顺序是如下的步骤(1),(2); 特殊情况才会使用(0)这个步骤。
  • (0) 参考环境变量LD_LIBRARY_PATH.(没有源码,又不能使用patchelf指定,就可以用这个了)
  • (1) 通常首先查找(可执行程序预先配置好的运行时搜索路径(如果有配置) runtime library search pach, 简称rpath);
  • (2) 接着,会参考系统缓存/etc/ls.so.cache, 看看该库是否在常规的位置。这是从缓存配置文件/etc/ld.so.conf中的目录列表获取的库文件名字的快速缓存。
注: 如其他配置文件一样,ld.so.conf 可能会包含 /etc/ld.so.conf.d中的配置。
标准的库目录/lib和/usr/lib是隐式的,即不需要包含在如上的配置文件中。
如果改动了ld.so.conf或者改动了某个共享库的目录,需要重建共享库缓存
$ ldconfig -v
-v 选项会输出被ldcongfig添加到缓存的目录的详细信息和他所监测到的改动。

编译时指定程序的rpath
$ cc -o test test.o -Wl,-rpath=/opt/obscure/lib -L/opt/obscure/lib -lweird
已编译的程序可用pathchelf加入不同的rpath, 不过最好还是在编译时就做好。

1.6.3 环境变量LD_LIBRARY_PATH

环境变量LD_LIBRARY_PATH, 用冒号分隔多个路径。如非必要,请勿滥用。
* 永远都不要在启动文件中或在编译软件时设置LD_LIBRARY_PATH。
在没有源码,又不能使用patchelf指定路径,最后的办法。
但也请将它嵌套进shell脚本里。例如:
#!/bin/sh
LD_LIBRARY_PATH=/opt/crummy/lib
export LD_LIBRARY_PATH
exec /opt/crummy/bin/crummy.bin $@

不使用 LD_LIBRARY_PATH 避免大部分共享库问题,但还可能遇到应用程序接口API的改变,导致装好的软件异常。
最好的解决办法就是预防,在安装库时也使用 -Wl, -rpath, 或者使用静态库。

2. 编译管理工具make

您需要一个名为makefile的文件来告诉您make该怎么做。通常,makefile告诉make如何编译和链接程序。

make有自己内置的规则,当你需要.o文件时,他就会自动去找.c文件,并会对.c文件运行cc -c命令,已达到获得.o文件的目标。
编译时通过一个 Makefile 文件进行,把这个 Makefile 文件置于 hello.c 同一目录下.

make命令的使用
大多数的make都支持“makefile”和“Makefile”这两种默认文件名。
特定的Makefile,你可以使用make的“-f”和“--file”参数,如:make -f Make.Linux。

Makefile对格式有要求。
注释#开头; 宏定义; 目标:描述; 都顶头开始,前面不需要空格或tab缩进。
任何真实的命令前面都必须要有tab键(不能使用空格键)。

2.1 make常用选项

  •  -f FILE, 将FILE文件作为makefile。
  •  -n, 显示一次构建所要用到的命令,但并不执行。
  •  -p, 打印make的内部数据库。
  •  -d, 打印大量调试信息。
  •  --trace, 打印跟踪信息。

2.2 make常用变量

$* 当前目标的基名。 f1.txt, $*就代表这里的f1
$< 指第一个前置条件。
$@ 写在规则里时,表示当前目标。
$(@D) $(@F) 分别指$@目标的目录名和文件名。
$(<D) $(<F) 分别指$<条件的目录名和文件名
$? 比目标更新的所有前置条件。 t: p1 p2, 若p2时间戳比t新,$?就代表p2
$^ 指所有前置条件,之间空格分隔。

2.3 makefile常见的宏

CFLAGS C编译器选项。make会将这个选项作为参数,在将.c变为.o的阶段传给编译器。
LDFLAGS 类似CFLAGS, 不过他是在将.o变为可执行程序的阶段传给编译器。
LDLIBS 如果使用了LDFLAGS,但不想库名选项与查找路径混在一起,可以将库名选项写在这里。
CC C编译器,默认是cc。指定为clang可用 $ make CC=clang
CPPFLAGS C预处理器选项。make运行预处理器时,将其作为参数。
CXXFLAGS GNU使用这个宏作为C++编译器选项。

2.4 makefile常规目标

clean 通常会吧所有对象文件和可执行程序都清掉,以便重新构建或者打包软件。 rm -f … //这个目标无处不在
distclean 它能删除原包以外的所有东西,包括Makefile。有些可发者更喜欢用 realclean.
//GNU autotools所生成的Makefile总会有这个目标。
install 将文件和编译好的程序放到Makefile认为适当的地方。可能有风险,最好先用make -n install看看会放在哪里。
test 或 check 检验构建出的东西是否可用。
depend 通过编译器的-M选项来检查源码,以建立依赖关系。 //这是一个不寻常的目标,因为它经常会改动Makefile自身。
all 通常是Makefile的地一个目标

https://www.gnu.org/software/make/manual/make.html#Makefile-Contents
Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。
  • 显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
  • 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
  • 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
  • 文件指示。其包括了三个部分,
    • 一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;
    • 另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;
    • 还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
  •  注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“\#”。

2.5 教程手册

入门简易版
https://zhuanlan.zhihu.com/p/47390641
Makefile由浅入深--教程、干货

《Makefile文件教程》
https://gist.github.com/isaacs/62a2d1825d04437c6f08

http://www.ruanyifeng.com/blog/2015/02/make.html
Make 命令教程  阮一峰  2015年2月20日
http://www.ruanyifeng.com/blog/2015/03/build-website-with-make.html
使用 Make 构建网站

完整版《GNU Make手册》
https://www.gnu.org/software/make/manual/make.html

https://seisman.github.io/how-to-write-makefile/introduction.html
跟我一起写Makefile
https://blog.csdn.net/ruglcc/article/details/7814546
https://blog.csdn.net/haoel/article/details/2886

2.6 GNU autotools 安装步骤

GNU autoconf 是一套流行的,用于自动产生Makefile的系统。解决跨平台的问题。
使用此套系统的包都会带有 configure, Makefile.in, config.h.in文件。其中.in是模板文件。

默认生成的Makefile中的install目标通常使用/usr/local作为前缀;二进制程序会去到/usr/local/bin. 库会到/usr/local/lib.
GUN autoconf及很多其他软件包的默认前缀都是/usr/local。他是本地安装软件的传统位置。操作系统不会更新/usr/local里的软件。所以更新不会是你哪里的东西丢失。

使用这类带配置的源码包安装步骤大致如下:
01 下载: 官网下载...
02-1 验证签名: $ gpg --verify *.sig . 验证签名
02-2 验证文件: $ md5sum 验证文件. md5sum,sha1sum,sha256sum验证文件。
03-1 包内容: $ tar -tvf 首先用t选项查看,避免有绝对路径等问题;
03-2 解包: $ tar -xvf[压缩选项] 再使用xvf[压缩选项]解包。
04 查看说明: $ cat readme 查看自述文件,install安装说明等
05 配置: $ ./configure 分析当前系统的特性,然后在Makefile.in文件的基础上做一些替换,创建出适合本机的构建文件:Makefile
06-1 空跑: $ make -n 显示一次构建所要用到的命令,但并不执行。
06-2 编译: $ make
07 检查: $ make check 若了解程序,也可是尝试运行生成的可执行文件。
08-1 空跑: $ make -n install 空跑一次,查看安装那些东西到哪里。
08-2 安装: $ make install
09 清理: $ make clean 清除编译连接过程中的一些临时文件
10 卸载: $ make uninstall 卸载相关应用程序

2.7 configure 常用的选项

  • --prefix=directory:  指定安装位置。
  • --bindir=directory: 指定可执行程序位置。
  • --sbindir=directory: 指定系统级的可执行程序位置。
  • --libdir=directory: 指定库位置。
  • --disable-shared: 不构建共享库(要看具体什么库)。
  • --with-package=directory: 告诉configure需要用到=directory: 目录的包。当某个库不在标准位置时,使用这个选项。但并非所有的configure脚本都能识别这个选项。

2.8 Autotools的基础知识(Gentoo开发人员手册)

https://devmanual.gentoo.org/general-concepts/autotools/index.html
主要的Autotools组件
Autotools是相关软件包的集合,当它们一起使用时,消除了创建便携式软件所涉及的许多困难。这些工具以及一些相对简单的上游提供的输入文件用于为包创建构建系统。https://devmanual.gentoo.org/general-concepts/autotools/diagram.png

在一个简单的设置:
  • 该autoconf程序configure从任一configure.in或产生一个脚本 configure.ac(见下面的注释)。
  • 该automake程序产生一个Makefile.in来自Makefile.am。
  • configure运行 该脚本以Makefile从 Makefile.in文件生成一个或多个文件。
  • 该make程序使用Makefile编译程序。
...

https://wiki.gentoo.org/wiki/Autotools
Autotools是一个在开源项目中常用的构建系统。虽然很常见,但并非每个开发人员都喜欢使用自动工具。一些项目试图避免这种构建系统。

https://devmanual.gentoo.org/eclass-reference/autotools.eclass/index.html
autotools.eclass - 重新生成auto *构建脚本
此eclass用于安全处理需要重新生成其构建脚本的自动化软件包。如果出现错误,所有功能都将中止。

https://wiki.gentoo.org/wiki/Comparison_of_build_systems
构建系统的比较 - 提供各种构建系统的简要比较。
https://en.wikipedia.org/wiki/List_of_build_automation_software#Build_script_generation_tools

2.9 更多相关链接

http://www.gnu.org/software/autoconf/
http://www.gnu.org/software/automake/

https://autotools.io/index.html
Autotools Mythbuster  Diego Elio “Flameeyes” Pettenò   作者和出版商 <flameeyes@flameeyes.com>
Autotools Mythbuster是一个严肃的Autotools指南,旨在提供GNU构建链中工具的完整集成视图:autoconf,automake,libtool,pkg-config等。

https://blogs.gentoo.org/lu_zero/2009/03/24/cmake-vs-autotools-a-benchmark/

==============
https://devmanual.gentoo.org/eclass-reference/autotools-utils.eclass/index.html
autotools-utils.eclass - 基于autotools的软件包的常见ebuild函数

==============
https://wiki.gentoo.org/wiki/Project:Quality_Assurance/Autotools_failures
本指南旨在描述使自动工具无法在ebuild中运行的常见情况,并提供有关如何解决这些问题的建议。
介绍
与术语自动工具,我们通常所说的由GNU工程创建平台和操作系统无关构建系统,开发工具autoconf,automake和libtool。虽然并非每个包装都同时使用所有包装,但大多数现代包装都是这样做的; 旧的包通常不使用automake和libtool替代; KDE包使用更复杂的构建系统,最终依赖于上述三个软件。

很容易识别构建系统基于autotools的软件包:
  • 如果有配置脚本,以及configure.in或configure.ac文件,构建系统基于autoconf ;
  • 如果各个子目录中有一个或多个Makefile.am文件,它也是automake基于;
  • 如果有一个ltmain.sh脚本,它也在使用libtool。

要构建一个使用基于autotools的构建系统的软件包,这些工具本身并不是绝对需要的:configure脚本是一个简单的Bourne Shell脚本(通常,但这将在最近讨论)并将Makefile.in文件转换为简单的Makefile的make(或者,更多的时候,gmake)。尽管它们是构建软件的可选项,但解决诸如--as-need构建失败或自动依赖性等问题所需的补丁通常需要重新运行工具来重新创建脚本和makefile的模板。

本指南不会说明如何使用autotools修复软件包的错误,因为这是一个需要解释很多内容的广泛主题。有关使用autotools时最常见错误的简单介绍,建议使用autotools文章阅读最佳实践。相反,它将描述重新运行autotools导致失败的常见情况,无论是在重建脚本还是在构建时。
================

https://en.wikipedia.org/wiki/GNU_Build_System
https://en.wikipedia.org/wiki/Automake
https://en.wikipedia.org/wiki/Configure_(computing)
https://en.wikipedia.org/wiki/Make_(software)
https://en.wikipedia.org/wiki/Autoconf
https://en.wikipedia.org/wiki/CMake

https://en.wikipedia.org/wiki/Meson_(software)
https://en.wikipedia.org/wiki/Configure_script
https://en.wikipedia.org/wiki/Pkg-config

https://en.wikipedia.org/wiki/GNU_Debugger
https://en.wikipedia.org/wiki/GNU_Bison
https://en.wikipedia.org/wiki/Berkeley_Yacc

https://en.wikipedia.org/wiki/GNU_Compiler_Collection
https://en.wikipedia.org/wiki/List_of_compilers
此页面旨在列出所有当前编译器,编译器生成器,解释器,转换器,工具基础,汇编程序,可自动执行的命令行界面(shell)等。

===========
https://en.wikipedia.org/wiki/List_of_GNU_packages
GNU包列表
GNU工具链
主要文章:GNU工具链
  • GNU Binutils - 包含GNU汇编程序(as)和GNU链接程序(ld)
  • GNU bison - 用于替换yacc的解析器生成器
  • GNU构建系统(autotools) - 包含Autoconf,Automake,Autoheader和Libtool
  • GNU Compiler Collection - 针对许多编程语言优化编译器,包括C,C ++,Fortran,Ada和Java
  • GNU调试器(gdb) - 一种高级调试器
  • GNU m4 - 宏处理器
  • GNU make - 为GNU制作程序
https://wiki.archlinux.org/index.php/GNU#Build_system
https://www.gnu.org/

构建系统autotools
autoconf
CMake
SCons

3. 调试器gdb

gdb 全称是 GNU Debugger,是 GNU 开源组织发布的一个强大的 UNIX 下的程序调试工具。
gdb 主要可帮助工程师完成下面 4 个方面的功能:

启动程序,可以按照工程师自定义的要求随心所欲的运行程序。
让被调试的程序在工程师指定的断点处停住,断点可以是条件表达式。
当程序被停住时,可以检查此时程序中所发生的事,并追索上文。
动态地改变程序的执行环境。

https://wiki.archlinux.org/index.php/Debug_-_Getting_Traces
https://wiki.archlinux.org/index.php/Step-by-step_debugging_guide
https://wiki.gentoo.org/wiki/Project:Quality_Assurance/Backtraces#Introducing_gdb
$ gdb -q /bin/ls
Reading symbols from /bin/ls...
(No debugging symbols found in /bin/ls)
(gdb) set args /usr/share/fonts
(gdb) run
Starting program: /usr/bin/ls /usr/share/fonts
OpenImageIO  adobe-source-code-pro  cantarell  gsfonts misc  noto-cjk
TTF      adobe-source-han-sans  encodings  mathjax noto
[Inferior 1 (process 26487) exited normally]
(gdb) q

界面友好的 Eclipse IDE 和 Emacs 系统也是Linux支持的。
想挖掘内存问题和生成统计信息,试试 Valgrind http://valgrind.org

https://www.ibm.com/developerworks/cn/linux/1508_zhangdw_gdb/index.html
使用 GDB 和 KVM 调试 Linux 内核与模块

4. Lex和Yacc

如果你要编译的程序需要读取配置文件或命令,那你可能要用到Lex和Yacc。这两个工具是用于制作编程语言的。

Lex, Lexical Analyzar 词法分析器的生成器,能将文本内容转换成一个个标记。
GNU/Linux版本叫做flex。可以使用编译器的-ll或-lfl连接器标记来连接Lex库。

Yacc, Yet Another Compiler Compiler 语法解析器的生成器,能根据语法来读取标记。
GNU的解析器是 bison。为使生成的语法分析器能与yacc兼容,需要执行bison -y。可以使用编译器的-ly连接器标记来连接Yacc的库。

https://www.ibm.com/developerworks/cn/linux/sdk/lex/index.html
Yacc 与 Lex 快速入门

Lex
Lex 是一种生成扫描器的工具。扫描器是一种识别文本中的词汇模式的程序。 这些词汇模式(或者常规表达式)在一种特殊的句子结构中定义。
Lex 和 C 是强耦合的。一个 .lex 文件(Lex 文件具有 .lex 的扩展名)通过 lex 公用程序来传递,并生成 C 的输出文件。这些文件被编译为词法分析器的可执行版本。

Lex 编程可以分为三步:
以 Lex 可以理解的格式指定模式相关的动作。
在这一文件上运行 Lex,生成扫描器的 C 代码。
编译和链接 C 代码,生成可执行的扫描器。

一个 Lex 程序分为三个段:
第一段是 C 和 Lex 的全局声明,第二段包括模式(C 代码),第三段是补充的 C 函数。
例如, 第三段中一般都有 main() 函数。这些段以%%来分界。

Yacc
Yacc 代表 Yet Another Compiler Compiler。 Yacc 的 GNU 版叫做 Bison。它是一种工具,将任何一种编程语言的所有语法翻译成针对此种语言的 Yacc 语 法解析器。它用巴科斯范式(BNF, Backus Naur Form)来书写。按照惯例,Yacc 文件有 .y 后缀。

用 Yacc 来创建一个编译器包括四个步骤:
通过在语法文件上运行 Yacc 生成一个解析器。
说明语法:
编写一个 .y 的语法文件(同时说明 C 在这里要进行的动作)。
编写一个词法分析器来处理输入并将标记传递给解析器。 这可以使用 Lex 来完成。
编写一个函数,通过调用 yyparse() 来开始解析。
编写错误处理例程(如 yyerror())。
编译 Yacc 生成的代码以及其他相关的源文件。
将目标文件链接到适当的可执行解析器库。

如同 Lex 一样, 一个 Yacc 程序也用双百分号分为三段。 它们是:
声明、语法规则和 C 代码。


5. 脚本语言

Unix中,所有#!开头的可执行文本文件都是脚本,后面的路径是该脚本的解释器。
#!/usr/bin/python
#!/usr/bin/env python
#!/usr/bin/tail -2

常见的脚本语言
  • Python: 文本处理,数据库访问,网络编程,多线程等,支持者众多。还有强大的交互模式和一套有组织的对象模型。David M. Beazley, Python Essential Reference, 4th edition (Addison-Wesley, 2009)
  • Perl: 是Unix上较为老旧的第三方脚本语言。是编程界的“瑞士军刀”,近年被Python超越。
  • PHP: 超文本处理语言,常用于动态网页编程。http://www.php.net
  • Ruby: 面向对象的爱好者和Web开发者尤其喜欢。http://www.ruby-lang.org
  • JavaScript: 有一种实现Node.js,可执行程序是node。
  • Emacs Lisp: Lisp语言的一个变种。
  • Matlab和Octave: Matlab是一套商业矩阵及数学编程语言和库。Octave是类似Matlab的免费软件。
  • R: 流行的免费统计分析语言。http://www.r-project.org
  • Mathematica: 商业的数学编程语言和库。
  • m4: 宏处理语言,常见与GNU autotools。
  • Tcl: 工具命令语言,是一种简单的脚本语言,其扩展有图形界面的Tk和自动化工具Expect。http://tcl.tk

6. Java

Java跟C一样都是编译型语言,他有更简单的语法和强大的面向对象能力。
多用于Web应用和一些特定的应用。Android应用就通常使用Java来开发的。

Java编译器分为两种:
用于生成机器码供系统使用的本地编译器(如C编译器);
字节码解释器(有时也叫虚拟机)使用的字节码编译器。在Linux上看到的Java程序都是字节码。(.class文件)

Java运行时环境(Java Runtime Environment, JRE)包含了运行Java字节码所需的程序。运行一个字节码:
$ java file.class
$ java -jar file.jar
(.jar)文件是由一堆.class文件打包而成的字节码文件。

有时需要将java的安装路径设置到JAVA_HOME环境变量中,甚至可能还需要使CLASSPATH变量包含你程序需要的所有class文件目录。

需要使用(Java Development Kit, JDK)Java开发工具,将.java文件编译为字节码:
$ javac file.java
JDK还包含了jar程序,创建和拆分.jar文件,用法类似tar。





2019-07-03

Compile-kernel-module

1. 内核模块编程
1.1 简介
1.2 加载内核模块
1.3 最简单的模块
1.4 模块必要信息
1.4.1 内核模块必须至少包含的头文件:
1.4.2 内核模块必须至少有两个功能:
1.4.3 printk()日志记录
1.4.4 优先级
1.4.5 许可和模块文档
1.5 编译内核模块
1.6 实战
1.6.1 源代码文件: hello.c
1.6.2 Makefile文件: Makefile
1.6.3 使用make编译
1.6.4 使用modinfo查看新的模块文件
1,6.5 使用insmod加载内核模块
1.6.6 使用lsmod查看已加载的模块
1.6.7 使用rmmod卸载模块
1.6.8 使用dmesg查看日志
1.6.9 练习
1.6.10 其他信息
1.7 更多基础示例链接
1.7.1 将命令行参数传递给模块
1.7.2 跨越多个文件的模块
1.7.3 构建预编译内核的模块
1.7.4 与应用程序交互
2 make工具
2.1 DESCRIPTION
2.2 退出状态
2.3 选项
3. 其他内核编译方法及相关连接
3.1 编译Arch的内核模块
3.2 Arch构建系统
3.3 内核/传统编译
3.4 修补包
3.5 模块与程序

1. 内核模块编程

http://www.tldp.org/LDP/lkmpg/2.6/html/
Linux内核模块编程指南 2007-05-18见2.6.4

1.1 简介

什么是内核模块?模块是可以根据需要加载和卸载到内核中的代码片段。它们扩展了内核的功能,而无需重启系统。
例如,一种类型的模块是设备驱动程序,它允许内核访问连接到系统的硬件。还有很多的文件系统等。

1.2 加载内核模块

您可以通过运行lsmod来查看已经加载到内核中的模块,lsmod通过读取文件/proc/modules来获取其信息。
执行modprobe来加载模块.modprobe以两种形式之一传递一个字符串:
  > 模块名称,如softdog或ppp。
  > 一个更通用的标识符,如char-major-10-30。
如果modprobe被赋予通用标识符,它首先在文件/etc/modprobe.conf中查找该字符串。如果找到如下的别名行:alias char-major-10-30 softdog
它知道通用标识符引用模块softdog.ko。
然后,modprobe查看文件/lib/modules/version/modules.dep,查看是否必须加载其他模块才能加载所请求的模块。该文件由depmod -a创建,包含模块依赖项。
      例如,msdos.ko要求fat.ko模块已经加载到内核中。
最后,modprobe使用insmod首先将任何必备模块加载到内核中,然后加载所请求的模块。
modprobe将insmod指向/lib/modules/'uname -r'/, 模块的标准目录。

insmod对于模块的位置是相当愚蠢的,而modprobe知道模块的默认位置,知道如何找出依赖关系并以正确的顺序加载模块。
因此,例如,如果要加载msdos模块,则必须运行:
$ insmod /lib/modules/2.6.11/kernel/fs/fat/fat.ko
$ insmod /lib/modules/2.6.11/kernel/fs/msdos/msdos.ko

要么:
$ modprobe msdos
所以,insmod要求你传递完整的路径名并以正确的顺序插入模块,而modprobe只取名字,没有任何扩展名,并通过解析/lib找出它需要知道的所有内容/modules/version/modules.dep。

1.3 最简单的模块

http://www.tldp.org/LDP/lkmpg/2.6/html/x121.html
Example 2-1. hello-1.c
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
int init_module(void)
{
printk(KERN_INFO "Hello world 1.\n");
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "Goodbye world 1.\n");
}

1.4 模块必要信息

1.4.1 内核模块必须至少包含的头文件:

#include <linux/module.h> /* module_init() module_exit() 函数来注册模块入口和退出处理。
#include <linux/kernel.h> /* Needed for KERN_INFO ,仅用于printk()日志级别的KERN_ALERT的宏扩展 */
#include <linux/init.h> /* Needed for the macros */

1.4.2 内核模块必须至少有两个功能:

  • 一个“开始”(初始化)功能调用 的init_module()当模块insmoded到内核被称为,
  • 以及“结束”(清理)函数调用在cleanup_module()这是刚刚称为在它被破坏之前。
从Linux 2.4开始,您可以重命名模块的init和cleanup功能; 它们不再需要分别被称为 init_module()和cleanup_module()。
这是通过 module_init()和module_exit()宏完成的。这些宏在linux / init.h中定义。
唯一需要注意的是,必须在调用宏之前定义init和cleanup函数,否则会出现编译错误。

1.4.3 printk()日志记录

由于代码运行在内核空间里面,不能直接使用用户空间的 print 函数,而要使用内核中的 printk 函数.
作为日志记录机制,用于记录信息或发出警告。
每个printk() 语句都带有一个优先级,即您看到的<1>和KERN_ALERT。
有8个优先级,内核有宏,所以你不必使用神秘的数字,你可以在linux/kernel.h中查看它们(及其含义)。
如果未指定优先级,则将使用默认优先级DEFAULT_MESSAGE_LOGLEVEL。

花点时间阅读优先级宏。头文件还描述了每个优先级的含义。
在实践中,不要使用数字,如<4>。始终使用宏,如 KERN_WARNING。

如果优先级低于int console_loglevel,则会在当前终端上打印消息。
如果syslogd和klogd都在运行,那么该消息也将附加到/var/log/messages,无论它是否打印到控制台。
我们使用高优先级(如KERN_ALERT)来确保将printk()消息打印到控制台而不是仅记录到日志文件中。
编写实际模块时,您需要使用对当前情况有意义的优先级。

1.4.4 优先级

https://szosoft.blogspot.com/2019/06/linux-journal.html#1021
cat  /usr/lib/modules/5.1.15-arch1-1-ARCH/build/include/linux/kernel.h
cat  /usr/lib/modules/5.1.15-arch1-1-ARCH/build/include/linux/printk.h
内核模块printk no (Key)journal
KERN_EMERG 0 Emergency 紧急
KERN_ALERT 1 Alert 警报
KERN_CRIT 2 Critical 危急
KERN_ERR 3 Error 错误
KERN_WARNING 4 Warning 警告
KERN_NOTICE 5 Notice 注意
KERN_INFO 6 Informational 信息
KERN_DEBUG 7 Debug 调试

1.4.5 许可和模块文档

MODULE_LICENSE("GPL");  /* Get rid of taint message by declaring code as GPL.  */
MODULE_AUTHOR("Tom"); /* Who wrote this module? */
MODULE_DESCRIPTION("Test"); /* What does this module do */
MODULE_VERSION("0.0.1");
MODULE_SUPPORTED_DEVICE("testdevice");   /* 声明模块支持哪些类型的设备。 */
在内核2.4及更高版本中,设计了一种机制来识别在GPL(和朋友)下许可的代码,以便可以警告人们代码是非开源的。
这是通过MODULE_LICENSE()宏实现的。通过将许可证设置为GPL,可以防止打印警告。
此许可证机制在linux/module.h中定义并记录:
$ cat  /usr/lib/modules/5.1.15-arch1-1-ARCH/build/include/linux/module.h |grep MODULE_

/*
 * 目前接受以下许可证标识为免费软件模块
 * "GPL" [GNU Public License v2 or later]
 * "GPL v2" [GNU Public License v2]
 * "GPL and additional rights" [GNU Public License v2 rights and more]
 * "Dual BSD/GPL" [GNU Public License v2 or BSD license choice]
 * "Dual MIT/GPL" [GNU Public License v2 or MIT license choice]
 * "Dual MPL/GPL" [GNU Public License v2 or Mozilla license choice]
 *
 * 以下其他标识可供选择
 * "Proprietary" [Non free products]
 */

1.5 编译内核模块

http://www.tldp.org/LDP/lkmpg/2.6/html/x181.html
内核模块的编译需要与常规用户空间应用程序略有不同。
以前的内核版本要求我们关注这些设置,这些设置通常存储在Makefile中。虽然按层次结构组织,但许多冗余设置在次级Makefile中累积并使它们变大并且难以维护。
幸运的是,有一种新方法可以做这些事情,称为kbuild,外部可加载模块的构建过程现在完全集成到标准内核构建机制中。
要了解有关如何编译不属于官方内核的模块的更多信息(例如本指南中的所有示例),请参阅文件 linux/Documentation/kbuild/modules.txt.

编译时通过一个 Makefile 文件进行,把这个 Makefile 文件置于 hello.c 同一目录下.
Makefile对格式有要求。每一行文本除非顶头开始,如果需要格式编排,不能使用空格键来控制文本行缩进,必须使用Tab键。

1.6 实战

1.6.1 源代码文件: hello.c

/*  hello.c - Demonstrates module documentation. */
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
#define DRIVER_AUTHOR "Peter Jay Salzman <p@dirac.org>"
#define DRIVER_DESC   "A sample driver"

static int __init init_hello(void)
{
printk(KERN_INFO "HelloWorld\n");
return 0;
}

static void __exit cleanup_hello(void)
{
printk(KERN_INFO "GoodbyeWorld\n");
}

module_init(init_hello);
module_exit(cleanup_hello);

MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR); /* Who wrote this module? */
MODULE_DESCRIPTION(DRIVER_DESC); /* What does this module do */
MODULE_SUPPORTED_DEVICE("testdevice");

1.6.2 Makefile文件: Makefile

obj-m += hello.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

从技术角度来看,第一行确实是必要的,为了方便起见,添加了“全部”和“清洁”目标。
现在您可以通过发出命令make来编译模块。您应该获得类似于以下内容的输出:

1.6.3 使用make编译

$ make
make -C /lib/modules/5.1.15-arch1-1-ARCH/build M=/home/toma/ko modules
make[1]: Entering directory '/usr/lib/modules/5.1.15-arch1-1-ARCH/build'
  CC [M]  /home/toma/ko/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/toma/ko/hello.mod.o
  LD [M]  /home/toma/ko/hello.ko
make[1]: Leaving directory '/usr/lib/modules/5.1.15-arch1-1-ARCH/build'

请注意,内核2.6引入了一种新的文件命名约定:内核模块现在具有.ko 扩展名(代替旧的.o扩展名),可以轻松地将它们与传统的目标文件区分开来。
原因是它们包含一个额外的.modinfo部分,其中保留了有关该模块的其他信息。我们很快就会看到这些信息有什么用处。
有关内核模块的Makefile的更多详细信息,请参见 linux/Documentation/kbuild/makefiles.txt .

$ ls -l   //编译后查看文件列表
name size
Makefile 154   //make文件
hello.c 786      //源文件
hello.ko 4528   //内核文件
hello.mod.c 646
hello.mod.o 2712
hello.o 2664   //目标文件
modules.order 30
Module.symvers 0

1.6.4 使用modinfo查看新的模块文件

$ modinfo hello.ko
filename:       /home/toma/ko/hello.ko
version:        0.0.1
description:    Test
author:         Tom
license:        GPL
srcversion:     BC3C3A49026E0297D738AE7
depends:     
retpoline:      Y
name:           hello
vermagic:       5.1.15-arch1-1-ARCH SMP preempt mod_unload

1,6.5 使用insmod加载内核模块

$ sudo insmod ./hello.ko

1.6.6 使用lsmod查看已加载的模块

$ lsmod |grep hello
Module                  Size  Used by
hello                  16384  0

1.6.7 使用rmmod卸载模块

$ sudo rmmod hello
$ lsmod |grep hello

1.6.8 使用dmesg查看日志

$ dmesg |tail
...
[562050.818179] HelloWorld
[562458.113633] GoodbyeWorld

1.6.9 练习

练习1
请参阅init_module()中 return语句上方的注释 ?将返回值更改为负值,重新编译并再次加载模块。怎么了?
$ sudo insmod ./hello-2.ko
insmod: ERROR: could not insert module ./hello-2.ko: Operation not permitted
$ dmesg |tail
...
[546135.110381] HelloWorld2.

练习2
将代码中许可的部分删除,再编译看看。
$ make
make -C /lib/modules/5.1.15-arch1-1-ARCH/build M=/home/toma/ko modules
make[1]: Entering directory '/usr/lib/modules/5.1.15-arch1-1-ARCH/build'
  CC [M]  /home/toma/ko/hello-1.o
  Building modules, stage 2.
  MODPOST 1 modules
style="color: red; font-size: xx-small;">WARNING: modpost: missing MODULE_LICENSE() in /home/toma/ko/hello-1.o
see include/linux/module.h for more information
  CC      /home/toma/ko/hello-1.mod.o
  LD [M]  /home/toma/ko/hello-1.ko
make[1]: Leaving directory '/usr/lib/modules/5.1.15-arch1-1-ARCH/build'

1.6.10 其他信息

现在看一下linux/drivers/char/Makefile的真实示例。
正如你所看到的,有些东西被硬件连接到内核(obj-y)但是那些obj-m去了哪里?
那些熟悉shell脚本的人很容易发现它们。
对于那些没有的,你看到的obj - $(CONFIG_FOO)条目会扩展为obj-y或obj-m,具体取决于CONFIG_FOO变量是否已设置为y或m。
虽然我们在这里,但那些正是你在linux/.config文件中设置的那种变量,最后一次你说make menuconfig 或类似的东西。

1.7 更多基础示例链接

1.7.1 将命令行参数传递给模块

http://www.tldp.org/LDP/lkmpg/2.6/html/x323.html

1.7.2 跨越多个文件的模块

http://www.tldp.org/LDP/lkmpg/2.6/html/x351.html

1.7.3 构建预编译内核的模块

http://www.tldp.org/LDP/lkmpg/2.6/html/x380.html

1.7.4 与应用程序交互

https://www.oschina.net/translate/writing-a-simple-linux-kernel-module

2 make工具

2.1 DESCRIPTION

make实用程序将自动确定需要重新编译大型程序的哪些部分,并发出命令以重新编译它们。
我们的示例显示了C程序,因为它们非常常见,但您可以使用make与任何编译语言,其编译器可以使用shell命令运行。
实际上,make并不仅限于程序。
您可以使用它来描述任何一些任务,其中某些文件必须在其他文件更改时自动从其他文件更新。

要准备使用make,您必须编写一个名为makefile的文件,该文件描述程序中文件之间的关系,以及用于更新每个文件的命令的状态。
在程序中,通常从目标文件更新可执行文件,而目标文件又通过编译源文件来完成。

一旦存在合适的makefile,每次更改一些源文件时,这个简单的shell命令:
make
足以执行所有必要的重新编译。
make程序使用makefile描述和文件的最后修改时间来决定需要更新哪些文件。
对于每个文件,它会发出makefile中记录的命令。

make执行makefile中的命令以更新一个或多个目标名称,其中name通常是程序。
如果不存在-f选项,make将按顺序查找 makefiles GNUmakefile, makefile, and Makefile, in that order.

(我们建议使用Makefile,因为它突出显示在目录列表的开头附近,紧邻其他重要文件,如README。)
检查的第一个名称,建议不要将GNUmakefile用于大多数makefile。
如果您具有特定于GNU make的makefile,则应使用此名称,并且其他版本的make不会理解该名称。
如果makefile为' - ',则读取标准输入。

2.2 退出状态

如果所有的makefile都被成功解析并且没有构建的目标失败,则GNU make退出状态为零。
如果使用-q标志并且make确定需要重建目标,则将返回状态1。
如果遇到任何错误,将返回状态2。

2.3 选项

-b, -m Ignored for compatibility. 忽略兼容性.
-B, --always-make Unconditionally make all targets. 无条件地制定所有目标.
-C DIRECTORY, --directory=DIRECTORY Change to DIRECTORY before doing anything. 在做任何事之前改为DIRECTORY.
-d Print lots of debugging information. 打印大量调试信息.
--debug[=FLAGS] Print various types of debugging information.  FLAGS可以用于所有调试输出(与使用-d相同),
b用于基本调试,v用于更详细的基本调试,i用于显示隐式规则,
j用于调用命令的详细信息,m用于在重新生成makefile时进行调试.
 使用n禁用所有先前的调试标志.
-e, --environment-overrides Environment variables override makefiles. 环境变量覆盖makefile.
--eval=STRING Evaluate STRING as a makefile statement. 将STRING评估为makefile语句.
-f FILE, --file=FILE, --makefile=FILE Read FILE as a makefile. 将FILE作为makefile读取.
-h, --help Print this message and exit. 打印此消息并退出.
-i, --ignore-errors Ignore errors from recipes.  忽略为重制文件而执行的命令中的所有错误.
-I DIRECTORY, --include-dir=DIRECTORY Search DIRECTORY for included makefiles. 搜索DIRECTORY以获取包含的makefile.
-j [N], --jobs[=N] Allow N jobs at once; infinite jobs with no arg. 一次允许N个工作;没有arg的无限工作.
-k, --keep-going Keep going when some targets can't be made. 当一些目标无法制作时继续前进.
-l [N], --load-average[=N],--max-load[=N] Don't start multiple jobs unless load is below N. 除非负载低于N,否则不要启动多个作业.
-L, --check-symlink-times Use the latest mtime between symlinks and target. 使用符号链接和目标之间的最新mtime.
-n, --just-print, --dry-run, --recon Don't actually run any recipe; just print them. 实际上不要运行任何配方;只需打印它们.
-o FILE, --old-file=FILE, --assume-old=FILE Consider FILE to be very old and don't remake it. 即使FILE很老,也不要重新生成它.
-O[TYPE], --output-sync[=TYPE] Synchronize output of parallel jobs by TYPE. 按TYPE同步并行作业的输出.
 当与-j并行运行多个作业时,请确保将每个作业的输出收集在一起,而不是穿插其他作业的输出.
 如果未指定type或是target,则将每个目标的整个配方的输出组合在一起.
 如果type为line,则配方中每个命令行的输出将组合在一起.
 如果type是recurse,则整个递归make的输出被组合在一起.
如果type为none,则禁用输出同步.
-p, --print-data-base Print make's internal database.  打印通过读取makefile产生的数据库(规则和变量值);
然后像往常一样或以其他方式指定执行.
要打印数据库而不尝试重新创建任何文件,请使用: make -p -f/dev/null.
-q, --question Run no recipe; exit status says if up to date. ``问题模式''.不要运行任何命令,也不要打印任何东西;如果指定的目标已经是最新的,则返回退出状态为零,否则返回非零值.
-r, --no-builtin-rules Disable the built-in implicit rules. 禁用内置隐式规则.
-R, --no-builtin-variables Disable the built-in variable settings. 禁用内置变量设置.
-s, --silent, --quiet Don't echo recipes.  无声操作;不要在执行时打印命令.
-S, --no-keep-going, --stop Turns off -k.  取消-k选项的效果.这是永远不必要的,
除了在递归make中,-k可能通过MAKEFLAGS从顶级make继承,
或者如果你在环境中的MAKEFLAGS中设置-k.
-t, --touch Touch targets instead of remaking them. 触摸目标而不是重新制作它们.
 这用于假装命令已完成,以欺骗未来的make调用.
--trace Print tracing information. 打印跟踪信息.
-v, --version Print the version number of make and exit. 打印make和exit的版本号.
-w, --print-directory Print the current directory. 打印当前目录.
--no-print-directory Turn off -w, even if it was turned on implicitly. 关闭-w,即使它是隐式打开的.
-W FILE, --what-if=FILE, --new-file=FILE, --assume-new=FILE Consider FILE to be infinitely new. 认为FILE是无限新的.
 没有-n,它几乎与在运行make之前在给定文件上运行touch命令相同,只是修改时间仅在make的想象中改变.
--warn-undefined-variables Warn when an undefined variable is referenced. 引用未定义的变量时发出警告.

3. 其他内核编译方法及相关连接

3.1 编译Arch的内核模块

https://wiki.archlinux.org/index.php/Compile_kernel_module
首先,您需要安装构建依赖项,例如compiler(base-devel)和linux-headers。

3.2 Arch构建系统

https://wiki.archlinux.org/index.php/Kernel/Arch_Build_System
https://www.kernel.org/doc/Documentation/kbuild/kconfig.txt
内核/Arch构建系统
该拱门构建系统可以用来构建基于官方的自定义内核的Linux软件包。
这种编译方法可以自动化整个过程,并且基于经过良好测试的软件包。
您可以编辑PKGBUILD以使用自定义内核配置或添加其他修补程序。
$ pacman -Ss asp
extra/asp 5-1    Arch Linux build source file management tool
安装的ASP封装和基devel的包组。
您需要一个干净的内核来开始自定义。通过运行以下命令从ABS获取最新的内核包文件到您的构建目录:
$ asp update linux
$ asp checkout linux
然后,从各自的源获取您需要的任何其他文件(例如,自定义配置文件,修补程序等)。

3.3 内核/传统编译

https://wiki.archlinux.org/index.php/Kernel/Traditional_compilation#Download_the_kernel_source
安装核心包
安装base-devel软件包组,其中包含必要的软件包,例如make和gcc。
还建议安装以下软件包,如默认的Arch内核PKGBUILD中所列:xmlto,kmod,inetutils,bc,libelf,git

3.4 修补包

https://wiki.archlinux.org/index.php/Patching_packages#Applying_patches
本文介绍如何创建以及如何在Arch Build System(ABS)中将补丁应用于包。
一个补丁描述了一组针对一个或多个文件线路的变化。补丁通常用于自动更改源代码。

3.5 模块与程序

http://www.tldp.org/LDP/lkmpg/2.6/html/x427.html
程序 模块
用户空间      内核空间
printf() printk()
main()开始 init_module或 通过module_init调用指定的函数 开始
  cleanup_module或 使用module_exit调用指定的函数 结束



AC-power

导航 (返回顶部) 1. AC 1.1 Alternating current 1.2 简谐交流电 1.3 频率 1.4 峰值和有效值 2. 交流电相位分类 2.1 单相电 2.2 三相电 2.3 比较 2.4 220v交流电的3个电压值 2.5 相电压与...