`
izuoyan
  • 浏览: 9149061 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

java和c的本质--最重要的是启动

阅读更多

java很神秘吗?说什么跨平台,虚拟机之类的。c#很神秘吗?c很神秘吗?操作系统呢?cpu呢?其实这些都不神秘,以前不懂操作系统的时候,看见个多线程就跟看见个神似的,现在呢?linux内核随便看,随便改,不就是进程管理那一堆事嘛,也没有多少代码。学习任何东西的时候,只要静态的框架以及动态的流程搞明白了,都不难的,这就和学英语一样,静态的东西其实就是字母表和单词,动态的东西就是听说读写,动静结合,必有长进。
下面以c和java为例说明其原理,特别说明c和java是如何启动的,想必搞这个之前,每个人的c或者java的功底一定很高了。下面就一步一步地来:
写c程序的时候,一般要写个main:
int main(int argc, char **argv);
写java的时候,一般也要写个main:
public static void main(String argv[]);
在c语言的下面,有加载器,类似的,在java的下面,也有加载器,c的加载器必须脱离c语言编程的机制,但是仍然可以使用c的语法写成,那么启动java的机制也必须脱离java编程的机制(加载器以下可能称为链接器)。
每一个编译好的c程序都会在其elf文件中被加入动态链接器的信息,操作系统启动新的elf文件的时候(和进程没有关系,进程是fork创建的,这里仅仅是加载elf,也就是exec),首先调用链接器的入口,然后连接器中再调用c的main函数(如上),其实仅仅告诉操作系统一个入口点就可以了,随便一个都可以,操作系统会自动调转到那里的,但是一般而言每个操作系统的动态链接程序是固定的,比如在linux上就是ld-linux,这是为了减少数据冗余,和重用组件的思想是一致的,因此一般而言,固定的链接器调用固定的c入口,这个入口就被规定成了main函数,如果你自定义链接器的入口,那么你完全可以调起来入口不是main的c程序。实际上,我们已经在使用这个办法了,那就是加载动态库并且调用动态库的函数,这个意义上,动态库就是一个可以没有main的c程序,ld-linux.so这一类so本质上它们才是真正的程序入口,而我们编写的带有main的c程序可以理解成是ld-linux.so的一个动态库。
java的链接器或者加载器或者称为启动器其实也是一样的道理,只是它比c更上层了,它的启动器不是操作系统直接调用的,而是c语言调用的,可以认为一个带有main函数的c语言写成的程序作为了java的启动器,这个c启动器可以调用别的动态库,在这些本地环境中为java的执行构建了一个虚拟的“执行”环境,这就是java虚拟机,注意,这里“执行”环境很重要,它导致java是跨平台的,操作系统和c库其实也做到了屏蔽下层的作用,然而它们都没有能模拟一个执行环境,仅仅做到了接口兼容而不是二进制兼容,对于操作系统而言,比如linux完全向用户空间暴露了机器指令,因此安装在sparc上的linux和安装在x86上的linux其上的应用程序是不兼容的,c库也是一样的道理。另外就是操作系统本身的系统调用接口的不统一也会导致程序无法即使在相同硬件但是不同操作系统上二进制跨平台。这种局面也许在操作系统和库设计支持,对于跨平台执行没有太大的需求,再者那时的硬件性能普遍很低,增加很多的虚拟层势必会进一步降低性能,第三,那时的人们并没有面对复杂应用的挑战,因此和机器比较亲近,软件工程几乎完全没有被系统研究。
ld-linux.so到底有什么用以及怎么用?它是到ld-2.x.so的软链接,由于几乎每一个正常且正规的程序都使用它,可以说,你把它删除了你的系统就起不来了,除非把磁盘mount到另一个系统上,然后拷贝一个过去,或者自己在别的系统上写一个自定义链接器的程序...(还有一种办法就是安装sash-stand alone shell,它不依赖任何别的库,静态编译的它因此也不依赖ld-linux,因此可以通过内核启动参数init=/sbin/sash来启动到它),然而如果你移动了它导致系统找不到它引起的任何程序无法运行,只要你知道把它搞到了哪里,那就有救,做以下实验:
#mv /lib/ld-2.7.so ./aaa
然后你会发现任何程序都没有办法运行了,此时幸好还有一个shell,只要不关闭它,ld-2.7.so就一直在它的空间里被映射着,这是因为linux是基于引用计数删除被移动的文件的。只要有shell就可以,执行:
#./aaa /bin/cp ./aaa /lib/ld-2.7.so
一切恢复正常。
那么ld-2.7.so到底是什么呢?如果它作为链接器的话,它的链接器在哪里呢?实际上它是一个静态链接的so,并且它是可执行的,从它的man手册上可以看出,它就是用来加载程序以及程序需要的动态库的,然后执行程序,它本身是不依赖其它的so的,它只要OS就能运行,因此任何程序都可以看起来这样运行:
#/lib/ld-2.7.so XX [可执行文件全路径XX的参数]
比如:
#/lib/ld-2.7.so /bin/ls的效果和ls是一样的。
只不过为了方便,linux内置了对elf可执行文件的直接支持,当执行exec的时候,OS自动地直接调用了ld-2.7.so(ld-2.7.so被动态链接进了elf可执行文件,作为其一个so,可以通过ldd看出来),而实际上,更加一般的方法就是通过命令行ld-2.7.so XXX来执行elf可执行程序的(这样ld-2.7.so就不需要链接进elf可执行文件了)。既然c/c++写出的代码可以直接编译成elf可执行文件来直接执行,其它任何的语言写出的代码都应该可以直接执行,在linux中这是通过binfmt_misc来支持的,具体的可以参考内核源码Documents目录中的binfmt_misc.txt文档。现在看一下java的执行:
#java XX(XX为类文件去掉.class)
这里的java就相当于一个链接器,和/lib/ld-2.7.so是类似的,只是它做了更多,包括构建一个虚拟执行环境(建立java虚拟机)等,它启动了java类XX。
现在elf可执行文件的执行方式以及java类的执行方式更加统一了,都是连接器来调用的:ld-2.7.so XX和java XX,它们的本质其实是一样的。那么它们的互操作就不成问题了,由于java程序本身就是一个elf可执行文件,并且它是c写成的,因此java.c就表达了如何在c语言中调用java方法,只不过java.c调用了固定的java方法,那就是main,这和ld-2.7.so最终调用c语言的main函数是一样的,完全是为了规定,没有机制上的原因。既然java类本身是c语言写的程序启动的,因此对于本地代码,它肯定能回调,这就是jni接口,可以在java类中调用本地c语言写成的函数。在理解了机制以后,我们完全可以参考java.c文件写一个不调用main方法的新的java链接器:
代码参考自:www.rgagnon.com首先定义一个类,没有main函数:
class Test {
public native void func(); //定义本地方法
static {
System.loadLibrary("func1");
}
public static void sub(String[] args) {
new Test().func();
}
}
编写一个c文件-startjava.c:
#include <jni.h>
#include <stdio.h>
int main() {
JavaVM *vm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[1];
options[0].optionString = "-Djava.class.path=.";
vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = 1;
jstring jstr;
jobjectArray args;
jint res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
jclass cls = (*env)->FindClass(env, "Test"); //找到Test类,这个也可以通过命令行传递
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "sub", "([Ljava/lang/String;)V"); //调用sub方法,而不是main
jstring argString = (*env)->NewStringUTF(env, ""); //empty arg list
args = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), jstr);
(*env)->CallStaticVoidMethod(env, cls, mid, args);
return 0;
}
然后定一个本地方法的实现-func1.c:
#include <jni.h>
JNIEXPORT void JNICALL Java_TestStunnel_func(JNIEnv *env, jobject obj)
{
....//随意
}
最终startjava这个elf可执行文件启动了一个没有main的java类,然后java类中又调用一个本地方法,startjava.c同样也可以没有main函数--将main改成abc,而是自己写一个链接器来执行,这个链接器负责从OS内核接手用户空间的执行(载入所需动态库-libc/libjvm等的过程早在内核分析elf的时候就做过了,因此不需要这个链接器来做),然后调用其abc函数即可。这样从最初从OS接手过来,每一个不管是elf可执行的本地文件还是java类,没有用到ld-2.7.so和java可执行程序,也没有一个拥有main函数(或者方法),然而“....//随意”真的就执行了。
java是这样,其它的比如perl,python也是这样,包括c#等,都能如此折腾!

分享到:
评论

相关推荐

    java高效学习之路-过来人经验

    无论使用何种Java应用服务器,其实质都是基于某个版本的JDK构建的。因此,熟练掌握JDK是学好Java的基础。 - **JDK版本**:主流的JDK由Sun公司发布,除此之外,还有其他公司或组织提供的JDK,如IBM、BEA以及GNU等。...

    Java所有工具篇 Java所有工具篇 Java所有工具篇

    - **重要性**:作为最权威的 Java 编程指南,覆盖了 Java 的各个方面。 - **学习方法**: - **随时查阅**:编写 Java 代码时应随时参考文档。 - **深入理解**:对于每个命令和参数都应该精通。 - **资源获取**...

    JAVA 开发环境 Java SE Development Kit (JDK) 16.0.1 x64.zip

    根据 TIOBE 编程社区索引,Java 编程语言是软件开发人员的最佳选择之一,他们力争在基于 C 和 C-based 语言中占据上风。从 PC,移动终端或媒体播放器到更复杂的医疗设备,全球大多数电子设备都使用其功能。 Java SE...

    Java学习从入门到精通

    - 这份文档是学习Java最重要的资源之一,几乎涵盖了Java所有方面的内容描述。 - **学习方法**: - 在编写Java代码时,应随时查阅JDK Documentation,以便更好地理解和运用各种类库和API。 #### 三、应用服务器...

    Java学习从入门到精通.docx

    - **重要性**: 是最重要的编程手册,全面覆盖 Java 各个方面。 - **获取方式**: 可以从 JDK 的下载页面获取。 - **使用建议**: 随身携带,编程时随时查阅。 #### 二、应用服务器(App Server) **1. 概念** - **...

    Java学习路径(一)工具篇

    JDK,即Java开发工具包,是Java技术的核心组成部分,包含了Java运行环境(Java Runtime Environment)、一系列Java工具和基本的类库(rt.jar)。无论何种Java应用服务器,其本质都是集成了特定版本的JDK。因此,熟练...

    Java从入门到精通之工具篇.doc

    为了确保系统能够识别JDK,需要在环境变量PATH的最前面添加java的路径C:\j2sdk1.4.2\bin。 ##### 2、JDK 的命令工具 - **java**: 启动JVM并执行class文件。 - **javac**: Java编译器。 - **jar**: Java打包工具。 ...

    JAVA 开发环境 Java SE Development Kit (JDK) 16.0.1 x64 免费下载.zip

    根据 TIOBE 编程社区索引,Java 编程语言是软件开发人员的最佳选择之一,他们力争在基于 C 和 C-based 语言中占据上风。从 PC,移动终端或媒体播放器到更复杂的医疗设备,全球大多数电子设备都使用其功能。 Java SE...

    java 学习步骤.doc

    无论使用哪种Java应用服务器,其实质都是集成了特定版本的JDK。因此,熟练掌握JDK是学习Java的第一步。 - **主要JDK提供商**: - **Sun公司的JDK**:最主流且广泛应用。 - **IBM公司的JDK**:包含的JVM运行效率较...

    java日常处理工具类part2-jar

    一个JAR文件本质上是一个ZIP文件,包含了一个manifest文件(MANIFEST.MF),它存储了关于JAR的基本信息,比如主类(程序启动入口)和版本信息。其他文件和目录则包含类文件、资源文件和配置文件。 创建JAR文件的...

    Java轻松上手

    掌握`java`(启动JVM执行class)、`javac`(Java编译器)、`jar`(Java打包工具)、`javadoc`(Java文档生成器)等命令行工具及其参数至关重要,这些工具的详尽文档可在JDK Documentation中找到。 #### 二、JDK ...

    真正的java学习从入门到精�.doc

    JDK Documentation作为最重要的编程手册之一,覆盖了Java所有方面的内容描述。学习Java的过程中,大多数时间将用于阅读Documentation。强烈建议下载并随身携带Documentation,以便随时查阅。 #### 三、应用服务器...

    新版Android开发教程.rar

    Android 是一个专门针对移动设备的软件集,它包括一个操作系统,中间件和一些重要的应用程序。 Beta 版 的 Android SDK 提供了在 Android 平台上使用 JaVa 语言进行 Android 应用开发必须的工具和 API 接口。 特性 ...

    真正的Java学习从入门到精通

    ### 真正的Java学习从入门到精通 ...最后,对Java应用的多样性和适用领域进行了概述,为读者展示了Java广阔的应用前景和发展空间。通过这些内容的学习,读者可以逐步从Java初学者成长为具备一定技能水平的专业人士。

    JAVA认证

    根据给定文件的信息,我们可以总结出以下几个重要的Java知识点: ### 1. 引用对象变量与对象的关系 在Java中,引用变量实际上是指向对象的一个指针(选项D)。对象存在于堆内存中,而引用变量则存储在栈内存中。...

    java高手成长之路

    JDK(Java Development Kit),即Java开发工具包,是Java程序员的必备工具,它包含了Java运行环境(Java Runtime Environment,JRE)、Java编译器以及其他多种工具和基础类库。掌握JDK是Java学习的第一步,也是最...

    jar文件如何打开或者jar文件关联到java

    JAR文件本质上是ZIP文件的一种扩展,用于打包Java类文件、资源文件和其他元数据。了解如何打开以及如何将JAR文件与Java环境进行关联对于开发者来说至关重要。 #### 一、打开JAR文件的方法 1. **使用命令行** 在...

Global site tag (gtag.js) - Google Analytics