android应用 linux

2011-08-22 00:57:41 Luoshengyang 阅读数 152112

        上文介绍了Android应用程序的启动过程,即应用程序默认Activity的启动过程,一般来说,这种默认Activity是在新的进程和任务中启动的;本文将继续分析在应用程序内部启动非默认Activity的过程的源代码,这种非默认Activity一般是在原来的进程和任务中启动的。

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

        这里,我们像上一篇文章Android应用程序启动过程源代码分析一样,采用再上一篇文章Android应用程序的Activity启动过程简要介绍和学习计划所举的例子来分析在应用程序内部启动非默认Activity的过程。

        在应用程序内部启动非默认Activity的过程与在应用程序启动器Launcher中启动另外一个应用程序的默认Activity的过程大体上一致的,因此,这里不会像上文Android应用程序启动过程源代码分析一样详细分析每一个步骤,我们着重关注有差别的地方。

        回忆一下Android应用程序的Activity启动过程简要介绍和学习计划一文所用的应用程序Activity,它包含两个Activity,分别是MainActivity和SubActivity,前者是应用程序的默认Activity,后者是非默认Activity。MainActivity启动起来,通过点击它界面上的按钮,便可以在应用程序内部启动SubActivity。

        我们先来看一下应用程序的配置文件AndroidManifest.xml,看看这两个Activity是如何配置的:

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="shy.luo.activity"  
    android:versionCode="1"  
    android:versionName="1.0">  
    <application android:icon="@drawable/icon" android:label="@string/app_name">  
        <activity android:name=".MainActivity"  
                  android:label="@string/app_name">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
        <activity android:name=".SubActivity"  
                  android:label="@string/sub_activity">  
            <intent-filter>  
                <action android:name="shy.luo.activity.subactivity"/>  
                <category android:name="android.intent.category.DEFAULT"/>  
            </intent-filter>  
        </activity>  
    </application>  
</manifest>  
        这里可以很清楚地看到,MainActivity被配置成了应用程序的默认Activity,而SubActivity可以通过名称“shy.luo.activity.subactivity”隐式地启动,我们来看一下src/shy/luo/activity/MainActivity.java文件的内容,可以清楚地看到SubActivity是如何隐式地启动的:

public class MainActivity extends Activity  implements OnClickListener {  
    ......  
  
    @Override  
    public void onClick(View v) {  
        if(v.equals(startButton)) {  
            Intent intent = new Intent("shy.luo.activity.subactivity");  
            startActivity(intent);  
        }  
    }  
}  
       这里,首先创建一个名称为“shy.luo.activity.subactivity”的Intent,然后以这个Intent为参数,通过调用startActivity函数来实现隐式地启动SubActivity。

       有了这些背景知识后,我们就来看一下SubActivity启动过程的序列图:


       与前面介绍的MainActivity启动过程相比,这里少了中间创建新的进程的步骤;接下来,我们就详细分析一下SubActivity与MainActivity启动过程中有差别的地方,相同的地方请参考Android应用程序启动过程源代码分析一文。

       Step 1. Activity.startActivity

       这一步与上一篇文章Android应用程序启动过程源代码分析的Step 2大体一致,通过指定名称“shy.luo.activity.subactivity”来告诉应用程序框架层,它要隐式地启动SubActivity。所不同的是传入的参数intent没有Intent.FLAG_ACTIVITY_NEW_TASK标志,表示这个SubActivity和启动它的MainActivity运行在同一个Task中。

       Step 2. Activity.startActivityForResult

       这一步与上一篇文章Android应用程序启动过程源代码分析的Step 3一致。

       Step 3. Instrumentation.execStartActivity

       这一步与上一篇文章Android应用程序启动过程源代码分析的Step 4一致。

       Step 4. ActivityManagerProxy.startActivity

       这一步与上一篇文章Android应用程序启动过程源代码分析的Step 5一致。

       Step 5. ActivityManagerService.startActivity

       这一步与上一篇文章Android应用程序启动过程源代码分析的Step 6一致。

       Step 6. ActivityStack.startActivityMayWait

       这一步与上一篇文章Android应用程序启动过程源代码分析的Step 7一致。

       Step 7. ActivityStack.startActivityLocked

       这一步与上一篇文章Android应用程序启动过程源代码分析的Step 8一致。

       Step 8. ActivityStack.startActivityUncheckedLocked

       这一步与上一篇文章Android应用程序启动过程源代码分析的Step 9有所不同,主要是当前要启动的Activity与启动它的Activity是在同一个Task中运行的,我们来详细看一下。这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

public class ActivityStack {

	......

	final int startActivityUncheckedLocked(ActivityRecord r,
		   ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
		   int grantedMode, boolean onlyIfNeeded, boolean doResume) {
		final Intent intent = r.intent;
		final int callingUid = r.launchedFromUid;

		int launchFlags = intent.getFlags();

		......

		if (sourceRecord == null) {
		   ......
		} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
		   ......
		} else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
		   ......
		}

		if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
		   ......
		}

		boolean addingToTask = false;
		if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
		   (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
		   || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
		   || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
			......
		}

		if (r.packageName != null) {
		   // If the activity being launched is the same as the one currently
		   // at the top, then we need to check if it should only be launched
		   // once.
		   ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
		   if (top != null && r.resultTo == null) {
			   if (top.realActivity.equals(r.realActivity)) {
				   ......
			   }
		   }

		} else {
		   ......
		}

		boolean newTask = false;

		// Should this be considered a new task?
		if (r.resultTo == null && !addingToTask
		   && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
			......

		} else if (sourceRecord != null) {
			......
			// An existing activity is starting this new activity, so we want
			// to keep the new one in the same task as the one that is starting
			// it.
			r.task = sourceRecord.task;
			......

		} else {
		   ......
		}

		......

		startActivityLocked(r, newTask, doResume);
		return START_SUCCESS;
	}

	......

}
        这里,参数intent的标志位Intent.FLAG_ACTIVITY_NEW_TASK没有设置,在配置文件AndriodManifest.xml中,SubActivity也没有配置启动模式launchMode,于是它就默认标准模式,即ActivityInfo.LAUNCH_MULTIPLE,因此,下面if语句不会执行:
    if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
        (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
	|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
	......
    }
        于是,变量addingToTask为false。

         继续往下看:

    if (r.packageName != null) {
	// If the activity being launched is the same as the one currently
	// at the top, then we need to check if it should only be launched
	// once.
        ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
	if (top != null && r.resultTo == null) {
	     if (top.realActivity.equals(r.realActivity)) {
		......
	     }
	}

    } 
        这里看一下当前要启动的Activity是否就是当前堆栈顶端的Activity,如果是的话,在某些情况下,就不用再重新启动了。函数topRunningNonDelayedActivityLocked返回当前
