//ZZ
应用程序二进制接口(ABI-Application Binary Interface)定义了一组在PowerPC系统软件上编译应用程序所需要遵循的一套规则。主要包括基本数据类型,通用寄存器的使用,参数的传递规则,以及堆栈的使用等等。
ABI涵盖了各种细节:如数据类型、大小和对齐;调用约定(控制着函数的参数如何传送以及如何接受返回值);系统调用的编码和一个应用如何向操作系统进行系统调用;以及在一个完整的操作系统ABI中,目标文件的二进制格式、程序库等等。一个完整的ABI,像Intel二进制兼容标准(iBCS)[1] ,允许支持它的操作系统上的程序不经修改在其他支持此ABI的操作体统上运行。
其他的 ABI 标准化细节包括 C++ 名称修饰[2] ,和同一个平台上的编译器之间的调用约定[3],但是不包括跨平台的兼容性。
ABI不同于应用程序接口(API),API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译,然而ABI允许编译好的目标代码在使用兼容ABI的系统中无需改动就能运行。 在Unix风格的操作系统中,存在很多运行在同一硬件平台上互相相关但是不兼容的操作系统(尤其是Intel 80386兼容系统)。有一些努力尝试标准化ABI,以减少销售商将程序移植到其他系统时所需的工作。然而,直到现在还没有很成功的例子,虽然Linux标准化工作组正在为Linux做这方面的努力。
API,顾名思义,是编程的接口,换句话说也就是你编写“应用程序”时候调用的函数之类的东西。对于内核来说,它的“应用程序”有两种:一种是在它之上的,用户空间的真正的应用程序,内核给它们提供的是系统调用这种接口,比如 read(2),write(2);另一种就是内核模块了,它们和内核处于同一层,内核给它们提供的是导出的内核函数,比如 kmalloc(),printk()。这些接口都是你可以在编写程序的时候直接看到的,可以直接拿来用的。
而 ABI 是另一种形式的接口,二进制接口。除非你直接使用汇编语言,这种接口一般是不能直接拿来用的。比如,内核系统调用用哪些寄存器或者干脆用堆栈来传递参数,返回值又是通过哪个寄存器传递回去,内核里面定义的某个结构体的某个字段偏移是多少等等,这些都是二进制层面上的接口。这些接口是直接给编译好的二进制用的。换句话说,如果 ABI 保持稳定的话,你在之前版本上编译好的二进制应用程序、内核模块,完全可以无须重新编译直接在新版本上运行。另一种比较特殊的 ABI 是像 /proc,/sys 目录下面导出的文件,它们虽然不是直接的二进制形式,但也会影响编译出来的二进制,如果它里面使用到它们的话,因此这些“接口”也是一种 ABI。
你平时看到的什么 POSIX 标准啊,C99 标准啊,都是对 API 的规定。而规定 ABI 的标准就不多,而且也没那么强势,Linux 上面的 ABI 标准似乎只有 Linux Foundation 提供的一些标准。
好了,从上面我们可以看出,其实保持一个稳定的 ABI 要比保持稳定的 API 要难得多。比如,在内核中 int register_netdevice(struct net_device *dev) 这个内核函数原型基本上是不会变的,所以保持这个 API 稳定是很简单的,但它的 ABI 就未必了,就算是这个函数定义本身没变,即 API 没变,而 struct net_device 的定义变了,里面多了或者少了某一个字段,它的 ABI 就变了,你之前编译好的二进制模块就很可能会出错了,必须重新编译才行。
你可能会感到意外,上游的 Linux 内核其实不光不保持稳定的 ABI,它就连稳定的 API 都不会保持!而且还牛逼哄哄地写了一个文档,叫 stable_api_nonsense.txt。这么做的道理是,内核一直在向前推进,而且速度很快,内核开发者们并不想因为 API 的限制而阻碍前进的脚步!毕竟我们不想成为下一个 Windows!:-)
所以,你的驱动在不同版本的内核上不经修改直接运行那几乎是不太可能的,就算是你允许重新编译也未必就能不经修改编译成功。即使在同一个大版本的不同发行版上也可能不行。
那你应该怎么办?最好的办法莫过于把你的驱动贡献到社区,汇入内核源代码树中,这样一旦内核的 API 有改动,改动这个 API 的人就有义务替你修改你的驱动的代码,你只需要 review 一下(或者这个也会有人帮你),也省去你不少时间,何乐而不为呢?另一种办法就是基于某个提供稳定 ABI 的内核,比如红帽的 RHEL (认为这是广告的人请使用 CentOS,谢谢!),红帽的企业版内核保证有稳定的 ABI,只要你没有跨大的版本,因为我们的源代码里会检测 ABI 的变化,为此我们实在付出了不少努力。
struct _GstElement { GstObject object; /*< public >*/ /* with LOCK */ GStaticRecMutex *state_lock; /* element state */ GCond *state_cond; guint32 state_cookie; GstState current_state; GstState next_state; GstState pending_state; GstStateChangeReturn last_return; GstBus *bus; /* allocated clock */ GstClock *clock; GstClockTimeDiff base_time; /* NULL/READY: 0 - PAUSED: current time - PLAYING: difference to clock */ /* element pads, these lists can only be iterated while holding * the LOCK or checking the cookie after each LOCK. */ guint16 numpads; GList *pads; guint16 numsrcpads; GList *srcpads; guint16 numsinkpads; GList *sinkpads; guint32 pads_cookie; /*< private >*/ union { struct { /* state set by application */ GstState target_state; /* running time of the last PAUSED state */ GstClockTime start_time; } ABI; /* adding + 0 to mark ABI change to be undone later */ gpointer _gst_reserved[GST_PADDING + 0]; } abidata; };
struct _GstEvent { GstMiniObject mini_object; /*< public >*/ /* with COW */ GstEventType type; guint64 timestamp; GstObject *src; GstStructure *structure; /*< private >*/ union { guint32 seqnum; gpointer _gst_reserved; } abidata; };
struct _GstMessage { GstMiniObject mini_object; /*< private >*//* with MESSAGE_LOCK */ GMutex *lock; /* lock and cond for async delivery */ GCond *cond; /*< public > *//* with COW */ GstMessageType type; guint64 timestamp; GstObject *src; GstStructure *structure; /*< private >*/ union { struct { guint32 seqnum; } ABI; /* + 0 to mark ABI change for future greppage */ gpointer _gst_reserved[GST_PADDING + 0]; } abidata; };
struct _GstClock { GstObject object; GMutex *slave_lock; /* order: SLAVE_LOCK, OBJECT_LOCK */ /*< protected >*/ /* with LOCK */ GstClockTime internal_calibration; GstClockTime external_calibration; GstClockTime rate_numerator; GstClockTime rate_denominator; GstClockTime last_time; GList *entries; GCond *entries_changed; /*< private >*/ /* with LOCK */ GstClockTime resolution; gboolean stats; /* for master/slave clocks */ GstClock *master; /* with SLAVE_LOCK */ gboolean filling; gint window_size; gint window_threshold; gint time_index; GstClockTime timeout; GstClockTime *times; GstClockID clockid; /*< private >*/ union { GstClockPrivate *priv; GstClockTime _gst_reserved[GST_PADDING]; } ABI; };
struct _GstPad { GstObject object; /*< public >*/ gpointer element_private; GstPadTemplate *padtemplate; GstPadDirection direction; /*< public >*/ /* with STREAM_LOCK */ /* streaming rec_lock */ GStaticRecMutex *stream_rec_lock; GstTask *task; /*< public >*/ /* with PREROLL_LOCK */ GMutex *preroll_lock; GCond *preroll_cond; /*< public >*/ /* with LOCK */ /* block cond, mutex is from the object */ GCond *block_cond; GstPadBlockCallback block_callback; gpointer block_data; /* the pad capabilities */ GstCaps *caps; GstPadGetCapsFunction getcapsfunc; GstPadSetCapsFunction setcapsfunc; GstPadAcceptCapsFunction acceptcapsfunc; GstPadFixateCapsFunction fixatecapsfunc; GstPadActivateFunction activatefunc; GstPadActivateModeFunction activatepushfunc; GstPadActivateModeFunction activatepullfunc; /* pad link */ GstPadLinkFunction linkfunc; GstPadUnlinkFunction unlinkfunc; GstPad *peer; gpointer sched_private; /* data transport functions */ GstPadChainFunction chainfunc; GstPadCheckGetRangeFunction checkgetrangefunc; GstPadGetRangeFunction getrangefunc; GstPadEventFunction eventfunc; GstActivateMode mode; /* generic query method */ GstPadQueryTypeFunction querytypefunc; GstPadQueryFunction queryfunc; /* internal links */ #ifndef GST_DISABLE_DEPRECATED GstPadIntLinkFunction intlinkfunc; #else #ifndef __GTK_DOC_IGNORE__ gpointer intlinkfunc; #endif #endif GstPadBufferAllocFunction bufferallocfunc; /* whether to emit signals for have-data. counts number * of handlers attached. */ gint do_buffer_signals; gint do_event_signals; /* ABI added */ /* iterate internal links */ GstPadIterIntLinkFunction iterintlinkfunc; /* free block_data */ GDestroyNotify block_destroy_data; /*< private >*/ union { struct { gboolean block_callback_called; GstPadPrivate *priv; } ABI; gpointer _gst_reserved[GST_PADDING - 2]; } abidata; };
struct _GstTask { GstObject object; /*< public >*/ /* with LOCK */ GstTaskState state; GCond *cond; GStaticRecMutex *lock; GstTaskFunction func; gpointer data; gboolean running; /*< private >*/ union { struct { /* thread this task is currently running in */ GThread *thread; } ABI; gpointer _gst_reserved[GST_PADDING - 1]; } abidata; GstTaskPrivate *priv; };