精华内容
参与话题
问答
  • C语言学生成绩管理系统源代码

    万次阅读 多人点赞 2018-03-21 20:27:20
    大学C语言实训课,C语言学生成绩管理系统。 #include<stdio.h> #include<string.h> #include<math.h> struct student { int num; char name[20]; float pingshi;... double ...

    大学C语言实训课,C语言学生成绩管理系统。

    #include<stdio.h>
    #include<string.h>
    #include<math.h>
    struct student
    	{
    		int num;
    		char name[20];
    		float pingshi;
    		float shiyan;
    		float kaoshi;
    		double zongping;
    	}stu[4];
    void main()
    {
    	void a();
    	void b();
    	void c();
    	void d();
    	void e();
    	int n;
    	while(n!=6)
    	{	printf("\t大学计算机基础成绩管理系统\n");
    		printf("1:输入一个班学生大学计算机基础成绩记录\n");
    		printf("2:显示所有成绩记录\n");
    		printf("3:计算并输出平均成绩,均方差\n");
    		printf("4:输出成绩与排名\n");
    		printf("5:结果存入文件chengji.txt\n");
    		printf("6:退出系统\n");
    		printf("输入选项代码:");
    		scanf("%d",&n);
    		switch(n)
    		{
    			case 1:a();break;
    			case 2:b();break;
    			case 3:c();break;
    			case 4:d();break;
    			case 5:e();break;
    			case 6:printf("\n*******************谢谢使用!*******************\n");break;
    			break;    
    		}	
    	}
    getchar();
    }
    /* 子  函  数*/
    void a() /* 输入一个班的学生记录*/
    
    {
    	int i;
    
    	for(i=0;i<4;i++)
    
    	{   
    
    		printf("请输入学号 姓名 平时成绩 实验成绩 考试成绩:");
    
    		scanf("%d%s%f%f%f",&stu[i].num,stu[i].name,&stu[i].pingshi,&stu[i].shiyan,&stu[i].kaoshi);
    		
    	}for(i=0;i<4;i++)
    stu[i].zongping=0.1*stu[i].pingshi+0.3*stu[i].shiyan+0.6*stu[i].kaoshi;
    }
    void b()/* 显示所有记录*/
    
    {
    	int i;
    
    	printf("学号        姓名       平时成绩       实验成绩      考试成绩     总评成绩\n");
    
    	for(i=0;i<4;i++)
    
    		printf("%d%14.2s%14.2f%14.2f%14.2f%14.2f\n",stu[i].num,stu[i].name,stu[i].pingshi,stu[i].shiyan,stu[i].kaoshi,stu[i].zongping);
    
    }
    
    void c()/* 求出全班平均成绩,显示均方差*/
    {  
    	int a[4]={0,1,2,3};
        int i,j;
        double total=0,pfc=0,bzc=0;
        double ave;
        for(i=0;i<4;i++)
    	{
    		total=total+stu[i].zongping;
    	}
        ave=total/4.0;
        printf("总评平均成绩是%f\n",ave);
    	for(i=0;i<4;i++)
    	{
    		pfc=pow((stu[i].zongping-ave),2)/4;
    	}
    	bzc=sqrt(pfc);
    	printf("\n平方差是%f\n",pfc);
    	printf("\n标准差是%f\n",bzc);
    }
    void d()
    
    {
    	int a[4]={0,1,2,3};
    	int i,j,temp;
    	for(j=0;j<3;j++)
    	{
    		for(i=0;i<3-j;i++)
    		if(stu[a[i]].zongping>stu[a[i+1]].zongping)
    		{	
    			temp=a[i];a[i]=a[i+1];
    			a[i+1]=temp;
    
    		}
    	}
    
    	printf("顺序为:\n");
    
    	printf("学号     姓名      总评成绩\n");
    
    	for(i=0;i<4;i++)
    
    		printf("%d%10.2s%15.2f\n",stu[a[i]].num,stu[a[i]].name,stu[a[i]].zongping);
    
    	printf("\n");
    
    }
    
    void e()
    {int i;
    FILE *fp;
    fp=fopen("chengji.txt","w");
    
    	fprintf(fp,"学号        姓名       平时成绩       实验成绩      考试成绩     总评成绩\n");
    
    	for(i=0;i<4;i++)
    
    		fprintf(fp,"%d%14.2s%14.2f%14.2f%14.2f%14.2f\n",stu[i].num,stu[i].name,stu[i].pingshi,stu[i].shiyan,stu[i].kaoshi,stu[i].zongping);
    
    printf("\n\n*******************恭喜,保存完成!*******************\n\n");
    }
    

    简单记录。

     

    关注微信公众号“资料在线”,回复“C语言源码”,即可获得C语言源码包

    更多资源等着你

     

    展开全文
  • 车牌识别系统源代码

    万次阅读 2014-08-20 21:15:05
    车牌识别系统源代码  2011-07-06 13:16 星期三  车牌识别系统源代码  本公司主要从事车牌识别算法开发,在车牌识别这行有一定知名度,有成熟的标清车牌识别源代码出售,并附源代码文档说明及VC++、BCB、VB...

    车牌识别系统源代码

      车牌识别系统源代码
      本公司主要从事车牌识别算法开发,在车牌识别这行有一定知名度,有成熟的标清车牌识别源代码出售,并附源代码文档说明及VC++、BCB、VB、Delphi、C#等开发包。在市场有五年应用成功案例,代码具有极高商业价值。
      源代码不含第三方控件或LIB,核心算法是C语言开发,基于VC6.0封装的DLL,基于Windows\Linux平台。
      也可以移植到DSP平台,也可以我方出技术,投资方出资60W以上。车牌识别系统源代码需要有开发能力,对车牌识别很熟悉,有经济实力的公司。产品用在高速公路卡口、电子警察、收费站、智能停车场等众多需要车牌识别的领域。
      开发环境:VC60软件功能如下:
      1)全天候识别 (晴天、雨天、白天、夜晚...)
      2)自动识别无严重污染的汽车牌照中的汉字、字母和数字部分;
      3)自动识别汽车牌照的底色 (黄、黑、蓝); 
      4)具有识别训练功能,可适应不用的应用环境,提高识别效果。
      5)识别速度:< 200 ms;
      6)识别率:整牌照识别率大于 99% 
      7) 如不需要源码,可提供成熟的车牌识别SDK开发包,开发端口,支持多种语言,便于应用软件集成。
      技术指标:
      识别时间<0.05秒。
      监视距离1-50米(具体视成像设备性能) 。
      可见光成像模式、支持LED和闪光灯等辅助光源,全天候7×24小时连续工作。
      车牌检测正确率>95%。
      汉字识别正确率>99%、字符位识别率大于>99%、全车牌识别正确率>99%。
      支持各种车牌识别:民用车牌(92式)、民用货车尾牌(双行)、民用车牌(2002个性化)、警车车牌、武警车牌、军用车牌(2004式)。
      在可见光成像条件下车牌颜色(蓝、黄、黑、白)识别正确率>99%。
      能够识别行驶速度可达200km/h高速行驶的车辆的车牌 。
      支持视频自动触发及物理触发车辆检测 。
      支持PCI、USB2.0、IEEE1394等接口的视频采集设备。
      输出车辆照片、车牌照片、识别结果、车牌颜色、二值化图像
      本车牌识别软件可以嵌入停车场管理系统,治安卡口,超速抓拍,电子警察,公路收费管理系统,地磅称重车牌识别系统,码头集装箱号码识别系统。如有需要,请电话或QQ联系洽谈。
      
      13714630172 秦先生 Q Q:444826945(注明:车牌识别)
    展开全文
  • curl源代码分析(包含源代码)

    千次下载 热门讨论 2012-01-08 14:43:01
    curl源代码分析 包含源代码 http协议 ftp协议 等等 批量下载 跨平台
  • 贪吃蛇源代码分析

    千次阅读 多人点赞 2014-01-26 13:03:24
    今天已经是农历12月16号了,用这一篇博文给今年的博客画上一个句号吧。...首先,下载一个贪吃蛇的源代码,结构如下:(网上资源很大,我这里就不给出了) 打开AndroidManifest.xml找到应用入口 package="com.xmob

    今天已经是农历12月16号了,用这一篇博文给今年的博客画上一个句号吧。

    首先,下载一个贪吃蛇的源代码,结构如下:(网上资源很多,我这里就不给出了)


    打开AndroidManifest.xml找到应用入口

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.xmobileapp.Snake" android:versionName="1.0" android:versionCode="1">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".Snake" android:label="贪吃蛇"
            android:screenOrientation="portrait">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
        <uses-sdk android:minSdkVersion="3" />
    </manifest> 
    可以看到入口Activity是Snake,好吧我们先进到Snake.java文件中看看onCreate方法

    在onCreate方法中首先是如下两句代码:

    //设置为无标题的主题样式
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    		
    setContentView(R.layout.snake_layout);
    我们看看贪吃蛇的布局文件吧,继续打开snake_layout.xml

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    	android:layout_width="fill_parent"
    	android:layout_height="fill_parent">
    	
    	<com.xmobileapp.Snake.SnakeView
    	 android:id="@+id/snake"
    		android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    tileSize="12" android:background="@color/teneight"/>
    	
    	<RelativeLayout
    		android:layout_width="fill_parent"
    		android:layout_height="fill_parent" >
    		
    		<TextView
    		 android:id="@+id/text"
    			android:visibility="visible"
    			android:layout_width="wrap_content"
    			android:layout_height="wrap_content"
    			android:layout_centerInParent="true"
    			android:gravity="center_horizontal"
    			android:textColor="#FF0033"
    			android:textSize="24sp"/>
    			
    	     <Button android:id="@+id/play"
    	        android:layout_width="wrap_content" 
    	        android:layout_height="wrap_content"
    	        android:layout_centerInParent="true"
    		    android:layout_marginTop="10px"
    		    android:gravity="center_horizontal"
    	        android:text="点击这里开始哦"/>
    	</RelativeLayout>
    	<AbsoluteLayout
    	    android:layout_width="fill_parent"
    		android:layout_height="fill_parent"	
    	>
    	
    	
    	<ImageButton	android:id="@+id/left"
    			        android:layout_width="100px" 
    			        android:layout_height="100px"
    		            
    		            style="?android:attr/buttonStyleSmall" 
    		    		android:src="@drawable/left" 
    		    		android:layout_x="75px" 
    		    		android:layout_y="100px"/>
       <ImageButton	    android:id="@+id/right"
    			        android:layout_width="100px" 
    			        android:layout_height="100px"
    		            
    		            style="?android:attr/buttonStyleSmall" 
    		    		android:src="@drawable/right" 
    		    		android:layout_x="175px" 
    		    		android:layout_y="100px"/>
    	
    	<ImageButton	android:id="@+id/up"
    			        android:layout_width="100px" 
    			        android:layout_height="100px"
    		            
    		            style="?android:attr/buttonStyleSmall" 
    		    		android:src="@drawable/up" 
    		    		android:layout_x="125px" 
    		    		android:layout_y="50px"/>
    		    		
       <ImageButton	    android:id="@+id/down"
    			        android:layout_width="100px" 
    			        android:layout_height="100px"
    		            
    		            style="?android:attr/buttonStyleSmall" 
    		    		android:src="@drawable/down" 
    		    		android:layout_x="125px" 
    		    		android:layout_y="150px"/>	    		
    	</AbsoluteLayout>
    </FrameLayout>

    从布局源码和上图可以看出最外面是一个帧布局(层布局)FrameLayout

    接下来上面是一个叫做SnakeView的视图

    再上面是一个相对布局,该布局中放置了开始按钮和说明文字

    最上面是一个绝对布局,该不居中放置了四个ImageButton

    这个界面布局很容易理解,唯独里面的com.xmobileapp.Snake.SnakeView是一个自定义View.好吧下面我们来看看这个自定义的视图。

    刚进去就发现它继承自TileView,这个貌似没有见过,原来也是一个自定义的View啊,我们先看看这个TileView是个什么吧,回头再看SnakeView类


    	@Override
    	public void onDraw(Canvas canvas) {
    		super.onDraw(canvas);
    		for (int x = 0; x < mXTileCount; x += 1) {
    			for (int y = 0; y < mYTileCount; y += 1) {
    				if (mTileGrid[x][y] > 0) {
    					canvas.drawBitmap(mTileArray[mTileGrid[x][y]], mXOffset + x
    							* mTileSize, mYOffset + y * mTileSize, mPaint);
    				}
    			}
    		}
    
    	}
    

    可以看到这个TileView继承自View并重写了onDraw方法,猜想应该是画贪吃蛇中的每个方块的。这个疑问暂且留在这里,我们再回到Snake.java中的onCreate方法中。

    mSnakeView = (SnakeView) findViewById(R.id.snake);
    mSnakeView.setTextView((TextView) findViewById(R.id.text));
    将界面中的TextView对象注入到了SnakeView类中。

    		if (savedInstanceState == null) {
    			mSnakeView.setMode(mSnakeView.READY);
    		} else {
    			Bundle map = savedInstanceState.getBundle(ICICLE_KEY);
    			if (map != null) {
    				mSnakeView.restoreState(map);
    			} else {
    				mSnakeView.setMode(SnakeView.PAUSE);
    			}
    		}
    如果刚启动的时候saveInstanceState == null 则设置状态为准备就绪,否则执行下面的方法。

    先查看Bundle对象的getBundle方法http://developer.android.com/reference/android/os/Bundle.html


    大概意思是通过一个键返回一个值,其中的ICICLE_KEY是一个常量key

    	private static String ICICLE_KEY = "snake-view";
    下面我们再来看看restoreState方法

    	public void restoreState(Bundle icicle) {
    		setMode(PAUSE);
    
    		mAppleList = coordArrayToArrayList(icicle.getIntArray("mAppleList"));
    		mDirection = icicle.getInt("mDirection");
    		mNextDirection = icicle.getInt("mNextDirection");
    		mMoveDelay = icicle.getLong("mMoveDelay");
    		mScore = icicle.getLong("mScore");
    		mSnakeTrail = coordArrayToArrayList(icicle.getIntArray("mSnakeTrail"));
    	}
    现在也看不懂,但是大概能知道这是保存状态的方法。

    setMode方法我们就不进去看了,估计也看不懂,这个从方法名上我们猜测是一个设置游戏状态的方法。

    下面我们再看看开始游戏的按钮监听函数

    		case PLAY:
    			play.setVisibility(View.GONE);
    			left.setVisibility(View.VISIBLE);
    			right.setVisibility(View.VISIBLE);
    			up.setVisibility(View.VISIBLE);
    			down.setVisibility(View.VISIBLE);
    			if (mSnakeView.mMode == mSnakeView.READY
    					| mSnakeView.mMode == mSnakeView.LOSE) {
    				mSnakeView.initNewGame();
    				mSnakeView.setMode(mSnakeView.RUNNING);
    				mSnakeView.update();
    				updateStatus = new UpdateStatus();
    				updateStatus.start();
    				break;
    			}
    
    			if (mSnakeView.mMode == mSnakeView.PAUSE) {
    				mSnakeView.setMode(mSnakeView.RUNNING);
    				mSnakeView.update();
    
    				break;
    			}
    
    			if (mSnakeView.mDirection != mSnakeView.SOUTH) {
    				mSnakeView.mNextDirection = mSnakeView.NORTH;
    
    				break;
    			}
    
    			break;

    上面的几句是游戏开始的时候上面的四个方向按钮显示,开始按钮隐藏。

    游戏中有如下几种状态在SnakeView类中定义

    	public static final int PAUSE = 0; //暂停
    	public static final int READY = 1;  //准备开始
    	public static final int RUNNING = 2;  //运行状态
    	public static final int LOSE = 3;  //游戏结束,也就是输了
    如果刚进入游戏,然后开始游戏就执行如下几句代码

    mSnakeView.initNewGame();
    mSnakeView.setMode(mSnakeView.RUNNING);
    mSnakeView.update();
    updateStatus = new UpdateStatus();
    updateStatus.start();
    进入initNewGam()方法看看

    	void initNewGame() {
    		mSnakeTrail.clear();
    		mAppleList.clear();
    
    		mSnakeTrail.add(new Coordinate(7, 7));
    		mSnakeTrail.add(new Coordinate(6, 7));
    		mSnakeTrail.add(new Coordinate(5, 7));
    		mSnakeTrail.add(new Coordinate(4, 7));
    		mSnakeTrail.add(new Coordinate(3, 7));
    		mSnakeTrail.add(new Coordinate(2, 7));
    		mNextDirection = NORTH;
    		addRandomApple();
    		addRandomApple();
    
    		mMoveDelay = 600;
    		mScore = 0;
    	}
    其他的参数不用管,我们现在可以明白了,里面的Coordinate其实就是贪吃蛇游戏开始的时候的小方块,在这里创建了6个小方块

    	private class Coordinate {
    		public int x;
    		public int y;
    
    		public Coordinate(int newX, int newY) {
    			x = newX;
    			y = newY;
    		}
    
    		public boolean equals(Coordinate other) {
    			if (x == other.x && y == other.y) {
    				return true;
    			}
    			return false;
    		}
    
    		@Override
    		public String toString() {
    			return "Coordinate: [" + x + "," + y + "]";
    		}
    	}
    再看看update方法

    	public void update() {
    		if (mMode == RUNNING) {
    			long now = System.currentTimeMillis();
    
    			if (now - mLastMove > mMoveDelay) {
    				clearTiles();
    				updateWalls();
    				updateSnake();
    				updateApples();
    				mLastMove = now;
    			}
    			mRedrawHandler.sleep(mMoveDelay);
    		}
    
    	}
    明白了,我们可以调节mMoveDelay控制贪吃蛇移动的速度,详细控制就不看了,改变坐标位置就可以实现了。

    再来看看这两句代码的作用

    updateStatus = new UpdateStatus();
    updateStatus.start();
    点进去一看,原来这是一个线程,用来监听游戏是否结束,如果结束则通过Handle将控制方向按钮隐藏,将开始游戏按钮显示。

    接下来我们看看前后左右四个按钮是怎么控制贪吃蛇的方向的。由于四个方向原理一样,我们就看LEFT吧

    		case LEFT:
    
    			if (mSnakeView.mDirection != mSnakeView.EAST) {
    				mSnakeView.mNextDirection = mSnakeView.WEST;
    			}
    			break;
    上面代码的意思是,假如说现在正在向右移动则我们按向左的按钮无效,如果不是向右移动则按向右按钮改变方向。

    这里它用到了两个变量一个是当前的方向mDirection另一个是下一次刷新时候的方向(也就是改变的方向)mNextDirection.可以在updateSnake方法中看到实际上在下次刷新的时候会将mNextDirection的值赋给mDirection。

    通过上面的分析,对基本的结构和原理有了一个认识,下面我们来具体看一下各个类的作用及实现过程。

    这部分分析发现有人已经做过,就不重复劳动了:http://blog.csdn.net/biaobiaoqi/article/details/6618313

    代码运行如下:



    展开全文
  • Android系统进程Zygote启动过程的源代码分析

    万次阅读 多人点赞 2011-09-19 00:59:08
    在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育(fork)出来的,这也许就是为... 在前面一篇文章Android应用程序进程启动过程的源代码分析中,我们看到了,当ActivityManager

            在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育(fork)出来的,这也许就是为什么要把它称为Zygote(受精卵)的原因吧。由于Zygote进程在Android系统中有着如此重要的地位,本文将详细分析它的启动过程。

    《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

            在前面一篇文章Android应用程序进程启动过程的源代码分析中,我们看到了,当ActivityManagerService启动一个应用程序的时候,就会通过Socket与Zygote进程进行通信,请求它fork一个子进程出来作为这个即将要启动的应用程序的进程;在前面两篇文章Android应用程序安装过程源代码分析Android系统默认Home应用程序(Launcher)的启动过程源代码分析中,我们又看到了,系统中的两个重要服务PackageManagerService和ActivityManagerService,都是由SystemServer进程来负责启动的,而SystemServer进程本身是Zygote进程在启动的过程中fork出来的。

            我们知道,Android系统是基于Linux内核的,而在Linux系统中,所有的进程都是init进程的子孙进程,也就是说,所有的进程都是直接或者间接地由init进程fork出来的。Zygote进程也不例外,它是在系统启动的过程,由init进程创建的。在系统启动脚本system/core/rootdir/init.rc文件中,我们可以看到启动Zygote进程的脚本命令:

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        socket zygote stream 666
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on
        onrestart restart media
        onrestart restart netd
    
            前面的关键字service告诉init进程创建一个名为"zygote"的进程,这个zygote进程要执行的程序是/system/bin/app_process,后面是要传给app_process的参数。

            接下来的socket关键字表示这个zygote进程需要一个名称为"zygote"的socket资源,这样,系统启动后,我们就可以在/dev/socket目录下看到有一个名为zygote的文件。这里定义的socket的类型为unix domain socket,它是用来作本地进程间通信用的,具体可以参考前面一篇文章Android学习启动篇提到的一书《Linux内核源代码情景分析》的第七章--基于socket的进程间通信。前面我们说到的ActivityManagerService就是通这个socket来和zygote进程通信请求fork一个应用程序进程的了。

            最后的一系列onrestart关键字表示这个zygote进程重启时需要执行的命令。

            关于init.rc文件的更多信息,请参考system/core/init/readme.txt文件。

            了解了这个信息之后,我们就知道Zygote进程要执行的程序便是system/bin/app_process了,它的源代码位于frameworks/base/cmds/app_process/app_main.cpp文件中,入口函数是main。在继续分析Zygote进程启动的过程之前,我们先来看看它的启动序列图:


            下面我们就详细分析每一个步骤。

            Step 1. app_process.main

            这个函数定义在frameworks/base/cmds/app_process/app_main.cpp文件中:

    int main(int argc, const char* const argv[])
    {
    	// These are global variables in ProcessState.cpp
    	mArgC = argc;
    	mArgV = argv;
    
    	mArgLen = 0;
    	for (int i=0; i<argc; i++) {
    		mArgLen += strlen(argv[i]) + 1;
    	}
    	mArgLen--;
    
    	AppRuntime runtime;
    	const char *arg;
    	argv0 = argv[0];
    
    	// Process command line arguments
    	// ignore argv[0]
    	argc--;
    	argv++;
    
    	// Everything up to '--' or first non '-' arg goes to the vm
    
    	int i = runtime.addVmArguments(argc, argv);
    
    	// Next arg is parent directory
    	if (i < argc) {
    		runtime.mParentDir = argv[i++];
    	}
    
    	// Next arg is startup classname or "--zygote"
    	if (i < argc) {
    		arg = argv[i++];
    		if (0 == strcmp("--zygote", arg)) {
    			bool startSystemServer = (i < argc) ?
    				strcmp(argv[i], "--start-system-server") == 0 : false;
    			setArgv0(argv0, "zygote");
    			set_process_name("zygote");
    			runtime.start("com.android.internal.os.ZygoteInit",
    				startSystemServer);
    		} else {
    			set_process_name(argv0);
    
    			runtime.mClassName = arg;
    
    			// Remainder of args get passed to startup class main()
    			runtime.mArgC = argc-i;
    			runtime.mArgV = argv+i;
    
    			LOGV("App process is starting with pid=%d, class=%s.\n",
    				getpid(), runtime.getClassName());
    			runtime.start();
    		}
    	} else {
    		LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    		fprintf(stderr, "Error: no class name or --zygote supplied.\n");
    		app_usage();
    		return 10;
    	}
    
    }
            这个函数的主要作用就是创建一个AppRuntime变量,然后调用它的start成员函数。AppRuntime这个类我们在Android应用程序进程启动过程的源代码分析一文中已经有过介绍了,它同样是在frameworks/base/cmds/app_process/app_main.cpp文件中定义:

    class AppRuntime : public AndroidRuntime
    {
    	......
    };
            它约继承于AndroidRuntime类, AndroidRuntime类定义在frameworks/base/core/jni/AndroidRuntime.cpp文件中:

    ......
    
    static AndroidRuntime* gCurRuntime = NULL;
    
    ......
    
    AndroidRuntime::AndroidRuntime()
    {
    	......
    
    	assert(gCurRuntime == NULL);        // one per process
    	gCurRuntime = this;
    }
            当AppRuntime对象创建时,会调用其父类AndroidRuntime的构造函数,而在AndroidRuntime类的构造函数里面,会将this指针保存在静态全局变量gCurRuntime中,这样,当其它地方需要使用这个AppRuntime对象时,就可以通过同一个文件中的这个函数来获取这个对象的指针:

    AndroidRuntime* AndroidRuntime::getRuntime()
    {
        return gCurRuntime;
    }
    
            回到上面的main函数中,由于我们在init.rc文件中,设置了app_process启动参数--zygote和--start-system-server,因此,在main函数里面,最终会执行下面语句:

        runtime.start("com.android.internal.os.ZygoteInit",
    	startSystemServer);
            这里的参数startSystemServer为true,表示要启动SystemServer组件。由于AppRuntime没有实现自己的start函数,它继承了父类AndroidRuntime的start函数,因此,下面会执行AndroidRuntime类的start函数。

            Step 2. AndroidRuntime.start

            这个函数定义在frameworks/base/core/jni/AndroidRuntime.cpp文件中:

    /*
    * Start the Android runtime.  This involves starting the virtual machine
    * and calling the "static void main(String[] args)" method in the class
    * named by "className".
    */
    void AndroidRuntime::start(const char* className, const bool startSystemServer)
    {
    	......
    
    	char* slashClassName = NULL;
    	char* cp;
    	JNIEnv* env;
    
    	......
    
    	/* start the virtual machine */
    	if (startVm(&mJavaVM, &env) != 0)
    		goto bail;
    
    	/*
    	* Register android functions.
    	*/
    	if (startReg(env) < 0) {
    		LOGE("Unable to register all android natives\n");
    		goto bail;
    	}
    
    	/*
    	* We want to call main() with a String array with arguments in it.
    	* At present we only have one argument, the class name.  Create an
    	* array to hold it.
    	*/
    	jclass stringClass;
    	jobjectArray strArray;
    	jstring classNameStr;
    	jstring startSystemServerStr;
    	stringClass = env->FindClass("java/lang/String");
    	assert(stringClass != NULL);
    	strArray = env->NewObjectArray(2, stringClass, NULL);
    	assert(strArray != NULL);
    	classNameStr = env->NewStringUTF(className);
    	assert(classNameStr != NULL);
    	env->SetObjectArrayElement(strArray, 0, classNameStr);
    	startSystemServerStr = env->NewStringUTF(startSystemServer ?
    		"true" : "false");
    	env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
    
    	/*
    	* Start VM.  This thread becomes the main thread of the VM, and will
    	* not return until the VM exits.
    	*/
    	jclass startClass;
    	jmethodID startMeth;
    
    	slashClassName = strdup(className);
    	for (cp = slashClassName; *cp != '\0'; cp++)
    		if (*cp == '.')
    			*cp = '/';
    
    	startClass = env->FindClass(slashClassName);
    	if (startClass == NULL) {
    		......
    	} else {
    		startMeth = env->GetStaticMethodID(startClass, "main",
    			"([Ljava/lang/String;)V");
    		if (startMeth == NULL) {
    			......
    		} else {
    			env->CallStaticVoidMethod(startClass, startMeth, strArray);
    			......
    		}
    	}
    
    	......
    }

            这个函数的作用是启动Android系统运行时库,它主要做了三件事情,一是调用函数startVM启动虚拟机,二是调用函数startReg注册JNI方法,三是调用了com.android.internal.os.ZygoteInit类的main函数。

            Step 3. ZygoteInit.main

            这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

    public class ZygoteInit {
    	......
    
    	public static void main(String argv[]) {
    		try {
    			......
    
    			registerZygoteSocket();
    			
    			......
    
    			......
    
    			if (argv[1].equals("true")) {
    				startSystemServer();
    			} else if (!argv[1].equals("false")) {
    				......
    			}
    
    			......
    
    			if (ZYGOTE_FORK_MODE) {
    				......
    			} else {
    				runSelectLoopMode();
    			}
    
    			......
    		} catch (MethodAndArgsCaller caller) {
    			......
    		} catch (RuntimeException ex) {
    			......
    		}
    	}
    
    	......
    }
             它主要作了三件事情,一个调用registerZygoteSocket函数创建了一个socket接口,用来和ActivityManagerService通讯,二是调用startSystemServer函数来启动SystemServer组件,三是调用runSelectLoopMode函数进入一个无限循环在前面创建的socket接口上等待ActivityManagerService请求创建新的应用程序进程。

             Step 4. ZygoteInit.registerZygoteSocket

             这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

    public class ZygoteInit {
    	......
    
    	/**
    	* Registers a server socket for zygote command connections
    	*
    	* @throws RuntimeException when open fails
    	*/
    	private static void registerZygoteSocket() {
    		if (sServerSocket == null) {
    			int fileDesc;
    			try {
    				String env = System.getenv(ANDROID_SOCKET_ENV);
    				fileDesc = Integer.parseInt(env);
    			} catch (RuntimeException ex) {
    				......
    			}
    
    			try {
    				sServerSocket = new LocalServerSocket(
    					createFileDescriptor(fileDesc));
    			} catch (IOException ex) {
    				.......
    			}
    		}
    	}
    		
    	......
    }
             这个socket接口是通过文件描述符来创建的,这个文件描符代表的就是我们前面说的/dev/socket/zygote文件了。这个文件描述符是通过环境变量ANDROID_SOCKET_ENV得到的,它定义为:

    public class ZygoteInit {
    	......
    
    	private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
    		
    	......
    }
            那么,这个环境变量的值又是由谁来设置的呢?我们知道,系统启动脚本文件system/core/rootdir/init.rc是由init进程来解释执行的,而init进程的源代码位于system/core/init目录中,在init.c文件中,是由service_start函数来解释init.rc文件中的service命令的:

    void service_start(struct service *svc, const char *dynamic_args)
    {
    	......
    
    	pid_t pid;
    
    	......
    
    	pid = fork();
    
    	if (pid == 0) {
    		struct socketinfo *si;
    
    		......
    
    		for (si = svc->sockets; si; si = si->next) {
    			int socket_type = (
    				!strcmp(si->type, "stream") ? SOCK_STREAM :
    				(!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
    			int s = create_socket(si->name, socket_type,
    				si->perm, si->uid, si->gid);
    			if (s >= 0) {
    				publish_socket(si->name, s);
    			}
    		}
    
    		......
    	}
    
    	......
    }
            每一个service命令都会促使init进程调用fork函数来创建一个新的进程,在新的进程里面,会分析里面的socket选项,对于每一个socket选项,都会通过create_socket函数来在/dev/socket目录下创建一个文件,在这个场景中,这个文件便是zygote了,然后得到的文件描述符通过publish_socket函数写入到环境变量中去:

    static void publish_socket(const char *name, int fd)
    {
        char key[64] = ANDROID_SOCKET_ENV_PREFIX;
        char val[64];
    
        strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
                name,
                sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
        snprintf(val, sizeof(val), "%d", fd);
        add_environment(key, val);
    
        /* make sure we don't close-on-exec */
        fcntl(fd, F_SETFD, 0);
    }
    
           这里传进来的参数name值为"zygote",而ANDROID_SOCKET_ENV_PREFIX在system/core/include/cutils/sockets.h定义为:

    #define ANDROID_SOCKET_ENV_PREFIX	"ANDROID_SOCKET_"
    
            因此,这里就把上面得到的文件描述符写入到以"ANDROID_SOCKET_zygote"为key值的环境变量中。又因为上面的ZygoteInit.registerZygoteSocket函数与这里创建socket文件的create_socket函数是运行在同一个进程中,因此,上面的ZygoteInit.registerZygoteSocket函数可以直接使用这个文件描述符来创建一个Java层的LocalServerSocket对象。如果其它进程也需要打开这个/dev/socket/zygote文件来和Zygote进程进行通信,那就必须要通过文件名来连接这个LocalServerSocket了,参考Android应用程序进程启动过程的源代码分析一文中的Step 4,ActivityManagerService是通过Process.start函数来创建一个新的进程的,而Process.start函数会首先通过Socket连接到Zygote进程中,最终由Zygote进程来完成创建新的应用程序进程,而Process类是通过openZygoteSocketIfNeeded函数来连接到Zygote进程中的Socket的:

    public class Process {  
    	......  
     
    	private static void openZygoteSocketIfNeeded()  
    			throws ZygoteStartFailedEx {  
    
    		......
    
    		for (int retry = 0  
    			; (sZygoteSocket == null) && (retry < (retryCount + 1))  
    			; retry++ ) {  
    
    				......
    
    				try {  
    					sZygoteSocket = new LocalSocket();  
    					sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET,  
    						LocalSocketAddress.Namespace.RESERVED));  
    
    					sZygoteInputStream  
    						= new DataInputStream(sZygoteSocket.getInputStream());  
    
    					sZygoteWriter =  
    						new BufferedWriter(  
    						new OutputStreamWriter(  
    						sZygoteSocket.getOutputStream()),  
    						256);  
    
    					......  
    				} catch (IOException ex) {  
    					......  
    				}  
    		}  
    
    		......  
    	}  
    
    	......  
    }
            这里的ZYGOTE_SOCKET定义为:

    public class Process {  
    	......  
     
    	private static final String ZYGOTE_SOCKET = "zygote";  
    
    	......  
    } 
            它刚好就是对应/dev/socket目录下的zygote文件了。

            Android系统中的socket机制和binder机制一样,都是可以用来进行进程间通信,读者可以自己对比一下这两者的不同之处,Binder进程间通信机制可以参考Android进程间通信(IPC)机制Binder简要介绍和学习计划一文。

           Socket对象创建完成之后,回到Step 3中的ZygoteInit.main函数中,startSystemServer函数来启动SystemServer组件。

           Step 5. ZygoteInit.startSystemServer
           这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

    public class ZygoteInit {
    	......
    
    	private static boolean startSystemServer()
    			throws MethodAndArgsCaller, RuntimeException {
    		/* Hardcoded command line to start the system server */
    		String args[] = {
    			"--setuid=1000",
    			"--setgid=1000",
    			"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003",
    			"--capabilities=130104352,130104352",
    			"--runtime-init",
    			"--nice-name=system_server",
    			"com.android.server.SystemServer",
    		};
    		ZygoteConnection.Arguments parsedArgs = null;
    
    		int pid;
    
    		try {
    			parsedArgs = new ZygoteConnection.Arguments(args);
    
    			......
    
    			/* Request to fork the system server process */
    			pid = Zygote.forkSystemServer(
    				parsedArgs.uid, parsedArgs.gid,
    				parsedArgs.gids, debugFlags, null,
    				parsedArgs.permittedCapabilities,
    				parsedArgs.effectiveCapabilities);
    		} catch (IllegalArgumentException ex) {
    			......
    		}
    
    		/* For child process */
    		if (pid == 0) {
    			handleSystemServerProcess(parsedArgs);
    		}
    
    		return true;
    	}
    	
    	......
    }
            这里我们可以看到,Zygote进程通过Zygote.forkSystemServer函数来创建一个新的进程来启动SystemServer组件,返回值pid等0的地方就是新的进程要执行的路径,即新创建的进程会执行handleSystemServerProcess函数。

            Step 6. ZygoteInit.handleSystemServerProcess
            这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

    public class ZygoteInit {
    	......
    
    	private static void handleSystemServerProcess(
    			ZygoteConnection.Arguments parsedArgs)
    			throws ZygoteInit.MethodAndArgsCaller {
    		closeServerSocket();
    
    		/*
    		* Pass the remaining arguments to SystemServer.
    		* "--nice-name=system_server com.android.server.SystemServer"
    		*/
    		RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
    		/* should never reach here */
    	}
    
    	......
    } 
            由于由Zygote进程创建的子进程会继承Zygote进程在前面Step 4中创建的Socket文件描述符,而这里的子进程又不会用到它,因此,这里就调用closeServerSocket函数来关闭它。这个函数接着调用RuntimeInit.zygoteInit函数来进一步执行启动SystemServer组件的操作。

            Step 7. RuntimeInit.zygoteInit

            这个函数定义在frameworks/base/core/java/com/android/internal/os/RuntimeInit.java文件中:

    public class RuntimeInit {  
    	......  
    
    	public static final void zygoteInit(String[] argv)  
    			throws ZygoteInit.MethodAndArgsCaller {  
    		......  
      
    		zygoteInitNative();  
    
    		......  
    
    
    		// Remaining arguments are passed to the start class's static main  
    
    		String startClass = argv[curArg++];  
    		String[] startArgs = new String[argv.length - curArg];  
    
    		System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);  
    		invokeStaticMain(startClass, startArgs);  
    	}  
    
    	......  
    }
             这个函数会执行两个操作,一个是调用zygoteInitNative函数来执行一个Binder进程间通信机制的初始化工作,这个工作完成之后,这个进程中的Binder对象就可以方便地进行进程间通信了,另一个是调用上面Step 5传进来的com.android.server.SystemServer类的main函数。

             Step 8. RuntimeInit.zygoteInitNative

             这个函数定义在frameworks/base/core/java/com/android/internal/os/RuntimeInit.java文件中:

    public class RuntimeInit {  
    	......  
    
    	public static final native void zygoteInitNative();  
    
    	......  
    }
            这里可以看出,函数zygoteInitNative是一个Native函数,实现在frameworks/base/core/jni/AndroidRuntime.cpp文件中,这里我们就不再细看了,具体可以参考Android应用程序进程启动过程的源代码分析一文的Step 9,完成这一步后,这个进程的Binder进程间通信机制基础设施就准备好了。

            回到Step 7中的RuntimeInit.zygoteInitNative函数,下一步它就要执行com.android.server.SystemServer类的main函数了。

            Step 9. SystemServer.main

            这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中:

    public class SystemServer  
    {  
    	......  
    
    	native public static void init1(String[] args);  
    
    	......  
    
    	public static void main(String[] args) {  
    		......  
    
    		init1(args);  
    
    		......  
    	} 
    
    	public static final void init2() {  
    		Slog.i(TAG, "Entered the Android system server!");  
    		Thread thr = new ServerThread();  
    		thr.setName("android.server.ServerThread");  
    		thr.start();  
    	}  
    
    	......  
    }
            这里的main函数首先会执行JNI方法init1,然后init1会调用这里的init2函数,在init2函数里面,会创建一个ServerThread线程对象来执行一些系统关键服务的启动操作,例如我们在前面两篇文章Android应用程序安装过程源代码分析Android系统默认Home应用程序(Launcher)的启动过程源代码分析中提到的PackageManagerService和ActivityManagerService。
            这一步的具体执行过程可以参考Android应用程序安装过程源代码分析一文,这里就不再详述了。

            这里执行完成后,层层返回,最后回到上面的Step 3中的ZygoteInit.main函数中,接下来它就要调用runSelectLoopMode函数进入一个无限循环在前面Step 4中创建的socket接口上等待ActivityManagerService请求创建新的应用程序进程了。

            Step 10. ZygoteInit.runSelectLoopMode

            这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

    public class ZygoteInit {
    	......
    
    	private static void runSelectLoopMode() throws MethodAndArgsCaller {
    		ArrayList<FileDescriptor> fds = new ArrayList();
    		ArrayList<ZygoteConnection> peers = new ArrayList();
    		FileDescriptor[] fdArray = new FileDescriptor[4];
    
    		fds.add(sServerSocket.getFileDescriptor());
    		peers.add(null);
    
    		int loopCount = GC_LOOP_COUNT;
    		while (true) {
    			int index;
    
    			......
    
    
    			try {
    				fdArray = fds.toArray(fdArray);
    				index = selectReadable(fdArray);
    			} catch (IOException ex) {
    				throw new RuntimeException("Error in select()", ex);
    			}
    
    			if (index < 0) {
    				throw new RuntimeException("Error in select()");
    			} else if (index == 0) {
    				ZygoteConnection newPeer = acceptCommandPeer();
    				peers.add(newPeer);
    				fds.add(newPeer.getFileDesciptor());
    			} else {
    				boolean done;
    				done = peers.get(index).runOnce();
    
    				if (done) {
    					peers.remove(index);
    					fds.remove(index);
    				}
    			}
    		}
    	}
    
    	......
    }      
            这个函数我们已经在Android应用程序进程启动过程的源代码分析一文的Step 5中分析过了,这就是在等待ActivityManagerService来连接这个Socket,然后调用ZygoteConnection.runOnce函数来创建新的应用程序,有兴趣的读者可以参考Android应用程序进程启动过程的源代码分析这篇文章,这里就不再详述了。

            这样,Zygote进程就启动完成了,学习到这里,我们终于都对Android系统中的进程有了一个深刻的认识了,这里总结一下:

            1. 系统启动时init进程会创建Zygote进程,Zygote进程负责后续Android应用程序框架层的其它进程的创建和启动工作。

            2. Zygote进程会首先创建一个SystemServer进程,SystemServer进程负责启动系统的关键服务,如包管理服务PackageManagerService和应用程序组件管理服务ActivityManagerService。

            3. 当我们需要启动一个Android应用程序时,ActivityManagerService会通过Socket进程间通信机制,通知Zygote进程为这个应用程序创建一个新的进程。

    老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

    展开全文
  • wireshark源代码分析

    万次阅读 热门讨论 2012-01-17 18:08:38
    经过多次尝试,终于在windows上成功编译wireshark源代码,但用的不是下面的这个步骤,不过大同小异,我的是vs2005,所以用的:http://blog.csdn.net/alexander_vc/article/details/6198836 的方法。 1.2.7版的...
  • 已经出版的相同选题的书:LEMON语法分析生成器(LALR(1))源代码情景分析       Bison是bash的语法分析器是gcc的语法分析器。 Linux其实是有三部分组成。 第一部分,内核(oldlinux论坛已有讨论和linux内核...
  • quake3源代码分析

    万次阅读 2011-11-09 21:00:03
    quake3源码分析(1) quake3 游戏demo演示版,没有玩过quake3游戏的或想了解quake3画面的 , 可到此处下载, http://ishare.iask.sina.com.cn/search.php?key=q3ademo&from=index&format= 这两个任意都可直接下载...
  • 如何分析源代码

    千次阅读 2017-05-11 14:40:27
    如何看懂源代码--(分析源代码方法) 如何去分析开源的代码,例如tomcat? 如何阅读程序源代码? 如何更有效地学习开源项目的代码? [知乎回答]如何提高阅读源代码的能力? 主要分六个部分 ( 1 )读...
  • Neo4j源代码分析

    千次阅读 2018-03-11 21:15:08
    1.下载neo4j源码 https://github.com/neo4j/neo4j/ 参考文章
  • FFMpeg 源代码分析

    千次阅读 2017-08-25 13:12:34
    FFMpeg 源代码分析引言最近在公司带领团队在做一个视频播放器的项目。由于整个团队之前并没有相关的技术积累,同时公司也没有相关的产品积累。为了能让项目快速的上线,决定使用FFMpeg作为底层的多媒体引擎。一方面...
  • TCPMP源代码分析

    千次阅读 2009-05-23 17:03:00
    网上深入分析TCPMP源代码的文章很少。《TCPMP播放器到WINDOWSCE平台的移植编译过程》一文对在EVC下编译TCPMP的介绍甚是详尽,非常适合入手。《WINCE TCPMP应用》与《水煮TCPMP》两篇文章简要介绍了下TCPMP的结构和各...
  • postgresql源代码分析

    千次阅读 2010-03-22 11:16:00
    从Linux下拷回通过编译的源代码后,在硬盘上展开,例如我展开后将所有的源代码放到D:/Postgresql/source目录下。然后建立一个目录D:/Postgresql/insight,打开sourceinsight后在这个目录下创建一个project,将D:/...
  • ptrace源代码分析

    千次阅读 2011-01-04 14:47:00
    ptrace作为应用程序调试的基石,要想对其有深入的了解,最好的方法是分析它的源代码。选取linux2.6.8,更高版本的内容基本相同。实现ptrace系统调用功能的主要是sys_ptrace函数,当然还包括一些读写寄存器的辅助函数...
  • Tomcat源代码分析

    千次阅读 2005-08-14 19:21:00
    文章 前言 请求的接收和处理 classLoader in tomcat conf/web.xml digester in tomcat lifecycleListener in tomcat naming in tomcat
  • Grub源代码分析

    千次阅读 2006-05-09 17:26:00
    GRUB简体中文化版使用方法http://sysoft.zdwx.com/grub/Grub 源代码分析http://www.linuxsir.org/bbs/showthread.php?t=170348
  • NSGA-2源代码分析(1)源代码结构

    千次阅读 2008-04-17 14:05:00
    NSGA2源代码可以由http://www.iitk.ac.in/kangal/codes.shtml上下载,下面将该算法源代码的结构组织。该结构由17个头文件和1个源文件组成,对每个头文件的分析如下:1。input.h2。init.h3。realinit.h4。decode.h5。...
  • 【学习方法】如何分析源代码

    千次阅读 2016-01-27 16:49:00
    如何看懂源代码--(分析源代码方法) 如何去分析开源的代码,例如tomcat? 如何阅读程序源代码? 如何更有效地学习开源项目的代码? [知乎回答]如何提高阅读源代码的能力? ...
  • 本文将继续分析在应用程序内部启动非默认Activity的过程的源代码,这种非默认Activity一般是在原来的进程和任务中启动的。 这里,我们像上一篇文章Android应用程序启动过程源代码分析一样,采用再上一篇文章Android...
  • 如何看懂源代码--(分析源代码方法)

    万次阅读 多人点赞 2018-03-15 11:49:31
    不管是参考也好,从开源抓下来研究也好,为了了解箇中含意,在有限的时间下,不免会对庞大的源代码解读感到压力。 网路上有一篇关于分析看代码的方法,做为程序设计师的您,不妨参考看看, 换个角度来分析。 也能...
  • Android日志系统Logcat源代码简要分析

    万次阅读 多人点赞 2011-07-15 02:27:42
    在前面两篇文章Android日志系统驱动程序Logger源代码分析和Android应用程序框架层和系统运行库层日志系统源代码中,介绍了Android内核空间层、系统运行库层和应用程序框架层日志系统相关的源代码,其中,后一篇文章...
  • 在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应用程序就是Launcher了,...
  • 读《莱昂氏UNIX源代码分析

    千次阅读 2010-02-09 17:57:00
    在unix早期的代码中,schedule和swap两个核心任务都是由0号进程来负责的,这个朴实的设计就是unix系统最最原始的设计,因为unix在开始设计的时候十分清楚进程应该做什么不应该做什么,应该做它本职的工作,而诸如...
  • MATLAB聚类分析源代码

    千次阅读 2018-09-04 11:36:29
    clc clear x=[75.2 0.14 1.86 0.91 5.21; 75.15 0.16 2.11 0.74 4.93; 72.19 0.13 1.52 0.69 4.65; 72.35 0.13 1.37 0.83 4.87; 72.74 0.10 1.41 0.72 4.99;...73.29 0.033 1.07 0.17 3.15;...73.72 0.033 0.77 0.28 2.7.....
  • 以太坊源代码分析

    千次阅读 2018-11-05 13:18:43
    以太坊的源代码是开源的,基于Go语言的版本的源代码获取参考下面的命令。还有用c++开发的源代码,不在本文的探讨范围之内。 git clone https://github.com/ethereum/go-ethereum.git 安装详细还可以参考手把手教你...
  • Jinja2 源代码分析

    千次阅读 2015-04-24 22:36:57
    介绍了 Jinja 2.0 的源代码结构,以及 Jinja 2.0 要解决的核心问题,并举例说明了如何将 HTML 模板转化为 Python 代码的过程。

空空如也

1 2 3 4 5 ... 20
收藏数 583,842
精华内容 233,536
关键字:

代码分析