堆栈顶端的Activity,这里即为MainActivity,而当前要启动的Activity为SubActivity,因此,这二者不相等,于是跳过里面的if语句。

        接着往下执行:

    // Should this be considered a new task?
    if (r.resultTo == null && !addingToTask
	&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
	......

    } else if (sourceRecord != null) {
	......
	// An existing activity is starting this new activity, so we want
	// to keep the new one in the same task as the one that is starting
	// it.
	r.task = sourceRecord.task;
	......

    } else {
	......
    }
        前面说过参数intent的标志位Intent.FLAG_ACTIVITY_NEW_TASK没有设置,而这里的sourceRecord即为当前执行启动Activity操作的Activity,这里即为MainActivity,因此,它不为null,于是于MainActivity所属的Task设置到r.task中去,这里的r即为SubActivity。看到这里,我们就知道SubActivity要和MainActivity运行在同一个Task中了,同时,变量newTask的值为false。

        最后,函数进 入startActivityLocked(r, newTask, doResume)进一步处理了。这个函数同样是定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

public class ActivityStack {

	......

	private final void startActivityLocked(ActivityRecord r, boolean newTask,
			boolean doResume) {
		final int NH = mHistory.size();

		int addPos = -1;

		if (!newTask) {
			// If starting in an existing task, find where that is...
			boolean startIt = true;
			for (int i = NH-1; i >= 0; i--) {
				ActivityRecord p = (ActivityRecord)mHistory.get(i);
				if (p.finishing) {
					continue;
				}
				if (p.task == r.task) {
					// Here it is!  Now, if this is not yet visible to the
					// user, then just add it without starting; it will
					// get started when the user navigates back to it.
					addPos = i+1;
					if (!startIt) {
						mHistory.add(addPos, r);
						r.inHistory = true;
						r.task.numActivities++;
						mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
							r.info.screenOrientation, r.fullscreen);
						if (VALIDATE_TOKENS) {
							mService.mWindowManager.validateAppTokens(mHistory);
						}
						return;
					}
					break;
				}
				if (p.fullscreen) {
					startIt = false;
				}
			}
		}

		......

		// Slot the activity into the history stack and proceed
		mHistory.add(addPos, r);
		r.inHistory = true;
		r.frontOfTask = newTask;
		r.task.numActivities++;

		......

		if (doResume) {
			resumeTopActivityLocked(null);
		}
	}

	......

}
        这里传进来的参数newTask为false,doResume为true。当newTask为false,表示即将要启动的Activity是在原有的Task运行时,如果这个原有的Task当前对用户不可见时,这时候就不需要继续执行下去了,因为即使把这个Activity启动起来,用户也看不到,还不如先把它保存起来,等到下次这个Task对用户可见的时候,再启动不迟。这里,这个原有的Task,即运行MainActivity的Task当前对用户是可见的,因此,会继续往下执行。

        接下去执行就会把这个SubActivity通过mHistroy.add(addPos, r)添加到堆栈顶端去,然后调用resumeTopActivityLocked进一步操作。

        Step 9. ActivityStack.resumeTopActivityLocked

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 10一致。 

        但是要注意的是,执行到这个函数的时候,当前处于堆栈顶端的Activity为SubActivity,ActivityStack的成员变量mResumedActivity指向MainActivity。
        Step 10. ActivityStack.startPausingLocked
        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 11一致。

        从这里开始,ActivityManagerService通知MainActivity进入Paused状态。

        Step 11. ApplicationThreadProxy.schedulePauseActivity

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 12一致。

        Step 12. ApplicationThread.schedulePauseActivity

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 13一致。

        Step 13. ActivityThread.queueOrSendMessage
        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 14一致。

        Step 14. H.handleMessage

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 15一致。

        Step 15. ActivityThread.handlePauseActivity

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 16一致。

        Step 16. ActivityManagerProxy.activityPaused

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 17一致。

        Step 17. ActivityManagerService.activityPaused

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 18一致。

        Step 18. ActivityStack.activityPaused

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 19一致。

        Step 19. ActivityStack.completePauseLocked

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 20一致。

        执行到这里的时候,MainActivity就进入Paused状态了,下面就开始要启动SubActivity了。

        Step 20. ActivityStack.resumeTopActivityLokced

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 21一致。

        Step 21. ActivityStack.startSpecificActivityLocked

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 22就有所不同了,这里,它不会调用mService.startProcessLocked来创建一个新的进程来启动新的Activity,我们来看一下这个函数的实现,这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

public class ActivityStack {

	......

	private final void startSpecificActivityLocked(ActivityRecord r,  
			boolean andResume, boolean checkConfig) {  
		// Is this activity's application already running?  
		ProcessRecord app = mService.getProcessRecordLocked(r.processName,  
				r.info.applicationInfo.uid);  

		......  

		if (app != null && app.thread != null) {  
			try {  
				realStartActivityLocked(r, app, andResume, checkConfig);  
				return;  
			} catch (RemoteException e) {  
				......  
			}  
		}  

		......

	}  

