最近阅读了Redis6.2源码,添加了一些注释,感兴趣的同学可以看下。
https://github.com/neohope/NeoRedisSrc
微服务性能调优
近期遇到一些技术问题,记录如下:
1、kafka并发处理量上不去
数据流:
数据生产服务-》kafka-》数据消费服务
表现:
数据消费服务加了很多个实例,但总感觉多数实例不工作
原因:
分析后发现,原来开发小伙伴把所有消息都扔到了同一个topic中,而分区只有3,消费者再多这并发量也上不去啊
解决:
按不同业务,拆分topic,同时增加分区数
同时,建议把一堆操作拆分为多个步骤进行,不要都放到一个方法里全部做掉,宁可多流转几次各司其职
2、数据上报并发处理量上不去
数据流:
DB-》轮询-》HTTP提交数据到数据上报网站【ZF】
表现:
要求4小时上传60W,实际1小时上传5K
原因:
问题很多,主要有两个,一是每次只上传一条数据,二是没有做并发
解决:
一开始准备做很大的调整,但后面项目组怕把开并发把数据上报网站压挂,最后没敢使用
最后,只是改造为批量上传数据,轮询时根据id做了一下简单的并发
数据上报网站,各地接口及要求各不一样,也是各种不容易吧
3、莫名奇妙的403
数据流:
浏览器-》网关-》鉴权服务
表现:
网关偶尔返回403
原因:
一开始日志输出太少,都没有定位到哪里的问题,只能临时在网关补充了一些日志【建议加了开关,定位问题后关闭】
加日志后,发现是一段上古代码,使用信号量进行了并发控制,超出了并发就获取不到用户权限。
而这个Bug暴漏出来,据反馈,居然是升级组件导致的。
遗留系统都是坑啊。
解决:
优化鉴权服务,定位到问题,问题也就解决了
4、无法提升的微服务性能
数据流:
浏览器-》网关-》N个服务来回调用
表现:
服务性能差,有时要几十秒才返回,一堆告警邮件
原因:
微服务拆分粒度太细,形成环状调用链路
开发同学无脑调用微服务,能调用一次的,居然会循环调用N次
解决:
重构,按业务领域合并微服务,微服务数量少了60%,同时干掉环状调用链
评审,找到不合理的循环调用,抓典型,整改
5、批量导出
数据流:
浏览器-》网关-》导出服务-》DB
表现:
批量查询、批量导出性能很差,要按分钟返回的,一堆告警邮件
原因:
数据量太大,DB拉取速度慢,数据回传也慢
解决:
根据业务情况,部分批量查询功能改到只读库查询,大批量导出功能迁移到了数据中台
6、数据批量下发
数据流:
数据中台-》kafka-》数据下发接收服务
表现:
数据中台下发数据,无法更新到下游业务系统
原因:
因安全需求,上游业务系统批量刷新数据库,导致数据中台下发大量数据,数据下发服务处理不及时,积累了大量数据待处理
下游业务系统把一堆业务逻辑放到了接收服务中,处理单条业务数据要按秒计算,数据越积越多
你没想错,kafka并发上不去,和之前原因一样一样的
解决:
接收服务简化,拆分不必要的业务逻辑,服务性能提高了几百倍
优化kafka配置
制定了批量刷新数据库的相关流程,都是泪
历史数据咋处理?和上游系统沟通后,99.9%的数据不必处理,于是忽略了历史积累数据,晚上将0.1%的数据进行了重推,解决了问题
7、Kafka卡顿
数据流:
系统A-》kafka-》系统B
表现:
kafka在半夜性能下降明显,从日志上看,就是工作2分钟,卡5分钟
原因:
kafka的消费者每次取了太多消息去消费,半夜为业务高峰期,部分消费者获取消息无法及时处理完毕,导致kafka会出发rebalance,你懂的
解决:
每次少取一些数据
8、HTTPS通讯失败
数据流:
前置系统A-》云-》云上系统B
表现:
有两家用户的数据无法连通云服务,HTTPS通讯失败
原因:
前置服务的服务器器时间错误,数据包被云的安全策略直接丢弃,被当成重放攻击了
解决:
开启前置服务自动时间同步服务
9、组件升级导致服务性能下降
表现:
系统组件升级后,系统整体服务性能下降,会有部分请求阻塞5S以上
CPU、内存、IO看起来都比较正常
生产环境才有问题,测试环境下无法复现
原因:
通过打印线程信息,发现存在大量网络IO阻塞
后定位到了一个可疑的点,日志同时输出到log文件和命令行时,命令行也发送到了log文件,会有阻塞
解决:
只输出到命令行,有待观察
整体经验:
1、微服务粒度不能太细,也不能一个服务啥都干,最好按业务领域进行拆分。业务量小的领域可以适当合并,业务逻辑复杂的考虑拆分。
2、微服务也应该分层次,不要出现环状调用链路
3、日志要足够判断问题所在,太多影响性能,太少没啥用
4、中间件不能熟练配置,不要随便上生产
5、批量操作要用特殊处理方式,没事别刷库
一次数据库字段加密升级记录
今年个保法发布了,根据安全团队要求,需要对数据库的敏感字段进行加密处理。
当前数据库连接已经加密了,只需要对数据加密。有两种代价较小的方式:
1、在数据库存储引擎层面加密,这样对全部业务系统是无感的。
2、退而求其次,可以在数据库中间件或在驱动层面做加密,这样全部业务系统修改量也是比较小的。
但由于种种原因,这两种方式都没有能推行。
最后采用的方式是,安全团队提供加解密SDK,各业务系统对接。
业务系统启动,通过加解密SDK从密钥分发服务器获取密钥-》写入数据库前加密、读取数据后解密
加密算法都是国密算法,包括对称加密及摘要算法。
这样全部业务系统都需要改造,而且批量操作效率也比较低。
优点是,即使别人攻破数据库,也拿不到明文数据。
改造过程也很痛苦:
1、注册APP ID,申请密钥
2、研发刷库工具
3、在数据库表中增加加密字段,通过刷库工具,将历史敏感数据进行加密,存放到加密字段
4、基于加解密SDK,研发适用于个团队的切面SDK
5、使用切面SDK,对服务进行改造,读明文,写明文+密文(可跳过)
6、使用切面SDK,对服务进行改造,读密文,写明文+密文(可跳过)
7、使用切面SDK,对服务进行改造,读密文,写密文(可跳过)
8、删除明文字段
整个过程十分痛苦。
稳定性保障:
整个密钥下发服务器,是全局的一个大故障点,必须保障可用性。
密钥分发服务器,用到了密码机,提供了同城容灾及异地容灾环境。
支持DC单独部署,也支持云访问。
对应用系统影响:
1、改造量大,且无直接业务收益,资源投入受限
2、性能下降,尤其是批量加解密时,性能下降较多
3、模糊查询受影响较大,只能通过提前计算的摘要信息进行查询了
4、遗留系统、外购系统,改造难度太大,成本太高,无法承受
对数据中台的冲击:
所有依赖于数据库日志的系统,都会受到影响,尤其是数据中台
有两种方式,一是不使用,二是用同样的key
一次技术中台升级记录
由于历史原因,我们有两套技术架构:
基于SpringBoot1.X的老框架、老技术中台、老数据中台。
基于SpringBoot2.X的新框架、新技术中台、新数据中台。
老技术中台研发的时候,整个生产环境都是我们自己搭建的,很多功能需要自研。
到了新技术中台时,已经迁移到了集团的云上,之前设计的很多功能也就用不到了。
而且,随着时间的推移,老中台人员都去参与了其他项目,长期处于支持维护状态,无论研发、运维还是安全同事,都感到压力山大。
由于云端环境时不时会做调整,运维同学都不敢轻易重启老中台服务,怕一重启,就再也起不来了。
安全扫描策略也不断升级,扫描出来的问题一次比一次多,很快就千疮百孔了,但大家也只能缝缝补补。
之前由于业务压力异常巨大,大家一直在忙着做各种新的业务功能,虽然一直想升级,但一直没有狠下心来。
到了今年四季度,遇到了几次生产事故后,大家痛定思痛,决定还是干吧,把老中台下掉。
整体步骤大体如下:
1、找了两位对老技术中台比较熟悉的同事,对全部服务进行了梳理
2、各技术团队,也对老中台的依赖关系重新进行了梳理
3、大家坐下来,对服务进行了分类
A、对于网关类服务,按领域拆分
B、对于调用中转类服务,已经不符合当前要求,改为K8S的微服务直连
C、先看调用量小的服务;没有调用量的服务及接口,尽早下线
D、其余调用量很少的服务,与业务方沟通,能下线下线,不能下线合并等待下线
E、新老技术中台都有的服务,新中台进行适配,下线老中台服务
F、老中台有,新中台没有的中台类服务,功能迁移,下线老中台服务
G、非中台类服务,拆分到业务系统,下线老中台服务
H、其余服务,一事一议
4、对新老平台都保持的数据,进行数据治理,统一数据标准
5、在服务迁移改造的过程中,要求按新框架规范,将服务升级到SpringBoot2
6、由于没有业务上的直接收益,所以整个改造周期排了很久,各团队根据实际情况,在每次迭代中把工作量消化掉
7、及时与测试同学沟通,确保每次测试覆盖到
8、与运维同学沟通,服务全部升级完成后,切换nacos到最新版本(之前受限于SpringBoot1,无法升级nacos)
9、这样框架、技术中台就升级完毕了,后面每半年升级一次框架就可以了
10、数据中台的小伙伴,因形势所迫,直接抛弃了原有技术栈和工具,拥抱了新技术栈,最后技术债务居然是最小的。
任务已排期,各团队都表示坚决支持。但你以为这样就能顺利推进吗?我感觉比较难哦。
过半年,再续写一下,推进情况究竟如何,敬请期待。
Icarus Verilog环境搭建
1、下载Icarus Verilog
Icarus Verilog:
http://iverilog.icarus.com/home
Icarus Verilog for Windows:
http://bleyer.org/icarus/
vscode插件
Verilog-HDL/SystemVerilog/Bluespec SystemVerilog
2、下载示例源码
极客时间 操作系统实战45讲 Icarus Verilog示例源码
3、编译
//iverilog路径,要加入到环境变量中,让vscode可以访问 iverilog -o my_alu alu_test.v alu.v vvp my_alu gtkwave -F my_alu.vcd
4、在gtkwave中能查看波形咯
Linux虚拟化管理
1、内核模块初始化
module_init(vmx_init)->kvm_init module_init(svm_init)->kvm_init //其中,kvm_init ->kvm_arch_init ->kvm_irqfd_init ->kvm_arch_hardware_setup ->misc_register(&kvm_dev)
2、从数据结构角度,又可以看到了设备皆为文件的思想
static struct miscdevice kvm_dev = { KVM_MINOR, "kvm", &kvm_chardev_ops, }; static struct file_operations kvm_chardev_ops = { .unlocked_ioctl = kvm_dev_ioctl, .llseek = noop_llseek, KVM_COMPAT(kvm_dev_ioctl), }; //初始化时,通过misc_register,实现了操作的绑定。
3、通过上面的数据结构,我们就可以找到创建虚拟机的方法,并生成控制文件
kvm_dev.kvm_chardev_ops.kvm_dev_ioctl
或者,ioctl系统调用KVM_CREATE_VM,效果也是一样的:
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) ->vfs_ioctl,会用到vfs_ioctl.unlocked_ioctl也就是kvm_dev_ioctl ->case KVM_CREATE_VM: -> r = kvm_dev_ioctl_create_vm(arg); ->file = anon_inode_getfile("kvm-vm", &kvm_vm_fops, kvm, O_RDWR); //其中,kvm_dev_ioctl_create_vm ->kvm_create_vm ->->kvm_arch_init_vm ->->hardware_enable_all ->->kvm_arch_post_init_vm ->->list_add(&kvm->vm_list, &vm_list);
4、生成虚拟CPU套路很相似,仍是文件操作
static struct file_operations kvm_vm_fops = { .release = kvm_vm_release, .unlocked_ioctl = kvm_vm_ioctl, .llseek = noop_llseek, KVM_COMPAT(kvm_vm_compat_ioctl), }; //创建虚拟机时,通过anon_inode_getfile,实际上就把文件和kvm_vm_fops绑定了起来。 anon_inode_getfile("kvm-vm", &kvm_vm_fops, kvm, O_RDWR)
5、在调用ioctl时
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) ->vfs_ioctl,会用到vfs_ioctl.unlocked_ioctl也就是kvm_vm_ioctl kvm_vm_ioctl->kvm_vm_ioctl_create_vcpu ->kvm_arch_vcpu_precreate ->kvm_vcpu_init ->kvm_arch_vcpu_create ->kvm_get_kvm ->create_vcpu_fd,生成设备文件inode ->kvm_arch_vcpu_postcreate //其中,kvm_arch_vcpu_create ->kvm_mmu_create ->vcpu->arch.user_fpu = kmem_cache_zalloc(x86_fpu_cache, GFP_KERNEL_ACCOUNT); ->kvm_pmu_init(vcpu); ->kvm_hv_vcpu_init(vcpu); ->kvm_x86_ops.vcpu_create(vcpu); ->kvm_vcpu_mtrr_init(vcpu); ->vcpu_load(vcpu); ->kvm_vcpu_reset(vcpu, false); ->kvm_init_mmu(vcpu, false); //包括init_kvm_tdp_mmu和init_kvm_softmmu两种虚拟化方式
6、启动虚拟机,还是文件操作
static struct file_operations kvm_vcpu_fops = { .release = kvm_vcpu_release, .unlocked_ioctl = kvm_vcpu_ioctl, .mmap = kvm_vcpu_mmap, .llseek = noop_llseek, KVM_COMPAT(kvm_vcpu_compat_ioctl), };
7、在调用ioctl时KVM_RUN
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) ->vfs_ioctl,会用到vfs_ioctl.unlocked_ioctl也就是kvm_vcpu_ioctl kvm_vcpu_ioctl-> case KVM_RUN: kvm_arch_vcpu_ioctl_run //其中, kvm_arch_vcpu_ioctl_run->vcpu_run->vcpu_enter_guest
8、IO同样有虚拟化和半虚拟化两种
一个处理函数为kvm_fast_pio,另一个为kvm_emulate_instruction
Linux系统调用03
Linux系统调用的整体流程为:
1、应用程序【用户态】通过syscall或glibc进行内核功能调用,这一部分在glibc源码中进行的
2、CPU收到syscall,Linux内核响应syscall调用【内核态】,这一部分在linux源码中进行的
3、返回结果到应用程序【用户态】
本节,给Linux系统,增加一个新系统调用功能,获取cpu数量。
1、新建一个源码编译目录
mkdir kernelbuild
2、下载源码,解压
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.10.59.tar.gz tar -xzf linux-5.10.59.tar.gz cd linux-5.10.59
3、清理
make mrproper
4、修改文件
4.1、arch/x86/entry/syscalls/syscall_64.tbl #在440后面增加一行 441 common get_cpus sys_get_cpus 4.2、include/linux/syscalls.h #在最后一个asmlinkage增加一行 asmlinkage long sys_get_cpus(void); 4.3、kernel/sys.c #在最后一个SYSCALL_DEFINE0后面增加下面几行 //获取系统中有多少CPU SYSCALL_DEFINE0(get_cpus) { return num_present_cpus(); }
5、内核配置
make menuconfig make oldconfig
6、修改.config,去掉一个证书
CONFIG_SYSTEM_TRUSTED_KEYS=“”
7、编译
make -j4
8、安装
sudo make modules_install sudo make install
9、测试
9.1、新建文件cpus.c
#include <stdio.h> #include <unistd.h> #include <sys/syscall.h> int main(int argc, char const *argv[]) { //syscall就是根据系统调用号调用相应的系统调用 long cpus = syscall(441); printf("cpu num is:%d\n", cpus);//输出结果 return 0; }
9.2、编译
gcc main.c -o cpus
9.3、运行
./cpus 在没有修改的内核上返回是-1 在修改过的为num_present_cpus数量
Linux系统调用02
Linux系统调用的整体流程为:
1、应用程序【用户态】通过syscall或glibc进行内核功能调用,这一部分在glibc源码中进行的
2、CPU收到syscall,Linux内核响应syscall调用【内核态】,这一部分在linux源码中进行的
3、返回结果到应用程序【用户态】
本节处理第二部分:
二、linux内核部分
1、在make时,会通过syscall_64.tbl生成syscalls_64.h,然后包含到syscall_64.c,进行调用号与函数之间的绑定。 arch/x86/entry/syscalls/syscall_64.tbl arch/x86/include/generated/asm/syscalls_64.h arch/x86/entry/syscall_64.c
1.1、以sys_openat为例,在syscall_64.tbl中为
257 common openat sys_openat 441 common get_cpus sys_get_cpus
1.2、make后,在生成的syscalls_64.h中为
__SYSCALL_COMMON(257, sys_openat)
1.3 在syscall_64.c中,展开__SYSCALL_COMMON
#define __SYSCALL_COMMON(nr, sym) __SYSCALL_64(nr, sym) //展开就是 __SYSCALL_64(257, sys_openat)
1.4、在syscall_64.c中,第一次展开__SYSCALL_64
#define __SYSCALL_64(nr, sym) extern long __x64_##sym(const struct pt_regs *); #include <asm/syscalls_64.h> #undef __SYSCALL_64 //展开就是 extern long __x64_sys_openat(const struct pt_regs *); //也就是每个__SYSCALL_64都展开成了一个外部函数
1.5、在syscall_64.c中,第二次展开__SYSCALL_64
#define __SYSCALL_64(nr, sym) [nr] = __x64_##sym, asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = { [0 ... __NR_syscall_max] = &__x64_sys_ni_syscall, #include <asm/syscalls_64.h> }; //展开其实就是指向了外部函数 [257]=__x64_sys_openat, //全部展开结果,都会被包含到sys_call_table中,从而完成了调用号与函数之间的绑定。
2、当产生系统调用时
2.1、应用直接syscall或通过glibc产生了syscall
2.2、cpu会产生类似于中断的效果,开始到entry_SYSCALL_64执行
//文件路径arch/x86/entry/entry_64.S SYM_CODE_START(entry_SYSCALL_64) //省略代码 call do_syscall_64 SYM_CODE_END(entry_SYSCALL_64) //文件路径arch/x86/entry/entry_64.S,32位兼容模式,过程与64位类似 SYM_CODE_START(entry_SYSCALL_compat) call do_fast_syscall_32 SYM_CODE_END(entry_SYSCALL_compat)
2.3、调用do_syscall_64
#ifdef CONFIG_X86_64 __visible noinstr void do_syscall_64(unsigned long nr, struct pt_regs *regs) { nr = syscall_enter_from_user_mode(regs, nr); instrumentation_begin(); if (likely(nr < NR_syscalls)) { nr = array_index_nospec(nr, NR_syscalls); regs->ax = sys_call_table[nr](regs); } instrumentation_end(); syscall_exit_to_user_mode(regs); } #endif
2.4、根据sys_call_table调用对应的功能函数
sys_call_table[nr](regs) 如果我们传入257,就会调用__x64_sys_openat 如果我们传入441,就会调用__x64_sys_get_cpus
2.5、但咱们实际写的函数sys_get_cpus,好像和实际调用函数__x64_sys_get_cpus,差了一个__x64,这需要一个wrapper
arch\x86\include\asm\syscall_wrapper.h #define SYSCALL_DEFINE0(sname) \ SYSCALL_METADATA(_##sname, 0); \ static long __do_sys_##sname(const struct pt_regs *__unused); \ __X64_SYS_STUB0(sname) \ __IA32_SYS_STUB0(sname) \ static long __do_sys_##sname(const struct pt_regs *__unused) #define __X64_SYS_STUB0(name) \ __SYS_STUB0(x64, sys_##name) #define __SYS_STUB0(abi, name) \ long __##abi##_##name(const struct pt_regs *regs); \ ALLOW_ERROR_INJECTION(__##abi##_##name, ERRNO); \ long __##abi##_##name(const struct pt_regs *regs) \ __alias(__do_##name); SYSCALL_DEFINE0(get_cpus),会展开成为 __X64_SYS_STUB0(get_cpus) //然后 __SYS_STUB0(x64, sys_get_cpus) //然后 long __x64_sys_get_cpus(const struct pt_regs *regs);
这样前后就对上了,glibc和linux内核就通了。
Linux系统调用01
Linux系统调用的整体流程为:
1、应用程序【用户态】通过syscall或glibc进行内核功能调用,这一部分在glibc源码中进行的
2、CPU收到syscall,Linux内核响应syscall调用【内核态】,这一部分在linux源码中进行的
3、返回结果到应用程序【用户态】
本节先处理第一部分:
一、glibc部分
1、应用程序调用open函数
//glibc/intl/loadmsgcat.c # define open(name, flags) __open_nocancel (name, flags)
2、展开后实际上调用了
__open_nocancel(name, flags)
3、而__open_nocancel 最终调用了INLINE_SYSCALL_CALL
//glibc/sysdeps/unix/sysv/linux/open_nocancel.c __open_nocancel(name, flags) ->return INLINE_SYSCALL_CALL (openat, AT_FDCWD, file, oflag, mode);
4、宏展开【理解就好,不保证顺序】
4.1、初始为
INLINE_SYSCALL_CALL (openat, AT_FDCWD, file, oflag, mode); 4.2、第1次展开INLINE_SYSCALL_CALL #define INLINE_SYSCALL_CALL(...) \ __INLINE_SYSCALL_DISP (__INLINE_SYSCALL, __VA_ARGS__) //展开得到: __INLINE_SYSCALL_DISP(__INLINE_SYSCALL, __VA_ARGS__【openat, AT_FDCWD, file, oflag, mode】) 4.3、第2次展开__INLINE_SYSCALL_DISP #define __INLINE_SYSCALL_DISP(b,...) \ __SYSCALL_CONCAT (b,__INLINE_SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__) //展开得到: __SYSCALL_CONCAT(b【__INLINE_SYSCALL】,__INLINE_SYSCALL_NARGS(__VA_ARGS__【openat, AT_FDCWD, file, oflag, mode】))(__VA_ARGS__【openat, AT_FDCWD, file, oflag, mode】)
4.4、第3次展开__INLINE_SYSCALL_NARGS
__INLINE_SYSCALL_NARGS(__VA_ARGS__【openat, AT_FDCWD, file, oflag, mode】) #define __INLINE_SYSCALL_NARGS(...) \ __INLINE_SYSCALL_NARGS_X (__VA_ARGS__,7,6,5,4,3,2,1,0,) //展开得到: __INLINE_SYSCALL_NARGS_X(openat, AT_FDCWD, file, oflag, mode,7,6,5,4,3,2,1,0,) //然后展开__INLINE_SYSCALL_NARGS_X #define __INLINE_SYSCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n //展开得到参数个数: 4 //从而4.4的结果为 __SYSCALL_CONCAT(__INLINE_SYSCALL,4)(__VA_ARGS__【openat, AT_FDCWD, file, oflag, mode】)
4.5、然后展开__SYSCALL_CONCAT,其实就是字符拼接
__SYSCALL_CONCAT(__INLINE_SYSCALL,4) #define __SYSCALL_CONCAT_X(a,b) a##b #define __SYSCALL_CONCAT(a,b) __SYSCALL_CONCAT_X (a, b) //展开得到: __INLINE_SYSCALL4 //从而4.5的结果为 __INLINE_SYSCALL4(openat, AT_FDCWD, file, oflag, mode)
4.6、然后展开INTERNAL_SYSCALL4
#define __INLINE_SYSCALL4(name, a1, a2, a3, a4) \ INLINE_SYSCALL (name, 4, a1, a2, a3, a4) //展开得到: INLINE_SYSCALL(openat, 4, AT_FDCWD, file, oflag, mode)
4.7、展开INLINE_SYSCALL
//glibc/sysdeps/unix/sysv/linux/sysdep.h #define INLINE_SYSCALL(name, nr, args...) \ ({ \ long int sc_ret = INTERNAL_SYSCALL (name, nr, args); \ __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (sc_ret)) \ ? SYSCALL_ERROR_LABEL (INTERNAL_SYSCALL_ERRNO (sc_ret)) \ : sc_ret; \ }) //展开得到 INTERNAL_SYSCALL (openat, 4, args【AT_FDCWD, file, oflag, mode】);
4.8、展开INTERNAL_SYSCALL
#define INTERNAL_SYSCALL(name, nr, args...) \ internal_syscall##nr (SYS_ify (name), args) //展开得到 internal_syscall4(SYS_ify(openat), args【AT_FDCWD, file, oflag, mode】) //展开 SYS_ify(openat) #define SYS_ify(syscall_name) __NR_##syscall_name //得到 __NR_openat //从而得到 internal_syscall4(__NR_openat, args【AT_FDCWD, file, oflag, mode】)
4.9、最后internal_syscall4中,汇编调用了syscall
glibc\sysdeps\unix\sysv\linux\x86_64\64\arch-syscall.h #define __NR_openat 257
最终,syscall时,先传入调用号257,然后是四个真正的参数。
Mininet+ONOS网络模拟环境搭建
1、下载mininet虚拟机:
http://mininet.org/download/#option-1-mininet-vm-installation-easy-recommended
2、导入并运行虚拟机
用户名/密码:mininet/mininet
3、安装docker
sudo apt-get update sudo apt install curl ssh curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
4、拉取onos镜像
sudo docker pull onosproject/onos
5、运行ones容器
#运行 sudo docker run -t -d -p 8181:8181 --name onos1 onosproject/onos #查看容器运行情况 sudo docker ps
6、连接容器,启用服务
#查看ip地址 sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}' onos1 #ssh连接到容器 ssh -p 8101 karaf@172.17.0.2 #启用OpenFlow Provider Suite app activate org.onosproject.openflow #启用 Reactive Forwarding app activate org.onosproject.fwd #退出 ctrl+d
7、创建网络
#创建临时网络 sudo mn --topo tree,2 --controller remote,ip=172.17.0.2 --switch=ovsk,protocols=OpenFlow13 *** Creating network *** Adding controller Connecting to remote controller at 172.17.0.2:6653 *** Adding hosts: h1 h2 h3 h4 *** Adding switches: s1 s2 s3 *** Adding links: (s1, s2) (s1, s3) (s2, h1) (s2, h2) (s3, h3) (s3, h4) *** Configuring hosts h1 h2 h3 h4 *** Starting controller c0 *** Starting 3 switches s1 s2 s3 ... *** Starting CLI: mininet> nodes available nodes are: c0 h1 h2 h3 h4 s1 s2 s3 mininet> links s1-eth1<->s2-eth3 (OK OK) s1-eth2<->s3-eth3 (OK OK) s2-eth1<->h1-eth0 (OK OK) s2-eth2<->h2-eth0 (OK OK) s3-eth1<->h3-eth0 (OK OK) s3-eth2<->h4-eth0 (OK OK) mininet> net h1 h1-eth0:s2-eth1 h2 h2-eth0:s2-eth2 h3 h3-eth0:s3-eth1 h4 h4-eth0:s3-eth2 s1 lo: s1-eth1:s2-eth3 s1-eth2:s3-eth3 s2 lo: s2-eth1:h1-eth0 s2-eth2:h2-eth0 s2-eth3:s1-eth1 s3 lo: s3-eth1:h3-eth0 s3-eth2:h4-eth0 s3-eth3:s1-eth2 c0 mininet> h1 ping h2 PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data. 64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=43.2 ms mininet> pingall *** Ping: testing ping reachability h1 -> h2 h3 h4 h2 -> h1 h3 h4 h3 -> h1 h2 h4 h4 -> h1 h2 h3 *** Results: 0% dropped (12/12 received) #退出 ctrl+d
8、网络访问
http://172.17.0.2:8181/onos/ui/login.html 账号/密码:karaf/karaf
然后就可以看到拓扑图了