android Framework 中用到了哪些跨进程通信方式?

Linux 有哪些跨进程的通信方式?

  • Binder 机制是Android基于Linux的一种独特的IPC机制。我们常用的AMS,PMS 等都是通过Binder机制来完成跨进程通信的,那么除了Binder ,Linux 还有其他跨进程通信的方式可以选择。在Android Framework中主要用到以下方式

      1. 管道
      2. Socket
      3. 共享内存
      4. 信号
    

管道

  • 管道的特点:半双工的,单向

    管道描述符数据只能往一个方向,要么read要么write。如果向既可以读又可以写,则需要两个描述符才可以。Linux 基于这种情况提供了 pipe(fds) api,这个api可以生成一对描述符,一个用来写,一个用来读。

  • 一般是在父子进程之间使用

    无名管道一般是在父子进程之间使用。如果是有名管道,只要两个进程之间都知道名字就可以直接通信了。

  • 管道的使用方式
    在这里插入图片描述

#include<stdio.h>
#include<unistd.h>
 
 
int main()
{
int n,fd[2];                         // 这里的fd是文件描述符的数组,用于创建管道做准备的
pid_t pid;
char line[100];
if(pipe(fd)<0)                     //   创建管道,生成描述符 fd[1] 是用来写的 fd[0] 是用来读的
   printf("pipe create error/n");
 
if((pid=fork())<0)              //利用fork()创建新进程
    printf("fork error/n");
 
else if(pid>0){                   //这里是父进程,先关闭管道的读出端,然后在管道的写端写入“hello world"
    close(fd[0]);
    write(fd[1],"hello word/n",11);
}
else{
    close(fd[1]);                 //这里是子进程,先关闭管道的写入端,然后在管道的读出端读出数据
   n= read(fd[0],line,100);
    write(STDOUT_FILENO,line,n);
}
exit(0);
}
  • Framework 中 在Android 4.4 中 Looper 中使用到了管道,高版本更换了 eventfd 的方式。当有线程拿到写的描述符,往里写内容,那么读端就可以收到通知了。
Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    int wakeFds[2];
    // 通过pipe 生成两个描述符
    int result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
	// 0 元素对应的是 读
    mWakeReadPipeFd = wakeFds[0];
    // 1 对应的是写 
    mWakeWritePipeFd = wakeFds[1];
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    mIdling = false;
    // Allocate the epoll instance and register the wake pipe.
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeReadPipeFd;
    // 通过 epoll_ctl 注册事件监听
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
}
  • epoll 是怎么监听读端事件的
int Looper::pollInner(int timeoutMillis) {
	// 。。。
	struct epoll_event eventItems[EPOLL_MAX_EVENTS];
	// epoll_wait 阻塞在这,当返回的时候 eventCount 代表有几个事件被触发了
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
	// 然后依次处理
	
	   for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        // 判断描述符 mWakeReadPipeFd ,如果是读描述符
        if (fd == mWakeReadPipeFd) {
            if (epollEvents & EPOLLIN) {
            	// 从管道中读取数据
                awoken();
            } 
        } else {
            // ... 
        }
    }
}
  • 往管道中写数据,通过 Looper 的 wake() 函数写
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
#endif

    ssize_t nWrite;
    do {
    	// 通过写描述符写
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite != 1) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

管道在进程内可以用,跨进程也可以用,可以和 epoll 相结合监听读写事件,一般用在数据量不大的跨进程通信中使用。

本地 Socket

  • Socket 特点

      全双工,既可以读也可以写
      两个进程之间可以无亲缘关系
    
  • Android Framework 中在 Zygote 中,通过 Socket 来接收 AMS 请求,启动应用进程。在 ZygoteInit 的入口函数中

public static void main(String argv[]) {
            // 注册 Zygote 的 socket 监听接口,用来接收启动应用程序的消息
            zygoteServer.registerServerSocketFromEnv(socketName);
            // 通过调用 runSelectLoop 进入监听和接收消息的环节 里面有一个 while (true) 
            caller = zygoteServer.runSelectLoop(abiList);
}
  • Runnable runSelectLoop(String abiList)  处理和返回Socket数据
    
Runnable runSelectLoop(String abiList) {

	while (true) {
            StructPollfd[] pollFds = new StructPollfd[fds.size()];
            // 。。。
            try {
            //	 用来检测有没有事件发生
                Os.poll(pollFds, -1);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
			if (i == 0) {
                    // 处理新来的连接
           } else {
					// 处理发过来的数据
			}
	}

}

共享内存

  • 共享内存的特点

      1. 很快,不需要多次拷贝。上面提到的管道和Socket数据量太大会很糟,因为会拷贝两次数据。
      共享内存拿到文件描述符后,把它同时交给两个进程,就可以进行通信了。
      2. 进程之间也不用存在亲缘关系
    
  • 具体匿名共享内存看之前的文章

  1. 匿名共享内存 ashmem
  2. 跨进程通信–共享内存(ashmem)实例

信号

  • 特点

      1. 单向发送:不关心发出去之后的事
      2. 只能带一个信号,不能带别的参数
      3. 知道进程 pid 就可以发信号了,而且可以群发信号
    
  • 哪里用到了信号?看下面的代码,大多数人都见过,一般我们安装或者重启应用的时候可能先kill掉自己。

android.os.Process.killProcess(android.os.Process.myPid())

killProcess 中就发送了一个信号

	public static final native void sendSignal(int pid, int signal);
	
    public static final void killProcess(int pid) {
        sendSignal(pid, SIGNAL_KILL);
    }
static void SetSigChldHandler() {
  struct sigaction sa;
  memset(&sa, 0, sizeof(sa)); //对sa地址内容进行清零操作
  sa.sa_handler = SigChldHandler;
  // zygote 关注的SIGCHLD信号,如果进程杀死了好及时回收资源
  int err = sigaction(SIGCHLD, &sa, NULL);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
千里马8年Android系统及应用开发经验,曾担任过美国unokiwi公司移动端技术总监兼架构师,对系统开发,性能优化,应用高级开发有深入的研究,Android开源定制ROM Lineage的贡献者之一,国内首家线下开辟培训Android Framework课程,拥有2年的Android系统培训经验。成为腾讯课堂专业负责android framework课程分享第一人,致力于提高国内android Framework水平Android Framework领域内是国内各大手机终端科技公司需要的人才,应用开发者都对Android系统充满着好奇,其的binder是重之重,都说无binder无Android,binde是Android系统的任督二脉。课程水平循序渐进,由级再到高级,满足各个层次水平的android开发者。1、灵活使用binder进程通信,在app端对它的任何api方法等使用自如2、可以单独分析android系统源码任何binder部分,分析再也没有难度3、掌握binder驱动本质原理,及对应binder驱动怎么进行进程通信,及内存等拷贝方式数据等4、对binder从上层的java app端一直到最底层的内核binder驱动,都可以顺利理通5、针对系统开发过程遇到的binder报错等分析方法,及binder bug案例学习6、针对面试官任何的binder问题都可以对答自如7、socket这种进程通信实战使用8、针对android源码使用的socket源码轻松掌握9、android系统源码最常见的socketpair双向进程通信10、使用socket实现一个可以让app执行shell命令的程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值