	......

}
        这里由于不是第一次启动应用程序的Activity(MainActivity是这个应用程序第一个启动的Activity),所以下面语句:
    ProcessRecord app = mService.getProcessRecordLocked(r.processName,  
	r.info.applicationInfo.uid);  
        取回来的app不为null。在上一篇文章Android应用程序启动过程源代码分析中,我们介绍过,在Activity应用程序中的AndroidManifest.xml配置文件中,我们没有指定application标签的process属性,于是系统就会默认使用package的名称,这里就是"shy.luo.activity"了。每一个应用程序都有自己的uid,因此,这里uid + process的组合就可以创建一个全局唯一的ProcessRecord。这个ProcessRecord是在前面启动MainActivity时创建的,因此,这里将它取回来,并保存在变量app中。注意,我们也可以在AndroidManifest.xml配置文件中指定SubActivity的process属性值,这样SubActivity就可以在另外一个进程中启动,不过很少有应用程序会这样做,我们不考虑这种情况。

        这个app的thread也是在前面启动MainActivity时创建好的,于是,这里就直接调用realStartActivityLocked函数来启动新的Activity了,新的Activity的相关信息都保存在参数r中了。

        Step 22. ActivityStack.realStartActivityLocked

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 28一致。
        Step 23. ApplicationThreadProxy.scheduleLaunchActivity

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 29一致。

        Step 24. ApplicationThread.scheduleLaunchActivity

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 30一致。

        Step 25. ActivityThread.queueOrSendMessage

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 31一致。

        Step 26. H.handleMessage

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 32一致。

        Step 27. ActivityThread.handleLaunchActivity

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 33一致。

        Step 28. ActivityThread.performLaunchActivity

        这一步与上一篇文章Android应用程序启动过程源代码分析的Step 34一致,不过,这里要从ClassLoader里面加载的类就是shy.luo.activity.SubActivity了。

        Step 29. SubAcitiviy.onCreate

        这个函数定义在packages/experimental/Activity/src/shy/luo/activity/SubActivity.java文件中,这是我们自定义的app工程文件:

public class SubActivity extends Activity implements OnClickListener {

	......

	@Override
	public void onCreate(Bundle savedInstanceState) {
		......

		Log.i(LOG_TAG, "Sub Activity Created.");
	}

	......

}
       这样,SubActivity就在应用程序Activity内部启动起来了。
       在应用程序内部启动新的Activity的过程要执行很多步骤,但是整体来看,主要分为以下四个阶段:

       一. Step 1 - Step 10:应用程序的MainActivity通过Binder进程间通信机制通知ActivityManagerService,它要启动一个新的Activity;
       二. Step 11 - Step 15:ActivityManagerService通过Binder进程间通信机制通知MainActivity进入Paused状态;
       三. Step 16 - Step 22:MainActivity通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态,于是ActivityManagerService就准备要在MainActivity所在的进程和任务中启动新的Activity了;
       四. Step 23 - Step 29:ActivityManagerService通过Binder进程间通信机制通知MainActivity所在的ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。

       和上一篇文章Android应用程序启动过程源代码分析中启动应用程序的默认Activity相比,这里在应用程序内部启动新的Activity的过程少了中间创建新的进程这一步,这是因为新的Activity是在已有的进程和任务中执行的,无须创建新的进程和任务。

      这里同样不少地方涉及到了Binder进程间通信机制,相关资料请参考Android进程间通信(IPC)机制Binder简要介绍和学习计划一文。

      这里希望读者能够把本文和上文Android应用程序启动过程源代码分析仔细比较一下应用程序的默认Activity和非默认Activity启动过程的不同之处,以加深对Activity的理解。

      最后,在本文和上文中,我们多次提到了Android应用程序中任务(Task)的概念,它既不是我们在Linux系统中所理解的进程(Process),也不是线程(Thread),它是用户为了完成某个目标而需要执行的一系列操作的过程的一种抽象。这是一个非常重要的概念,它从用户体验的角度出发,界定了应用程序的边界,极大地方便了开发者利用现成的组件(Activity)来搭建自己的应用程序,就像搭积木一样,而且,它还为应用程序屏蔽了底层的进程,即一个任务中的Activity可以都是运行在同一个进程中,也中可以运行在不同的进程中。Android应用程序中的任务的概念,具体可以参考官方文档http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html,上面已经介绍的非常清楚了。

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

2018-12-15 12:10:27 lin5103151 阅读数 7268

最近忙于查找Linux和android平台的资料,今天将其整理整理,根据本人拙见分享给大家。

Android和Linux作为现行主流的操作系统,无论在消费类产品还是在工控领域,都有广泛的应用。都说Android系统是脱胎于Linux系统,那么是不是Android是不是属于Linux的一种。现在就来谈谈Android和Linux系统的异同点。
在这里插入图片描述
1. 两者的共同点
Android是基于Linux内核的系统。Android和Linux系统的核心系统服务都依赖于Linux内核,如安全、内核管理、进程管理、网络堆栈、驱动模型等。Linux内核也作为两者的硬件和软件之间的抽象层,它隐藏具体硬件细节而为上层应用开发提供统一的服务。
在这里插入图片描述
2. 系统框架的差异
Linux系统与Android表面上为两个不同的操作系统,但是Android本质上还是一个Linux系统,只不过Google对它进行了裁剪和定制。
另外,相较于Linux系统,android在其Linux内核上面运行了一个叫Dalvik(4.4版本后改为ART)的Java虚拟机(JVM),因而使用JAVA开发的android的应用程序可以通过JAVA虚拟机运行在Android系统上。而Linux的应用程序使用的是C/C++开发的,可以机器码的形式运行在内核系统上,两者的系统框架差异,如图。
在这里插入图片描述
在这里插入图片描述

