frida
windows安装
1 | pip3 install frida frida-tools |
不过frida
安装时总会卡住不动,可以到这里 选择和你的python版本一致的,注意指令集也要和python的一样.然后直接easy_install xxx.egg
Android安装
到这里下载server,主要要和手机的指令集一致的,解压后
1 | adb push server 手机路径 |
应用
python脚本:
1 | import frida |
获取非native方法的输入值和返回值,这里的方法为
String c([]String)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17if (Java.available) {
Java.perform(function () {
var MyApplication = Java.use('com.picacomic.fregata.MyApplication');
console.log(MyApplication);
MyApplication.c.implementation = function (v) {
console.log("param:");
console.log(v);
var ret = this.c(v);
console.log("return value:");
console.log(ret);
return ret;
};
});
} else {
console.log("load java failed");
}获取native方法的输入值和返回值,这里的方法是
String getStringSigFromNative();
1 | if (Java.available) { |
这里的Java_com_picacomic_fregata_MyApplication_getStringSigFromNative
是查so文件得到的方法名,非java源码里面的名称.可以把 .so抠出来,丢到linux里面,然后 nm -D libJniTest.so
,输出大概长这样
其他注意事项
打印String VAL 需要用 Java.vm.getEnv().getStringUtfChars(VAL, null).readCString()
,不然打出来是个地址
redis
简单动态字符串(SDS)
1 | struct __attribute__ ((__packed__)) sdshdr8 { |
不包括头的意思是:redis在新建完sds,并不会直接返回这个结构体,而是返回buf的地址,这样就可以直接使用string.h
里面的方法进行处理,所以下面:
1 | #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) |
这个就是输入T(是sdshdrT),s就是字符串的首地址,这样处理就可以通过字符串的首地址获得整个结构体
ziplist
结构为:
1 |
|
Rust learning note
Option<T>
像C#的nullable,就是使没有支持null(None)的数据类型增加null(None)这个取值,要用Some(x)进行解包
变量所有权
一个变量只能拥有一个可写的引用,但是可以拥有多个只读引用,读与写引用互斥.
栈上变量直接赋值,会直接复制,不会转移所有权.但是堆上变量赋值如果没有使用clone(),则会转移所有权,转移后原来变量名失去所有权,使用的话会在编译阶段报错
函数如果没有传引用的话,会把所有权转移到被调用函数中
生命周期
1 | // 表示a,b和返回值有相同的生命周期 |
静态生命周期
1 | let s: &'static str = "455"; |
trait
很像接口
1 | pub trait Ahh { |
zero copy
概念
由于Linux文件读写是对缓存进行操作的,虽说可以减少对磁盘的读写,但是数据拷贝时会消耗大量CPU资源.零拷贝zero-copy
这种技术可以有效地改善数据传输的性能,在内核驱动程序(比如网络堆栈或者磁盘存储驱动程序)处理 I/O 数据的时候,零拷贝技术可以在某种程度上减少甚至完全避免不必要 CPU 数据拷贝操作。
但是只能用于单纯的文件传输,无法实现文件修改后再传输
正常文件拷贝传输,是由内核拷贝到缓冲区,然后内核再把数据写到用户所给内存,再写到socket缓冲区,最后再交付
分类
- 直接 I/O:对于这种数据传输方式来说,应用程序可以直接访问硬件存储,操作系统内核只是辅助数据传输:这类零拷贝技术针对的是操作系统内核并不需要对数据进行直接处理的情况,数据可以在应用程序地址空间的缓冲区和磁盘之间直接进行传输,完全不需要 Linux 操作系统内核提供的页缓存的支持。
- 在数据传输的过程中,避免数据在操作系统内核地址空间的缓冲区和用户应用程序地址空间的缓冲区之间进行拷贝。有的时候,应用程序在数据进行传输的过程中不需要对数据进行访问,那么,将数据从 Linux 的页缓存拷贝到用户进程的缓冲区中就可以完全避免,传输的数据在页缓存中就可以得到处理。在某些特殊的情况下,这种零拷贝技术可以获得较好的性能。Linux 中提供类似的系统调用主要有
mmap()
,sendfile()
以及splice()
。 - 对数据在 Linux 的页缓存和用户进程的缓冲区之间的传输过程进行优化。该零拷贝技术侧重于灵活地处理数据在用户进程的缓冲区和操作系统的页缓存之间的拷贝操作。这种方法延续了传统的通信方式,但是更加灵活。在 Linux 中,该方法主要利用了写时复制技术
直接I/O
1 | // flag使用O_DIRECT |
针对数据传输不需要经过应用程序地址空间的零拷贝技术
mmap
1 | tmp_buf = mmap(file, len); |
应用程序调用了 mmap() 之后,数据会先通过 DMA 拷贝到操作系统内核的缓冲区中去。接着,应用程序跟操作系统共享这个缓冲区,这样,操作系统内核和应用程序存储空间就不需要再进行任何的数据拷贝操作。应用程序调用了 write() 之后,操作系统内核将数据从原来的内核缓冲区中拷贝到与 socket 相关的内核缓冲区中。接下来,数据从内核 socket 缓冲区拷贝到协议引擎中去,这是第三次数据拷贝操作
当对文件进行了内存映射,然后调用 write() 系统调用,如果此时其他的进程截断了这个文件,那么 write() 系统调用将会被总线错误信号 SIGBUS 中断,因为此时正在执行的是一个错误的存储访问。这个信号将会导致进程被杀死,解决这个问题可以通过以下这两种方法:
- 为 SIGBUS 安装一个新的信号处理器
- 第二种方法是通过文件租借锁来解决这个问题的,这种方法相对来说更好一些。我们可以通过内核对文件加读或者写的租借锁,当另外一个进程尝试对用户正在进行传输的文件进行截断的时候,内核会发送给用户一个实时信号:RT_SIGNAL_LEASE 信号,这个信号会告诉用户内核破坏了用户加在那个文件上的写或者读租借锁,那么 write() 系统调用则会被中断,并且进程会被 SIGBUS 信号杀死,返回值则是中断前写的字节数,errno 也会被设置为 success。文件租借锁需要在对文件进行内存映射之前设置。
sendfile
1 | ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count) |
sendfile() 系统调用利用 DMA 引擎将文件中的数据拷贝到操作系统内核缓冲区中,然后数据被拷贝到与 socket 相关的内核缓冲区中去。接下来,DMA 引擎将数据从内核 socket 缓冲区中拷贝到协议引擎中去。如果在用户调用 sendfile () 系统调用进行数据传输的过程中有其他进程截断了该文件,那么 sendfile () 系统调用会简单地返回给用户应用程序中断前所传输的字节数,errno 会被设置为 success。如果在调用 sendfile() 之前操作系统对文件加上了租借锁,那么 sendfile() 的操作和返回状态将会和 mmap()/write () 一样
sendfile() 系统调用不需要将数据拷贝或者映射到应用程序地址空间中去,所以 sendfile() 只是适用于应用程序地址空间不需要对所访问数据进行处理的情况。相对于 mmap() 方法来说,因为 sendfile 传输的数据没有越过用户应用程序 / 操作系统内核的边界线,所以 sendfile () 也极大地减少了存储管理的开销。
- sendfile() 局限于基于文件服务的网络应用程序
- 由于网络传输具有异步性,很难在 sendfile () 系统调用的接收端进行配对的实现方式,所以数据传输的接收端一般没有用到这种技术
- 基于性能的考虑来说,sendfile () 仍然需要有一次从文件到 socket 缓冲区的 CPU 拷贝操作,这就导致页缓存有可能会被传输的数据所污染
splice
1 | #include <fcntl.h> |
在 Linux 2.6.23 中,sendfile由splice实现
调用 splice() 系统调用会导致操作系统内核从数据源 fdin 移动最多 len 个字节的数据到 fdout 中去,这个数据的移动过程只是经过操作系统内核空间,需要最少的拷贝次数。使用 splice() 系统调用需要这两个文件描述符中的一个必须是用来表示一个管道设备的。不难看出,这种设计具有局限性。
fd_in
参数是待输入描述符。如果它是一个管道文件描述符,则off_in
必须设置为NULL;如果off_in
不是一个管道文件描述符(比如socket),那么off_in
表示从输入数据流的何处开始读取数据,此时若为NULL,则从输入数据流的当前偏移位置读入fd_out/off_out
参数的含义与fd_in/off_in
相同,不过用于输出数据流len参数指定移动数据的长度
参数 flags 用于表示拷贝操作的执行方法,当前的 flags 有如下这些取值:
SPLICE_F_NONBLOCK
:splice 操作不会被阻塞。然而,如果文件描述符没有被设置为不可被阻塞方式的 I/O ,那么调用 splice 有可能仍然被阻塞。SPLICE_F_MORE
:告知操作系统内核下一个 splice 系统调用将会有更多的数据传来。SPLICE_F_MOVE
:如果输出是文件,这个值则会使得操作系统内核尝试从输入管道缓冲区直接将数据读入到输出地址空间,这个数据传输过程没有任何数据拷贝操作发生
1 | //创建管道 |
写时复制 Copy On Write(COW)
如果有多个应用程序需要同时访问同一块数据,那么可以为这些应用程序分配指向这块数据的指针,在每一个应用程序看来,它们都拥有这块数据的一份数据拷贝,当其中一个应用程序需要对自己的这份数据拷贝进行修改的时候,就需要将数据真正地拷贝到该应用程序的地址空间中去,也就是说,该应用程序拥有了一份真正的私有数据拷贝,这样做是为了避免该应用程序对这块数据做的更改被其他应用程序看到。这个过程对于应用程序来说是透明的,如果应用程序永远不会对所访问的这块数据进行任何更改,那么就永远不需要将数据拷贝到应用程序自己的地址空间中去。这也是写时复制的最主要的优点。
写时复制的实现需要 MMU 的支持,MMU 需要知晓进程地址空间中哪些特殊的页面是只读的,当需要往这些页面中写数据的时候,MMU 就会发出一个异常给操作系统内核,操作系统内核就会分配新的物理存储空间,即将被写入数据的页面需要与新的物理存储位置相对应。
参考文章
笔记
Reactor
Reactor 要求主线程或内核负责监听文件描述符是否有事件发生,有的话立刻告知线程.数据读写全部由控制线程负责.同步io
epoll就是这种方式
组件
event demultiplexer
事件多路分发器,如select,poll,epoll
handle
事件源:用于识别每一个事件,Linux上是文件描述符Reactor
反应器,用于管理事件的调度和注册删除event handler
事件处理器:管理已经注册的事件和已经激活等待调度的事件,并分成了不同类型的事件(如读/写、信号),当事件发生时,会提供对应的处理程序。由Reactor
调用。
Proactor
Proactor 所有io操作由主线程或者内核负责,控制线程只负责业务逻辑,异步io
aio
timewait
TCP四次挥手中,主动方在发送第二个ACK后的2MSL(max segment lifetime)时间之内,RFC推荐是2MSL=120,Linux内核2MSL=60s
目的:防止ACK包丢失,等待是否有被动方重发的seq
解决方法:
- 编程时使用
SO_REUSEADDR
- Linux里
1
2
3
4
5
6
7
8# 启动TCP ts,tcp_tw_reuse和.tcp_tw_recycle依赖此实现
net.ipv4.tcp_timestamps=1
# 复用TIME_WAIT的socket
net.ipv4.tcp_tw_reuse=1
# 销毁TIME_WAIT的socket,由于nat可能会导致无法握手
net.ipv4.tcp_tw_recycle=1
# 重设超时
net.ipv4.tcp_fin_timeout=30- 编程时使用
Linux IO模型
- 阻塞IO
- 非阻塞IO
- IO复用
- 信号驱动IO
- 异步IO
僵尸进程解决
两次fork.父进程创建子进程,子进程负责fork孙子进程,孙子进程负责执行逻辑业务,子进程fork完孙子线程后直接exit.孙子进程变成孤儿线程,交由init管理
子进程在结束时会向父线程发送
SIGCHLD
.父进程只要在信号处理函数中wait,进行子进程的PCB进行回收1
2
3
4
5
6
7
8
9
10
11static void sig_child(int signo) {
pid_t pid;
int stat;
//处理僵尸进程
while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
printf("child %d terminated.\n", pid);
}
int main() {
signal(SIGCHLD, sig_child);
......
}忽略请求
1
signal(SIGCLD, SIG_IGN);
一致性hash
哈希值映射到圆环,节点分布在环上,顺时针的第一个节点就是数据存取节点
RVO (return value optimization) 和NRVO (named return value optimization)
Traits
被其他object或algorithm使用的,用来携带信息的object
实现:模板的特化与偏特化
cloudflare
cloudflare 的服务器支持SNI,所以其实服务器委托在cf上的话,他的ip可以指向cf的任意ip地址,也可以正常访问