聊聊 C 语言和 ABAP 这两门编程语言的关系
![聊聊 C 语言和 ABAP 这两门编程语言的关系](https://static001.geekbang.org/infoq/92/923c9955744887877f0a3371f3f190d6.jpeg)
TIOBE 2022 年 3 月的编程语言排行榜显示:
https://www.tiobe.com/tiobe-index/
C 语言和 C++ 分别名列第二和第四位:
![](https://static001.geekbang.org/infoq/cd/cdca8607c504bc7bf9f53a7e61975836.png)
而 ABAP 编程语言名列第 42 位:
![](https://static001.geekbang.org/infoq/79/7989ec2a7775cf740a6f1cca205393b6.png)
为什么这篇文章要把 C 语言和 ABAP 放在一起讲,而不是别的编程语言比如 Java 和 ABAP 呢?因为 ABAP 语言底层是基于 C/C++ 实现的,包括其关键字(比如最简单的关键字 WRITE 的 C++ 实现有 2 千多行)和虚拟机(ABAP Runtime).
SAP 内部的一群计算机科学家们发明了 ABAP 这门在企业管理软件影响极其深远的编程语言,由它实现的各种 SAP 应用帮助了全球超过 180 个国家和地区的客户们更好地运行其业务。
通过 Google 我们能搜索到一些关于这些 SAP 计算机科学家们的介绍,比如这个链接。
![](https://static001.geekbang.org/infoq/0e/0e85460ab9a6cc462e23fd1bd226a166.png)
比如像下图这种用 kernel module 修饰的 sc_km_check_feature_2, 以及每一个 ABAP 关键字,其 C 语言的实现代码在 SAP 内部的 Netweaver 系统可以查看到,但是在客户系统上,则是以二进制目标文件的形式存储,无法查看源代码。
![](https://static001.geekbang.org/infoq/2d/2db0d83eccb8366800d7037d3d842339.png)
本文的目的是希望通过 C 语言和 ABAP 编译过程的一些介绍,加深 ABAP 开发人员们对这门语言的理解。
用 C 语言写个 Hello World 程序,另存为 study.c:
![](https://static001.geekbang.org/infoq/23/23e4a3d7c458bad6b9a5540f87bf232b.png)
用命令行 gcc ./study.c --verbose
进行编译,参数 verbose 可供我们查看编译明细。上述命令行在我的 Ubuntu 系统上产生一串长长的输出:
![](https://static001.geekbang.org/infoq/5e/5e8fe68be5d7ba13b31e4597e59f8617.png)
![](https://static001.geekbang.org/infoq/2c/2cc74eea3a1afe8e5dff6651f4c85713.png)
我们可以一步步分析。首先用参数 -E 查看预处理生成的目标文件 study.i:
gcc -E study.c -o study.i
可以看到源代码文件只有 78 字节,编译预处理后生成的输出文件有 17116 字节。
![](https://static001.geekbang.org/infoq/a0/a098866592529f62cee18b05b8b8ea02.png)
为什么膨胀了这么多?原因是因为我源代码文件的第一行,#include<stdio.h>
被预处理器替换成了 stdio.h
的实际内容,而 stdio.h
里如果又存在 #include
其他文件的声明,这个替换过程会递归执行。因此直到 study.i
的末尾部分,我们才能看到在 study.c 里书写的源代码部分。
![](https://static001.geekbang.org/infoq/18/18cd44f6339df10c3860ac7c81217a22.png)
源代码文件 study.c 里的第一行语句 #include<stdio.h>
, 请大家记住,后面讲 ABAP 还会提到。
用命令行 gcc -S 可以查看 study.c 编译后生成的汇编代码:
![](https://static001.geekbang.org/infoq/60/606fab796f7325a37a533158d3778fc4.png)
下面我们来聊聊 ABAP.
![](https://static001.geekbang.org/infoq/dd/dd983d03e71b02579f26f024d13126c2.png)
SAP note 1230076 Generation of ABAP loads: Tips for the analysis
介绍了一个工具程序:RSDEPEND。这个 note 提到,一个即便看起来最简单的 ABAP Hello World 报表,其实也依赖于许多标准的 Repository 对象,这些依赖我们假定称其为 A,B,C.
假设 A,B,C 其中有任何一个有改动产生,比如 A 是一个 include 程序,里面使用到了一个 DDIC 结构,在某个时刻,系统导入了一个传输请求(Transport Request), 里面包含了针对这个 DDIC 结构的更改,那么此时这个最简单的 Hello World 报表的 load 就成为了 obsolete 状态。在重新执行该报表之前,ABAP Runtime(中文译成 ABAP 运行时)会自动做一个 load invalidation
操作,生成一个最新版本的 load.
什么是 ABAP load?看 ABAP help 里的官方定义:
In the ABAP environment, a load describes a binary representation of a repository object which is optimized for fast access, in the memory or on the database.
翻译成中文:ABAP load 是 Repository 对象的二进制表现形式,针对 ABAP 环境的快速访问而做过特别优化,可以存储在数据库表中或者加载于内存里。
我们用一个实际的例子来理解 ABAP 报表激活和运行时发生的事情。
创建一张非常简单的透明表 ZLOADTEST:
![](https://static001.geekbang.org/infoq/7f/7fdb1efc84745aac6a9668dcb2c4bc1a.png)
写一个简单的报表,命名 为 ZTESTLOAD.报表的源代码以压缩的格式存储在表 REPOSRC 的 DATA 字段里。
![](https://static001.geekbang.org/infoq/5c/5c25e5aac767b66600c2366e514d08f3.png)
测试报表的源代码很简单,把表里的数据全部读取出来:
![](https://static001.geekbang.org/infoq/34/340b8e6d1f316cfdf74cf650cdc60d96.png)
激活这个简单的报表(是的,在 ABAP 世界里,我们习惯说激活,而不是编译)。激活后生成的 ABAP load 存储在表 REPOLOAD 的字段 LDATA 和 QDATA 里。
![](https://static001.geekbang.org/infoq/b9/b923cb46b27a15f0304eba4e599a7d30.png)
这两个字段存储的内容就是前面 ABAP help 提到的 ABAP load 在数据库表中的存储形式。
菜单 Goto->Navigate to->Switch to Classic Debugger:
![](https://static001.geekbang.org/infoq/0d/0d4a59ebf472ede99e787535ad2c0cad.png)
Goto->System Areas->Internal Information:
![](https://static001.geekbang.org/infoq/5d/5d71f340044287b235a9a10201a23d2f.png)
在 System Area 区域输入 CONT,就能在下图的 NAME 列看到 ABAP load 里包含的指令。当然同开源的 JVM 不同,JVM 字节码指令集在网上能够查到,而这些 ABAP load 的指令是 SAP internal 的,因此不能在这里做解释。
![](https://static001.geekbang.org/infoq/dd/dd7d075000ec6fd5a8dce12e433b3501.png)
然后执行前面提到的工具报表 RSDEPEND, 输入参数 program name = ZTESTLOAD, 得到结果,其中测试报表的 ABAP Load 时间戳为 07:21:02, 这个报表依赖的标准 Include 有:
![](https://static001.geekbang.org/infoq/8d/8d4d8d0eb8c525e85202bb11c8533c49.png)
![](https://static001.geekbang.org/infoq/70/709bf142d5de2f7ec3a1178e1e4c9c4c.png)
由此看出,每一个标准的 ABAP 报表都自动包含了这些 include.
如果开发人员显式地再包含其中任意一个,会遇到语法错误: Module %_PF_STATUS is already defined as a OUTPUT module.
![](https://static001.geekbang.org/infoq/d7/d78e4e515bca7f9516bbdf75701a094e.png)
![](https://static001.geekbang.org/infoq/0d/0d9dbdd8e7507cdcbd9a76b208c00e1a.png)
大家觉得这个 <REPINI>
是不是很像前文 C 语言部分提到的 #include<stdio.h>
?
下面我们再做几轮测试。
测试 1
修改透明表的描述信息,然后重新激活透明表。
执行 RSDEPEND
, 可以看到只有透明表的 Last Changed
字段发生了变化,ABAP Time Stamp 和 Screen Time Stamp 都不变,这是我们期望的结果,因为我们只是修改了透明表的描述信息,并未修改结构。
![](https://static001.geekbang.org/infoq/6a/6a20264735bb78b27a8f0d85f595485b.png)
再次执行测试报表 ZTESTLOAD, 用 RSDEPEND 检测,发现测试报表的 ABAP Load 时间戳没有发生变化,这说明:即使依赖的透明表的描述信息发生变化,使用了该透明表的 ABAP 报表不需要重新编译,因为透明表描述信息不需要在报表执行期使用。
测试 2
给透明表增加新的一列,再次激活。
![](https://static001.geekbang.org/infoq/21/2121ee073d3f4c9bce2ec22b6bf4117b.png)
此时通过 RSDEPEND 发现,透明表的三个时间戳全部发生了变化,如下图蓝色矩形框所示。然而测试报表 ABAP Load 本身的时间戳仍然未变,这也是合理的,因为我们给透明表里增加了新的列后,还未执行测试报表。
![](https://static001.geekbang.org/infoq/d9/d926567b617010f163a7e093f2342a49.png)
再次执行 ZTESTLOAD 后,这次发现它的 ABAP Load 已经被自动 invalidate 了,时间戳从 07:21:02 变成了 07:36:02。
![](https://static001.geekbang.org/infoq/ea/ea171c5d3d87fb365462e2d7ca4b0792.png)
这也解释了一个现象:有的朋友们观察到,当系统刚升完级后,或者有一批新的传输请求导入到系统后,第一次使用 SAP 应用时,系统响应速度很慢。原因其实通过前文的两个测试已经说明了:系统在花费时间去做相关 ABAP Load invalidation. 在应用依赖的这些 Load invalidation 没有结束之前,系统无法响应用户请求。
为了避免用户在第一次使用应用时长时间等待,可以使用事务码 SGEN 预先进行 Load invalidation.
总结
C/C++ 与 ABAP 有着不同的语法和各自的应用场景,然而 ABAP Kernel 和 ABAP 运行时都是基于 C/C++ 实现,因此使用 ABAP 编写的程序随处可见 C/C++ 的影子,比如 ABAP 里的 Field Symbol 和 Reference,同 C 语言里的指针用法高度相似。本文从 C 语言一个 Hello World 程序的编译过程出发,分享了 ABAP 编程语言一些基于 C/C++ 实现的技术细节。感谢阅读。
版权声明: 本文为 InfoQ 作者【Jerry Wang】的原创文章。
原文链接:【http://xie.infoq.cn/article/195d24aa91e3370ee3bfa7ab4】。文章转载请联系作者。
评论