3. Linux与android底层驱动软件的差异
Android除了Linux内核具备的常用核心驱动外,还增加了自己专有的内核驱动程序,如显示驱动、蓝牙驱动、相机驱动、内存卡驱动、Binder IPC驱动等。
另外,Android系统为了保护硬件厂商的知识产权,增加了HAL层,可将驱动具体的实现部分抽取发到HAL中去实现,从而规避了硬件驱动开源的风险,所以android的驱动程序是分为两部分,一部分写入内核中,一部分写入HAL层中。
Android除要实现底层驱动的开发外,还需要根据JNI规则将驱动程序封装为JNI层接口,以达到应用程序(JAVA程序) 可通过JNI来调用内核驱动程序。以显示驱动程序Framebuffer为例,如图。
在这里插入图片描述
Linux系统的显示驱动模型
在这里插入图片描述
Android系统的显示驱动模型

(1)Linux系统:Framebuffer驱动只需要编译到Linux内核中,并留出相应的read、write、ioctl等接口,便可供上位机应用程序调用。
(2)Android系统:Framebuffer驱动先将简单的硬件寄存器读写操作的驱动编译进入内核中,再将具体的硬件实现方式的驱动写入HAL层中,并根据JNI规格封装为JNI接口,才可供上位机应用程序调用。

4. Linux与android应用层软件的差异
但是Android应用程序是使用java语言写的,不能直接调用C语言实现的系统接口,而Linux系统中可以用C语言调用系统接口来与内核进行通信,于是Android系统中就有了一个叫做JNI的概念,用实现java与C/C++程序之间的信息交互。

(1)Android应用程序访问Android内核驱动程序的方式:

  1. APP—>JNI—>Android内核驱动程序
  2. APP—>硬件访问服务—>Android内核驱动程序

(2)Linux应用程序访问linux驱动程序的方式

  1. APP—> Linux内核驱动程序
  2. APP—>C库—>Linux内核驱动程序

如果您喜欢,可关注个人公众号“电子应用学习馆”,获取更多的资料例程。
在这里插入图片描述

2018-08-24 16:39:26 cdemtronix 阅读数 1993

  Android是移动设备的主流操作系统,近年来越来越多的工业领域的客户开始关注基于Android操作系统的设备在工控领域的应用。鉴于Android是基于Linux内核的事实,我们发展了一种以双应用进程为特色的Android工控应用方案,并在ESM6802工控主板上加以实现。具体说来,就是在Linux平台上运行一个直接操作硬件接口的控制通讯管理进程,为保证运行效率,该进程采用C/C++语言编写(以下简称C进程或控制进程);另一方面在Android平台采用标准Java语言编写一个人机界面进程(以下简称Java进程)。底层的控制进程并不依赖与上层的Java进程而独立运行,两个进程之间通过本地IP进行通讯,控制进程处于服务器侦听模式,Java进程则为客户端模式。本方案的主要优点是客户可以直接继承已有的现成应用程序作为底层控制进程的基础,仅仅增加标准的Socket侦听功能,即可快速完成新的底层应用程序的设计。而界面的Java程序,由于不再涉及具体的工控硬件接口,属于单纯的Android程序,编程难度也大大降低。

 

  我们将通过多篇技术报告来具体介绍双进程方案在ESM6802主板上实现的相关技术。本文是《Android双应用进程工控方案》的第一篇,主要介绍在Android环境中,如何编译C/C++应用程序,下载并配置为开机启动程序。

 

1、重新编译C/C++应用程序

 

  如图1所示,由于传统的Linux程序依赖的是glibc库,而Android程序需要的是谷歌公司在AOSP(Android Open Source Project)中提供的Bionic库(比glibc小,提供了Android特定的函数)。所以,原来Linux上的C/C++程序要运行在Android系统上,必须要在Android的编译环境中重新编译。英创推荐使用Android官方开发工具Android Studio,下载CMake和NDK工具,进行C/C++程序的重新编译。

 

Android开机启动CC++应用程序.gif

图1 Android和Linux依赖库区别

 

  下面开始介绍使用Android Studio的NDK编译工具重新编译C/C++程序的过程。

 

  1.1 搭建Android Studio NDK编译环境

 

  Android Studio的安装具体过程请参考文档《Android Studio 应用开发简介》的第一章,在SDK Tools页面中一定要勾选NDK和CMake。

 

  1.2 在Android Studio中新建C++项目

 

Android开机启动CC++应用程序.gif

图2 新建C++项目

 

  首先新建一个Android Studio项目,并勾选Include C++ support选项,此处的Application name是Android app的名字,与最终需要的C++程序无关,用户可随意设定。然后一直点击下一步“Next”,直到图3页面,使用默认的工具链,点击Finish。

 

Android开机启动CC++应用程序.gif

图3 默认工具链

 

  点击finish后会进入项目编辑页面,进入到图4所示的项目视图,可以看到所有的目录结构,其中app/src/main/cpp目录、app/build.gradle和app/CMakeLists.txt是用户需要编辑修改的。然后,点击左上角File >> Project Structure进入图5的页面,检查NDK环境路径是否正确设置。

 

Android开机启动CC++应用程序.gif

图4 项目目录结构及要修改的文件

 

Android开机启动CC++应用程序.gif

图5 环境路径设置检查

 

  1.3 复制C/C++应用程序源码

 

  将原C/C++应用程序的所有源文件拷贝到app/src/main/cpp目录。

 

  1.4 修改CMakeLists.txt

 

  新的Android Studio已经支持使用cmake编译c++项目,这里提供对于简单项目使用的CMakeLists.txt,对于更复杂的需求,用户可以参考cmake官网文档https://cmake.org/cmake/help/v3.4/自行修改。

 

  app/CMakeLists.txt:

  cmake_minimum_required(VERSION 3.4.1)

  # Android 5.0 以上需要在此处设置PIE

  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE")

  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie")

  # 头文件目录

  include_directories(

  src/main/cpp

  )

  # 源文件目录

  aux_source_directory(src/main/cpp DIR_SRCS)

  # 添加要编译的可执行文件

  add_executable(serialControlDaemon ${DIR_SRCS})

 

  其中,add_executable表明要生成的是可执行文件,名字为SerialControlDaemon,源文件为DIR_SRCS变量代表的文件,而aux_source_directory将src/main/cpp目录下的所有文件赋给了变量DIR_SRCS。

 

  1.5 修改build.gradle

 

  app/build.gradle文件主要是设置构建app的一些参数,这里主要往android>> defaultConfig>>externalNativeBuild>>cmake添加targets和abiFilters两个参数。其中,targets表示生产目标文件的名字,与CmakeLists.txt中的相同;abiFilters表示要生产哪种cpu架构下的目标文件,这里使用armeabi-v7a。修改之后会在右上角提示需要同步项目,点击即可。

 

