主要关注三个层面
描述符
数据只能往一个方向流,要么读要么写,
如果需要既能读又能写,则需要给管道有两个描述符
;
不过Linux给了我们一个APIpipe(fds)
,
这个API可以生成一对描述符
,
一个用来写一个用来读;无名管道
是在父子进程
之间使用的;
有名管道
只要两个进程都知道这个管道的名字
就可以通信了;pipe
调用,生成管道的一对描述符;
fd[1]是用来写的; fd[0]是用来读的;
通过fork()
调用创建一个子进程;
子进程会继承这对描述符;关闭
,把子进程写描述符关闭
;
接着,父进程往写描述符
里边写一个字符串
;
然后,子进程就可以从读描述符
里边把这个字符串
给读出来
;
if(pid == 0){
close(fd[1]);//把子进程写描述符关闭
//子进程就可以从读描述符里边把这个字符串给读出来
read(fd[0], buf, SIZE);
}else if(pid > 0){
close(fd[0]);//把子进程写描述符关闭
write(fd[1], "Hello", 5);//父进程往写描述符里边写一个字符串
}
概念图如下,
我们可以看到数据流的方向是
父进程写描述符fd[1]--管道--子进程读描述符fd[0]
,
即,我们刚刚所说的半双工设计
:
Android 4.4
中的MQ机制
中的重要元素Looper
,用到了管道
(更高的版本如Android 6.0
用的就不是管道了):
配置一对读写描述符
;
后半部分是注册一个监听事件
,
监听读描述符的读事件
,即eventItem.data.fd = mWakeReadPipeFd;
这个时候如果有另一个线程拿到写描述符
并往里面写东西的话,
读端就能收到通知了; 相关阅读
epoll机制有一套函数,共三个,如下
创建epoll句柄:
1. int epfd =epoll_create(intsize);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
将被监听的描述符(对应例程中mWakeReadPipeFd
)
添加
到epoll句柄
(对应例程中mEpollFd
)
或从epool句柄中删除
或者对监听事件进行修改
(添加、删除和修改通过op
位参数进行控制)
3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
注意最后一句话, 该函数返回需要处理的事件数目,即几个事件被触发了, 第二个参数events列表用来接收存入触发的事件;
epoll_wait
得到触发的事件列表及其数量;触发事件列表
,
遍历到事件的fd是刚刚设置的读描述符的(mWakeReadPipeFd
),
及其事件是刚刚设定的读事件的(EPOLLIN
),
则调用awoken()
,把管道中的东西读出来,
管道满了就写不进去了,所以二话不说先读出来;小结 管道使用起来还是比较方便的, 它可以跟epoll相结合监听读写事件; 管道在进程自身中可以用, 跨进程也可以用; 在数据量不怎么大的跨进程通信的时候还是比较有用的;
registerZygoteSocket()
创建了一个本地的Socket,
socketName通过argv接收Socket的名字,
接着进入runSelectLoop()
,一个循环,
检测这个socket有没有新过来的连接或者是数据;runSelectLoop()
:
poll()
用来监测有没有我们关注的事件发生,
如果有的话,可能会有两种情况,
第一种,是可能会有新的连接
;
第二种,就是有新的数据
发过来,
这时候可以调用runOnce()
来处理数据(先从Socket中
把参数读
出来,
根据参数去执行响应的指令,主要是创建应用进程,
应用进程启动之后,通过Socket
把pid写
给对方)
所以我们可以看到Socket其实很方便,
它即可以读又可以写;跨进程
传递;图像相关的传输
;MemoryFile
为例,
这是Android的一个工具类,
封装了内存共享机制Ashmem,也就是匿名共享内存;MemoryFile是android在最开始就引入的一套框架,其内部实际上是封装了android特有的内存共享机制Ashmem匿名共享内存,简单来说,Ashmem在Android内核中是被注册成一个特殊的字符设备,Ashmem驱动通过在内核的一个自定义slab缓冲区中初始化一段内存区域,然后通过mmap把申请的内存映射到用户的进程空间中(通过tmpfs),这样子就可以在用户进程中使用这里申请的内存了。
native_open方法会返回一个描述符(句柄);
把描述符mFD映射到当前进程的内存空间, 内存空间的地址则返回过来,即例程中的mAddress;
读函数
就是把数据
从共享内存
读
到应用层的buff
中,
SetByteArrayRegion()
就是把native
的buff数据
拷到java数据流的;写函数
则与读函数
相反,
就是把数据
从 应用层的buff
拷到共享内存
中
GetByteArrayRegion()
就是把java数据流中的数据
拷到native
的buff
的;Userid
相同,
本进程才能发信号;SIGNAL_KILL
信号,
pid参数位就是要杀掉的进程pid;
当然不是想杀就能杀,同样这里是有权限控制
的,
比如说本进程跟另一进程的Userid
相同,
本进程才能发信号,杀掉另一个进程;《开发艺术探索》中有一段类似的描述
不可以随便给别的进程发信号
的;zygote翻译成中文是受精卵的意思,名字比较奇怪、但是很有意思, zygote在android中主要有两个作用: 建立运行时环境并启动虚拟机,为应用程序创建DVM进程。 执行com.android.internal.os.ZygoteInit的main函数, 从而fork SystemService。 类似参考文章