在写一个Bash脚本的时候碰到一个问题,这个脚本是用来启动一个程序B的,而这个脚本又被另一个程序A调用,结果发现新启动的B进程中有很多A进 程打开的文件描述符(如Socket)。因此决定在脚本中将它们关闭,因为为了简单起见,我在A程序中使用了system()来启动该脚本。增加了关闭文 件描述符的脚本如下:
#!/bin/sh cd $(dirname "$0") || exit 1 exec 3>&- exec 4>&- ./rbtunnel "$@" & #sleep 1
这里的关键点就是关闭文件描述的代码:exec fd>&- 也可以是:exec fd<&- 其中fd为文件描述符的数字,而文件描述符在Linux下可以通过lsof -c rbtunnel来查看。
源文:http://www.vktone.com/articles/how-to-close-file-descriptor-on-bash.html
已有 0 人发表留言,猛击->>这里<<-参与讨论
ITeye推荐
- —软件人才免语言低担保 赴美带薪读研!—
通过之前的两篇文章,我们已经对JNI技术有了一个了解-能够让java与其他的语言进行交互。
android的应用同样也是用java开发,所以也可以使用JNI技术来进行其他语言的调用,比如C\C++,大名顶顶的cocos2d-x就是用C来做开发语言的。
为什么要在Android中使用C\C++?
1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
2. 在NDK中调用第三方C/C++库,因为大部分的开源库都是用C/C++代码编写的。
3. 便于移植,用C/C++写得库可以方便在其他的嵌入式平台上再次使用。
Android的NDK提供了一些交叉编译工具链和Android自带的库,这些Android的库可以让开发者在编写本地语言的程序时调用。而NDK提供的交叉编译工具链就对已经编写好的C&C++代码进行编译,生成库。
当然了,也可以自己搭建交叉编译环境,而不用NDK的工具和库。然后生成库,只要规范操作,一样可以生成能让JAVA层成功调用的库文件的,不过这个属于高端玩家了,NDK还是比较容易上手一些。
注:交叉编译器(英语:Cross compiler)是指一个在某个系统平台下可以产生另一个系统平台可执行文件的编译器。交叉编译器在目标系统平台(开发出来的应用程序所运行的平台)难以或不容易编译时非常有用。
下面用两种方式在NDK下实现HelloWorld。(确保已配置好NDK)
1、先编写C库,再创建项目
1)创建库
新建一个目录,命名为HelloWorld,然后在里面新建一个名为jni的目录(名称一定要是jni,因为ndk-build的时候会在HelloWorld目录下寻找jni的目录,然后进行build),在jni目录下新建如下文件HelloWorld.c和Android.mk。
/* FileName:HelloWorld.c Description:realise of HelloWorld */ #include <string.h> #include <jni.h> jstring Java_com_empty_helloworld_HelloWorldActivity_helloWorldFromJNI( JNIEnv* env, jobject thiz ) { return (*env)->NewStringUTF(env, "HelloWorld! I am from JNI !"); }
注意函数的命名规则是:
Java+Android工程的包名+Android工程的Activity名+方法名,点号用下划线表示,这个写法很严格。
包名:com_empty_helloworld
Activity名:HelloWorldActivity
方法名:helloWorldFromJNI
#FileName:Android.mk #Description:makefile of Helloworld LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := HelloWorld LOCAL_SRC_FILES := HelloWorld.c include $(BUILD_SHARED_LIBRARY)
关于.mk
LOCAL_PATH := $(call my-dir)
一个Android.mk 文件首先必须定义好LOCAL_PATH变量。它用于在开发树中查找源文件。在这个例子中,宏函数’my-dir’, 由编译系统提供,用于返回当前路径(即包含Android.mk file文件的目录)。
include $( CLEAR_VARS)
CLEAR_VARS由编译系统提供,指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),
除LOCAL_PATH 。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
LOCAL_MODULE := HelloWorld
编译的目标对象,LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。
注意:编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'HelloWorld'的共享库模块,将会生成'libHelloWorld.so'文件。
重要注意事项:
如果你把库命名为‘libhello-jni’,编译系统将不会添加任何的lib前缀,也会生成 'HelloWorld.so',这是为了支持来源于Android平台的源代码的Android.mk文件,如果你确实需要这么做的话。
LOCAL_SRC_FILES :=HelloWorld.c
LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意,你不用在这里列出头文件和包含文件,因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文件就好。
注意,默认的C++源码文件的扩展名是’.cpp’. 指定一个不同的扩展名也是可能的,只要定义LOCAL_DEFAULT_CPP_EXTENSION变量,不要忘记开始的小圆点(也就是’.cxx’,而不是’cxx’)
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY表示编译生成共享库,是编译系统提供的变量,指向一个GNU Makefile脚本,负责收集自从上次调用'include $(CLEAR_VARS)'以来,定义在LOCAL_XXX变量中的所有信息,并且决定编译什么,如何正确地去做。还有 BUILD_STATIC_LIBRARY变量表示生成静态库:lib$(LOCAL_MODULE).a, BUILD_EXECUTABLE 表示生成可执行文件。
$NDK_ROOT/ndk-build
编译结果如下
编译成功后,会在在目录生成libs和obj两个文件夹,libs里面有刚刚编译成的libHelloWorld.so库则相应的可运行在arm平台上的HelloWorld库编译成功了.
2)创建Android项目
打开Eclipse创建一个Android项目,将刚才生成的armeabi文件夹拷贝到项目下libs文件夹内。
项目结构如下:
主Activity代码如下:
package com.empty.helloworld; import com.example.ndktest1.R; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.widget.TextView; public class HelloWorldActivity extends Activity { // statement of native function.Means it has been realized by native layer. public native String helloWorldFromJNI(); static { // Load library,No Prefix and Suffix. System.loadLibrary("HelloWorld"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView myTextView=(TextView)findViewById(R.id.myTextView); myTextView.setText(helloWorldFromJNI()); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } }
在模拟器里面跑一下
2、先创建项目,再写native实现
1)创建工程
在Eclipse中创建一个Android工程,主Activity内容如下(基本没变):
package com.empty.ndktest2; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.widget.TextView; public class HelloWorldActivity extends Activity { // statement of native function.Means it has been realized by native layer. public native String helloWorldFromJNI(); // Load library,No Prefix and Suffix. static { System.loadLibrary("HelloWorld"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_hello_world); TextView myTextView=(TextView)findViewById(R.id.myTextView); myTextView.setText(helloWorldFromJNI()); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_hello_world, menu); return true; } }
保存之后Eclipse会自动编译成class文件,一般在项目目录/bin/classes下面,如果没有自动编译,钩选Project->Build Automatically.
2)编写Native实现
首先利用javah这个工具生成相应的.h文件。
在Android工程项目文件夹下创建jni文件夹,Terminal进入项目目录,运行:
javah -classpath bin/classes -d jni com.empty.helloworld.HelloWorldActivity
-classpath是指定class文件的位置,-d是制定生成头文件的位置,最后则是完整的类名。
执行成功之后jni文件中就会多出一个.h文件。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_empty_helloworld_HelloWorldActivity */ #ifndef _Included_com_empty_helloworld_HelloWorldActivity #define _Included_com_empty_helloworld_HelloWorldActivity #
题目:输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出所有的数对(原文章只要求输出任意一对即可)。
例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。
分析:如果我们不考虑时间复杂度,最简单想法的莫过去先在数组中固定一个数字,再依次判断数组中剩下的n-1个数字与它的和是不是等于输入的数字。可惜这种思路需要的时间复杂度是O(n2)。
我们假设现在随便在数组中找到两个数。如果它们的和等于输入的数字,那太好了,我们找到了要找的两个数字;如果小于输入的数字呢?我们希望两个数字的和再大一点。由于数组已经排好序了,我们是不是可以把较小的数字的往后面移动一个数字?因为排在后面的数字要大一些,那么两个数字的和也要大一些,就有可能等于输入的数字了;同样,当两个数字的和大于输入的数字的时候,我们把较大的数字往前移动,因为排在数组前面的数字要小一些,它们的和就有可能等于输入的数字了。
我们把前面的思路整理一下:最初我们找到数组的第一个数字和最后一个数字。当两个数字的和大于输入的数字时,把较大的数字往前移动;当两个数字的和小于数字时,把较小的数字往后移动;当相等时,打完收工。这样扫描的顺序是从数组的两端向数组的中间扫描。
参考网址:http://zhedahht.blog.163.com/blog/static/2541117420072143251809/
public class findTwoNumbersWithSum { static int num1; static int num2; public static void main(String[] args) { // TODO Auto-generated method stub int[] arr = {1,2,4,7,11,13,15}; int sum = 15; if(!findTwoNumbers(arr,sum)){ return; } } public static boolean findTwoNumbers(int[] arr,int sum){ if(arr.length == 0){ System.out.println("数组为空!"); return false; } int front = 0; int behind = arr.length-1; while(front < behind){ if(arr[front] + arr[behind] < sum){ front ++; } else if(arr[front] + arr[behind] > sum){ behind--; } else{ num1 = arr[front]; num2 = arr[behind]; System.out.println("["+num1+","+num2+"]"); // front++; behind--; continue; } } return true; } }
输出结果:
[2,13]
[4,11]