Android开机启动CC++应用程序.gif

图6 修改build.gradle

 

  1.6 编译cpp项目

 

  在Android Studio中直接使用Build>>Make Project即可编译整个项目,包括cpp和java。生成的目标文件在目录app/.externalNativeBuild/cmake/debug/armeabi-v7a目录下,名字为serialControlDaemon。

 

  1.7 下载目标文件到Android

 

  Android Studio集成了Android开发的所有工具,在Android Studio中使用adb push命令可以将编译得到的目标文件下载到Android目标板上。首先,要使用usb otg的调试线连接PC和目标板;然后点击左下角的Terminal窗口会弹出所在项目的命令行窗口;输入命令:

  adb push app\.externalNativeBuild\cmake\debug\armeabi-v7a\serialControlDaemo      /data/local

 

  这样,serialControlDaemon便下载到了目标板的/data/local目录下。这时,使用adb shell登陆到Android目标板的命令行,修改目标文件的运行权限并运行,整个过程如图7所示。程序正常运行起来后,表明整个编译过程没有问题,用户可以在命令行中按Ctrl+c停止运行应用程序,并输入exit命令退出adb shell登陆,然后进行下一步的开机自启动配置。

 

Android开机启动CC++应用程序.gif

图7 下载目标文件到Android

 

2、开机自启动配置

 

  ESM6802上电后通过uboot引导进入linux内核,内核完成一系列系统配置后会启动第一个用户进程:init进程。Android相关的启动过程也是从init开始的。在init进程中会挂载Android的文件系统,运行init.rc脚本。init进程启动过后,会fork出子进程去开启init.rc文件中配置的service。

 

  为了满足用户运行不同名字的应用程序,英创在init.rc中配置了一个usersh服务。usersh服务开机自动运行,具体过程用户不用关心。 要想开机自启动C/C++程序,用户只需要做两件事:

  ● 编辑userinfo.txt文件

  ● 复制userinfo.txt以及C/C++程序的目标文件到指定目录/sdcard/Download

 

  2.1 编辑userinfo.txt

 

  Android启动后,usersh服务会自动检测/sdcard/Download/userinfo.txt文件。如果userinfo.txt文件存在,usersh会去解析并启动userinfo.txt文件中指定的应用;如果userinfo.txt不存在,则结束usersh服务。userinfo.txt起到一个配置文件的作用,其格式如下:

  Name=serialControlDaemon

  Param=2

 

  其中,Name指定程序名字,Param指定要带的参数,没有可以不写。用户可以直接在Android Studio中创建并编辑userinfo.txt文件。

 

Android开机启动CC++应用程序.gif

图8 Android Studio中新建userinfo.txt

 

  2.2 复制userinfo.txt以及C/C++程序到指定目录/sdcard/Download

 

  Android系统中,不是每个目录都有读写以及可执行的权限,这里我们选择/sdcard/Download作为存储userinfo.txt和C/C++程序的指定目录。复制userinfo.txt以及C/C++程序到指定目录有两种方法:通过usb_otg接口使用Android Studio的adb push命令下载到ESM6802,或者通过U盘从PC端拷贝到ESM6802。用户按其中一种方法下载文件到指定目录后,重启ESM6802即可以开机启动userinfo.txt中指定的C/C++程序。

 

  1、Android Studio命令行下载userinfo.txt和C/C++程序到ESM6802

  使用Android Studio命令行下载文件到ESM6802,首先需要使用调试线连接PC和目标板的usb_otg接口。然后,在Android Studio的Terminal窗口输入:

  adb push app\.externalNativeBuild\cmake\debug\armeabi-v7a\serialControlDaemo      /sdcard/Download

  adb push app\userinfo.txt  /sdcard/Download

  重启设备即可实现开机自启动serialControlDaemon。

 

  2、U盘拷贝userinfo.txt和C/C++程序到ESM6802

  使用U盘拷贝userinfo.txt和C/C++程序到ESM6802,只需要将userinfo.txt和目标文件(serialControlDaemon)拷贝到U盘,插到ESM6802的USB接口上,打开Android的文件管理应用ES File Explorer,将userinfo.txt和serialControlDaemon拷贝到/sdcard/Download目录,重新启动即可。

 

  2.3 查看程序是否开机运行

 

  通过以上设置之后,Android开机boot_completed=1之后会启动应用程序serialControlDaemon,用户可以通过命令adb shell登陆consolo控制台,输入命令getprop | grep init.svc | grep usersh来查看usersh服务的运行状态;当然usersh实际运行的应用程序serialControlDaemon的进程状态可以通过ps | grep serialControlDaemon查看。

 

Android开机启动CC++应用程序.gif

图9 检测usersh服务运行状态

 

