android linux进程间通信——使用匿名管道

本文介绍了Linux中的管道概念,通过实例解析了如何在Android中使用匿名管道进行进程间通信。详细讲解了pipe函数的使用,以及在JNI中创建、读写和销毁匿名管道的方法,并给出了Java和C的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是管道
如果你使用过Linux的命令,那么对于管道这个名词你一定不会感觉到陌生,因为我们通常通过符号“|"来使用管道,但是管理的真正定义是什么呢?管道是一个进程连接数据流到另一个进程的通道,它通常是用作把一个进程的输出通过管道连接到另一个进程的输入。

举个例子,在shell中输入命令:ls -l | grep string,我们知道ls命令(其实也是一个进程)会把当前目录中的文件都列出来,但是它不会直接输出,而是把本来要输出到屏幕上的数据通过管道输出到grep这个进程中,作为grep这个进程的输入,然后这个进程对输入的信息进行筛选,把存在string的信息的字符串(以行为单位)打印在屏幕上。

二、pipe调用
pipe函数的原型如下:
#include <unistd.h>  
int pipe(int file_descriptor[2]);

我们可以看到pipe函数的定义非常特别,该函数在数组中墙上两个新的文件描述符后返回0,如果返回返回-1,并设置errno来说明失败原因。

数组中的两个文件描述符以一种特殊的方式连接起来,数据基于先进先出的原则,写到file_descriptor[1]的所有数据都可以从file_descriptor[0]读回来。由于数据基于先进先出的原则,所以读取的数据和写入的数据是一致的。
pipe是基于文件描述符工作的,所以在使用pipe后,数据必须要用底层的read和write调用来读取和发送。
不要用file_descriptor[0]写数据,也不要用file_descriptor[1]读数据,其行为未定义的,但在有些系统上可能会返回-1表示调用失败。数据只能从file_descriptor[0]中读取,数据也只能写入到file_descriptor[1],不能倒过来。

例子:
首先,我们在原先的进程中创建一个管道,发送消息,然后在进程中的线程接收消息(android开启进程是?),并关闭管道
jni下
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<jni.h>
#include<signal.h>
#include<android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "native-activity", __VA_ARGS__))
#ifndef NELEM
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif

static char CLASS_NAME[] = "com/sailor/Pipe";


static int fd[2];
static int nret = -1;

void DestroyUnNamePipe(JNIEnv *env, jobject thiz)
{
	if(nret == 0)
	{
		close(fd[0]);
		close(fd[1]);
	}
}

jint CreateUnNamePipe(JNIEnv *env, jobject thiz)
{
	int ret = -1;
	ret = pipe(fd);
	nret = ret;
	if(ret != 0)
	{
		LOGE("create unname pipe error:%d" ,ret);
	}

	return ret;
}
jstring ReadUNPMessage(JNIEnv *env, jobject thiz)
{
	char buffer[80+1];
	int ret = -1;
	if(nret == 0)
	{
		ret = read(fd[0], buffer, sizeof(buffer));
		LOGE("read ret:%d" , ret);
		if(ret == -1)
		{
			return NULL;
		}
	}

	return (*env)->NewStringUTF(env, buffer);
	
}

jint SendUNPMessage(JNIEnv *env, jobject thiz, jstring message)
{
	int ret = -1;
	if(nret == 0)
	{
		const char *pMsg = (*env)->GetStringUTFChars(env, message, 0);

		ret = write(fd[1], pMsg, strlen(pMsg));
		LOGE("send ret: %d", ret);
		
		(*env)->ReleaseStringUTFChars(env, message, pMsg);
	}
	return ret;
}

static JNINativeMethod mehods[] = {
	
		{ "createUnNamePipe", "()I", 
			(void *) CreateUnNamePipe },	
		{ "sendUNPMessage", "(Ljava/lang/String;)I", (void *) SendUNPMessage },	
		{ "readUNPMessage", "()Ljava/lang/String;", (void *) ReadUNPMessage },
		{ "destroyUnNamePipe", "()V", (void *) DestroyUnNamePipe }
};


static int registerNativeMethods(JNIEnv *env, const char* className,
                                 const JNINativeMethod* methods, int numMethods)
{
    int rc;
    jclass clazz;
    clazz = (*env)->FindClass(env, className);
    if (clazz == NULL) {
        LOGE("Native registration unable to find class '%s'\n", className);
        return -1;
    }
    if (rc = ((*env)->RegisterNatives(env, clazz, methods, numMethods)) < 0) {
        LOGE("RegisterNatives failed for '%s' %d\n", className, rc);
        return -1;
    }
    return 0;
}


static int register_jni(JNIEnv *env)
{
	return registerNativeMethods(env, CLASS_NAME, mehods, NELEM(mehods));
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
     JNIEnv* env = NULL;
     jint result = -1;    
 
     //获取JNI版本
     if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) 
     {
         LOGE("GetEnv failed!");
             return result;
     }

     if (register_jni(env) < 0) 
	{
	LOGE("register method failed!");
             return result;
     }
     return JNI_VERSION_1_4;
}

java下调用:
package com.sailor;

public class Pipe {
	static {
		// 加载动态库
		System.loadLibrary("JNIPipe");
	}
	public native int createUnNamePipe();
	public native int sendUNPMessage(String msg);
	public native String readUNPMessage();
	public native void destroyUnNamePipe();
}

public void startPipe(View view){
		pipe = new Pipe();
		pipe.createUnNamePipe();
		Thread thread = new Thread(new Runnable() {

			@Override
			public void run() {
				while(true){
					Log.d("jacklam", "wait read msg");
					Log.d("jacklam", "" + pipe.readUNPMessage());
					pipe.destroyUnNamePipe();
					break;
				}
				
			}
			
		});
		thread.start();
		
		pipe.sendUNPMessage("just test");
	}

当写数据的管道没有关闭,而又没有数据可读时,read调用通常会阻塞,但是当写数据的管道关闭时,read调用将会返回0而不是阻塞。注意,这与读取一个无效的文件描述符不同,read一个无效的文件描述符返回-1。

另因android sdcard为fat32,而mkfifo只是调用用mknod()来创建一个特殊的文件,和fat32不支持,所以不能创建命名管道
int mkfifo(const char *filename, mode_t mode);  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值