3、Q & A

 

  Q1:查看C/C++程序输出

  在Android控制台上看不到开机启动的C/C++程序输出信息,开发中如何在Android上调试C/C++程序?

  A1:使用kill命令终止掉已经启动的C/C++程序;然后,在Android命令行中执行命令:user.sh,即可手动启动C/C++应用程序,并且C/C++应用程序的输出信息将打印到Android控制台。

 

  Q2:关于userinfo.txt和C/C++程序指定目录的说明

  A2:userinfo.txt和C/C++程序指定目录要具有读写可执行权限,在2.2节中,adb push命令将C/C++应用程序(serialControlDaemon)下载到了/sdcard/Download目录,其实下载到/data/local也是可以的,而U盘却只能拷贝到/sdcard/Download/。这是因为usersh服务会比较/sdcard/Download/serialControlDaemon是否比/data/local/serialControlDaemon更新,如果是,则先用新文件覆盖旧文件,再运行/data/local/serialControlDaemon。因此,使用adb push命令的指定目录用/sdcard/Download/或者/data/local都是可以的;而使用U盘,则受限于ES File Manager应用不能访问/data/local目录,只能拷贝到/sdcard/Download。

 

  Q3:关于Android Studio的Terminal窗口

  A3:Android Studio的Terminal窗口在进入的时候,工作在PC的文件系统上,操作的文件都是PC上的;当使用adb shell登陆Android目标板之后,工作在Android目标板的文件系统上,操作的文件、执行的命令都是Android目标板上的;在使用adb shell登陆之后,可以使用exit命令退出登陆状态,返回到PC端的工作目录。

 

  Q4:adb连接不上设备

  使用adb devices查看一下是否有已连接的设备;检查usb_otg和PC端的物理连接;重新插拔一下调试线或者重启系统。

  如果ethernet正常工作,可以使用ethernet代替usb_otg,在Terminal中输入一下命令:

  $ adb usb

  restarting in USB mode

  $ adb devices

  List of devices attached

  ????????????    device

  $ adb tcpip 5555

  restarting in TCP mode port: 5555

  $ adb connect YOUR_IP_ADDRESS

  connected to YOUR_IP_ADDRESS:5555

  $ adb devices

  List of devices attached

  ????????????    device

  YOUR_IP_ADDRESS:5555 device

  退出:

  adb disconnect YOUR_IP_ADDRESS

 

  Q5:常用命令

  查看所有service运行状态:getprop | grep init.svc

  adb相关:

  adb devices  查看usb_otg已连接的设备

  adb push localfile remotepath 将PC端的localfile下载到Android端的remotepath目录下。

  adb pull remotefile 复制Android端的remotefile文件到PC端的当前目录

 

成都英创信息技术有限公司 http://www.emtronix.com

原文下载:http://www.emtronix.com/download/android_c_cpp.pdf

2016-09-12 19:31:58 u011913612 阅读数 4811

一直都想亲自做一次使用android应用程序访问Linux内核驱动的尝试,但总是没能做到。最近抽出时间,下决心重新尝试一次。尝试的开始当然是先写一个Linux内核驱动了。
我希望写一个简单测驱动程序,实现写一个字符串进去,然后再把它读出来的功能。驱动中会创建dev/hello设备节点和/sys/class/hello/hello/val 设备节点,没有实现proc/下的对应的设备节点。/sys/class/hello/hello/val 主要用于快速测试,而dev/hello则主要用于供上层应用调用。
这篇博客参考了老罗的android之旅相关博客:http://blog.csdn.net/luoshengyang/article/details/6568411

一。驱动源码

代码我已经在android6.0的linux kernel上测试过了,代码中有响应的注释,所以这里直接贴出代码:

hello.c

文件如下:

#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/types.h>  
#include <linux/fs.h>  
#include <linux/proc_fs.h>  
#include <linux/device.h>  

#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/fcntl.h>

#include <linux/poll.h>
#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>

#include <asm/uaccess.h>
#include <linux/slab.h>


#include "hello.h"  

/*定义主设备和从设备号变量*/  
static int hello_major = 0;  
static int hello_minor = 0;  

/*设备类别和设备变量*/  
static struct class* hello_class = NULL;  
static struct hello_test_dev* hello_dev = NULL;  

/*传统的设备文件操作方法*/  
static int hello_open(struct inode* inode, struct file* filp);  
static int hello_release(struct inode* inode, struct file* filp);  
static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);  
static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);  

/*设备文件操作方法表*/  
static struct file_operations hello_fops = {  
    .owner = THIS_MODULE,  
    .open = hello_open,  
    .release = hello_release,  
    .read = hello_read,  
    .write = hello_write,   
};  

/*访问设置属性方法*/  
static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr,  char* buf);  
static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);  

/*定义设备属性*/  
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store); 

/*打开设备方法*/  
static int hello_open(struct inode* inode, struct file* filp) {  
    struct hello_test_dev* dev;          

    /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/  
    dev = container_of(inode->i_cdev, struct hello_test_dev, dev);  
    filp->private_data = dev;  

    return 0;  
}  

/*设备文件释放时调用,空实现*/  
static int hello_release(struct inode* inode, struct file* filp) {  
    return 0;  
}  

/*读内存*/  
static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {  
    ssize_t err = 0;  
    struct hello_test_dev* dev = filp->private_data;          

    /*同步访问*/  
    if(down_interruptible(&(dev->sem))) {  
        return -ERESTARTSYS;  
    }  

    if(count < sizeof(dev->val)) {  
        goto out;  
    }          

    /*读字符串*/  
    if(copy_to_user(buf, dev->val, sizeof(dev->val))) {  
        err = -EFAULT;  
        goto out;  
    }  

    err = sizeof(dev->val);  

out:  
    up(&(dev->sem));  
    return err;  
}  

/*写字符串*/  
static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {  
    struct hello_test_dev* dev = filp->private_data;  
    ssize_t err = 0;          

    /*同步访问*/  
    if(down_interruptible(&(dev->sem))) {  
        return -ERESTARTSYS;          
    }          

    if(count != sizeof(dev->val)) {  
        goto out;          
    }          

    /*将用户写进来的字符串保存到驱动的内存中*/  
    if(copy_from_user(dev->val, buf, count)) {  
        err = -EFAULT;  
        goto out;  
    }  

    err = sizeof(dev->val);  

out:  
    up(&(dev->sem));  
    return err;  
}  


/*写字符串到内存*/  
static ssize_t __hello_set_val(struct hello_test_dev* dev, const char* buf, size_t count) {       

    /*同步访问*/          
    if(down_interruptible(&(dev->sem))) {                  
        return -ERESTARTSYS;          
    }          
    printk(KERN_ALERT"__hello_set_val.buf: %s  count:%d\n",buf,count);
    printk(KERN_ALERT"__hello_set_val.dev->val: %s  count:%d\n",dev->val,count);
    strncpy(dev->val,buf, count);
    printk(KERN_ALERT"__hello_set_val.dev->val: %s  count:%d\n",dev->val,count);
    up(&(dev->sem));  

    return count;  
}  

/*读取设备属性val*/  
static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf) {  
    struct hello_test_dev* hdev = (struct hello_test_dev*)dev_get_drvdata(dev);          
    printk(KERN_ALERT"hello_val_show.\n");
    printk(KERN_ALERT"%s\n",hdev->val);
    return 0;
}  

/*写设备属性val*/  
static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {   
    struct hello_test_dev* hdev = (struct hello_test_dev*)dev_get_drvdata(dev);    
    printk(KERN_ALERT"hello_val_store.buf: %s  count:%d\n",buf,count);

    return __hello_set_val(hdev, buf, count);  
}  


/*初始化设备*/  
static int  __hello_setup_dev(struct hello_test_dev* dev) {  
    int err;  
    dev_t devno = MKDEV(hello_major, hello_minor);  

    memset(dev, 0, sizeof(struct hello_test_dev));  

    cdev_init(&(dev->dev), &hello_fops);  
    dev->dev.owner = THIS_MODULE;  
    dev->dev.ops = &hello_fops;          

    /*注册字符设备*/  
    err = cdev_add(&(dev->dev),devno, 1);  
    if(err) {  
        return err;  
    }          

    /*初始化信号量和寄存器val的值*/  
    init_MUTEX(&(dev->sem));  
    dev->val = kmalloc(10,GFP_KERNEL);  
    strncpy(dev->val,"hello",sizeof("hello"));
    return 0;  
}  

/*模块加载方法*/  
static int __init hello_init(void){   
    int err = -1;  
    dev_t dev = 0;  
    struct device* temp = NULL;  

    printk(KERN_ALERT"hello_init.\n");          

    /*动态分配主设备和从设备号*/  
    err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME);  
    if(err < 0) {  
        printk(KERN_ALERT"Failed to alloc char dev region.\n");  
        goto fail;  
    }  

    hello_major = MAJOR(dev);  
    hello_minor = MINOR(dev);          

    /*分配helo设备结构体变量*/  
    hello_dev = kmalloc(sizeof(struct hello_test_dev), GFP_KERNEL);  
    if(!hello_dev) {  
        err = -ENOMEM;  
        printk(KERN_ALERT"Failed to alloc hello_dev.\n");  
        goto unregister;  
    }          

    /*初始化设备*/  
    err = __hello_setup_dev(hello_dev);  
    if(err) {  
        printk(KERN_ALERT"Failed to setup dev: %d.\n", err);  
        goto cleanup;  
    }          

    /*在/sys/class/目录下创建设备类别目录hello*/  
    hello_class = class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME);  
    if(IS_ERR(hello_class)) {  
        err = PTR_ERR(hello_class);  
        printk(KERN_ALERT"Failed to create hello class.\n");  
        goto destroy_cdev;  
    }          

    /*在/dev/目录和/sys/class/hello目录下分别创建设备文件hello*/  
    temp = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME);  
    if(IS_ERR(temp)) {  
        err = PTR_ERR(temp);  
        printk(KERN_ALERT"Failed to create hello device.");  
        goto destroy_class;  
    }          

    /*在/sys/class/hello/hello目录下创建属性文件val*/  
    err = device_create_file(temp, &dev_attr_val);  
    if(err < 0) {  
        printk(KERN_ALERT"Failed to create attribute val.");                  
        goto destroy_device;  
    }  

    dev_set_drvdata(temp, hello_dev);          

    printk(KERN_ALERT"Succedded to initialize hello device.\n");  
    return 0;  

destroy_device:  
    device_destroy(hello_class, dev);  

destroy_class:  
    class_destroy(hello_class);  

destroy_cdev:  
    cdev_del(&(hello_dev->dev));  

cleanup:  
    kfree(hello_dev);  

unregister:  
    unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1);  

fail:  
    return err;  
}  

/*模块卸载方法*/  
static void __exit hello_exit(void) {  
    dev_t devno = MKDEV(hello_major, hello_minor);  

    printk(KERN_ALERT"hello_exit\n");          

    /*销毁设备类别和设备*/  
    if(hello_class) {  
        device_destroy(hello_class, MKDEV(hello_major, hello_minor));  
        class_destroy(hello_class);  
    }          

    /*删除字符设备和释放设备内存*/  
    if(hello_dev) {  
        cdev_del(&(hello_dev->dev));  
        kfree(hello_dev);  
    }          
    if(hello_dev->val != NULL){
     kfree(hello_dev->val);
    }
    /*释放设备号*/  
    unregister_chrdev_region(devno, 1);  
}  

MODULE_LICENSE("GPL");  
MODULE_DESCRIPTION("Test Driver");  

module_init(hello_init);  
module_exit(hello_exit);  

hello.h

文件如下:

#ifndef _HELLO_TEST_H_  
#define _HELLO_ANDROID_H_  

#include <linux/cdev.h>  
#include <linux/semaphore.h>  

#define HELLO_DEVICE_NODE_NAME  "hello"  
#define HELLO_DEVICE_FILE_NAME  "hello"  
#define HELLO_DEVICE_CLASS_NAME "hello"  

struct hello_test_dev {  
    char * val;  
    struct semaphore sem;  
    struct cdev dev;  
};  

#endif  

二。编译驱动

在linux源码目录的driver下新建hello目录,把hello.c和hello.h文件放入其中。新增Makefile和Kconfig文件:

1.Makefile,用于编译驱动

obj-$(CONFIG_HELLO) += hello.o

2.Kconfig

       config HELLO
           tristate "Test Driver"
           default n
           help
           This is the test driver.

3修改driver目录下的Makefile:

添加如下一项:

obj-$(CONFIG_HELLO) += hello/

4.修改driver目录下的Kconfig

添加如下一项:

source "drivers/hello/Kconfig"

5.make menuconfig

如果对编译linux kernel不熟悉请自行百度。
配置界面如下:
这里写图片描述

可以看到这里出现了我们在Kconfig中添加的Test Driver选项,我们把它选择成以模块的形式编译,这更有利于我们的测试,这样我们就不需要重新少些linux kernel了。Android的linux kernel一般是打包进boot.img文件的,如果想把模块编译进Linux内核,并且重写烧写linux kernel ,建议把kernel放到out/target/product/xxx/目录下,然后使用make bootimage-nodeps能快速生成boot.img文件,具体kernel放置的位置还需要看android编译系统相关配置,这里就不深究了。

6.make -j16

执行make -j16(-j16标示使用16个cpu来编译,只是个请求),这个过程还是很快的,几分钟就可以编译成功。编译后的hello目录如下:
这里写图片描述
可以看到生成了hello.ko文件。

三.使用/sys/class/hello/hello/val 进行测试

1.把hello.ko拷贝到android设备上

建议直接使用adb push hello.ko /data 即可,也可以用U盘拷贝。总之,把它放到android设备上。

2.装载驱动

cd /data
insmod hello.ko
这个时候可以看到/dev/hello /sys/class/hello/hello/val文件已经出现

3.测试

输入命令:echo haha > val
打印如下:
[ 9641.053505] hello_val_store.buf: haha
[ 9641.053505] count:5
[ 9641.060167] __hello_set_val.buf: haha
[ 9641.060167] count:5
[ 9641.066841] __hello_set_val.dev->val: hello count:5
[ 9641.073088] __hello_set_val.dev->val: haha
[ 9641.073088] count:5
可以看到数据已经写入,
使用cat val 读取:
打印如下:
[ 9644.953496] hello_val_show.
[ 9644.957127] haha
[ 9644.957127]
可以看到haha打印出来了,书名写入是成功的。

四.使用/dev/hello测试

首先在android源码的packages目录下新建一个hellotest目录,该目录下新建hellotest.c和Android.mk两个文件。

1.hellotest.c

这个文件用来打开/dev/hello文件并尝试读和写字符串操作,并打印相关信息,源码如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <fcntl.h>  
#define HELLO_DEVICE "/dev/hello"  
int main(int argc, char** argv)  
{  
    int fd = -1;  
    char * str = malloc(10);
    //打开/dev/hello
    fd = open(HELLO_DEVICE, O_RDWR);  
    if(fd == -1) {  
        printf("Failed to open device %s.\n", HELLO_DEVICE);  
        return -1;  
    }  

    printf("Read original value:\n"); 
    //先读一次数据看看
    read(fd, str, 10);  
    printf("read data: %s\n", str);  
    strncpy(str,"nihao",sizeof("nihao"));  
    printf("write nihao\n");  
    //写nihao进去
    write(fd, str, sizeof(str));  

    printf("Read the value again:\n");  
    //读看看是不是nihao
        read(fd, str, 10);  
        printf("read data: %s\n", str);  
    close(fd);  
    return 0;  
}  

2.Android.mk

android下编译应用程序使用Android.mk还是很方便的:

      LOCAL_PATH := $(call my-dir)

      include $(CLEAR_VARS)

      LOCAL_MODULE_TAGS := optional

      LOCAL_MODULE := hellotest

      LOCAL_SRC_FILES := $(call all-subdir-c-files)

      include $(BUILD_EXECUTABLE)

3.编译

执行mm命令即可
编译后的文件在out/target/product/xxx/obj/EXECUTABLES/hellotest_intermediates/目录下,该目录编译后如图:
这里写图片描述
关于mm命令编译执行文件的过程,可以参考我的这篇博客: Android编译系统<二>-mm编译单个模块

4.测试

使用adb push hellotest /data把hellotest 推送到android设备上来,然后chmod +x hellotest添加可执行权限。
最后./hellotest
打印如下:

Read original value:
read data: hell
write nihao
Read the value again:
read data: nihao

可见测试成功。

2016-01-18 21:43:37 WannerWang 阅读数 1895

 Android Linux的关系

1Android采用Linux作为内核

2Androidlinux内核作了修改,已适应其在移动设备上的应用。

Android继承于Linux

Android是在Linux的内核基础上运行的,提供的核心系统服务包括安全,内存管理,进程管理,网络组和驱动模型等内容,内核部分相当于一个介于硬件层和系统中其他软件组之间的一个抽象层次,但严格上不算是linux操作系统

AndroidLinux内核区别:

Android系统的系统层面的的底层是Linux,中间加上了一个叫作Dalvilkjava虚拟机,表面层上面是Android运行库,每个Android应用都运行在自己的进程上,享有dalivk虚拟机为它分配的专有实例,为了支持多个虚拟机在同一设备上高效运行,Dalvilk被改写过,Davlik虚拟机执行的是Dalvik格式的文件(.dex),该格式化经过优化,以降低内存耗用到最低,java编译器将java源文件转换为class文件,class文件被内置的dx工转换为dex格式文件,这种文件在Dalvik虚拟机上注册并运行。

Android系统的应用软件都是在都是运行Dalvik 之上的java软件,而Dalvilk是运行在Linux中的,在一些底层的功能 比如线程和底层内存管理方面,Dalvilk虚拟机依赖linux内核,由此可见Android是运行在Linux之上的操作系统,但它本身不能算是Linux内核的某个版本。