Jasper Ji

博观而约取,厚积而薄发。

Brew实践

2021-04-16 09:20

Mac上安装软件基本上都在使用Brew,做一些记录。

###常用命令

brew info 有些软件长时间不用了,记不起来命令的使用,用这个可以查到如何使用。

###常见问题

更新慢

执行brew update时很慢的问题,主要是是Github访问慢的缘故,设置从镜像拉取,基本上可以解决这个问题。用了中科大的源,亲测速度感刚的。

替换brew.git
cd "$(brew --repo)"
git remote set-url origin https://mirrors.ustc.edu.cn/brew.git
替换homebrew-core.git
cd "$(brew --repo)"/Library/Taps/homebrew/homebrew-core
git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git
替换homebrew-cask.git
cd "$(brew --repo)"/Library/Taps/homebrew/homebrew-cask
git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-cask.git

参考

brew update 更新时 shallow clone
mac使用brew update无反应,更新慢解决办法

Eclipse笔记

2021-04-14 06:43

为什么是Eclispe

最早接触Eclispe主要是用来开发Android应用,早期Android开发没有Android Studio,只能通过安装插件来开发。后来开发iOS后,Eclispe就用的少了,偶尔用来跑些简单的例子。后来做Java web,Eclispe又开始用了起来。

虽然很多人推荐IntelliJ IDEA,下载的试用了下,某些功能确实比Eclispe强大,但是Eclispe+Spring Tools,对目前的开发来说确实已经够用了,另外还是有点用不习惯,也没有Spring tools 这样的插件,所以干嘛花钱买个自己用不惯的东西呢?

自动补全

默认出现点号时才会出现自动补全的提示,不过可以设置,这里以Mac环境为例。

选择菜单Perferences->Java->Content Assist->Auto activation triggers for Java,默认为.,使用.abcdefghijklmnopqrstuvwsyzABCDEFGHIJKLMNOPQRSTUVWSYZ_替换,点击Ok。

常用插件

EasyShell,这个工具主要是可以快速在当前目录打开Shell以及打开当前的文件夹。

Maven 配置文件报错

Maven使用本地Jar包后pom.xml显示错误,实际上本地Jar包是可以加载的,但总是提示错误。

dependencies.dependency.systemPath’ for com.test:test:jar must specify an absolute path but is ${pom.basedir}/libs/test.jar

<dependency>
	<groupId>com.test</groupId>
	<artifactId>test</artifactId>
	<version>1.0</version>
	<scope>system</scope>
	<systemPath>${pom.basedir}/libs/test.jar</systemPath>
</dependency>

这个问题虽然不影响使用,但是老显示红叉,总感觉像去掉的。我原来使用Eclispe版本是2020-03 (4.15.0),没有这个问题。但是使用最新的Eclipse版本2021-03 (4.19.0)有这个问题,包括Spring ToolSuite 4也有这个问题,我把4.15.0到4.19.0版本下载几个对比后,终于发现4.16.0开始都有问题,最后发现4.16.0版本Preferences多了一个XMl(Wide Web Developer)项,把子项Validation & Resolution,Enable validation 禁用就没有了。

升级与不升级

如果现有环境稳定,那就最好不要升级。Eclispe好处是可以支持多个语言的开发,可以装一堆插件支持,但感觉只把它当作Java IDE就可以了,现在VS Code完全可以做其他语言的IDE。

之前的Eclispe很稳定,但一次插件的安装后,弹出需要更高版本的Eclispe,然后就无法启动了。一气之下,直接删除Eclispe,重新安装那些插件,结果发现2020-03 (4.15.0)下载的Spring Tools插件不能使用,发现Spring Tools 4已经出了,但是我当前这个版本无法使用。最后下载了Spring Tools官方出品的Eclispe整合版本Spring ToolSuite 4,就结果就出现了上面提到的Maven配置报错的问题,虽然问题解决了,但是还是提示插件安装也是要谨慎,可能升级不对就的折腾半天了。

一次语言间的性能对比

2021-04-02 05:53

最近突然间想对比下几个语言间的性能,日常使用的是Java,但平时也会用Python,JavaScript,C,Go之类的语言。Java写项目真的也没有什么问题,成熟的生态圈,遇到问题很容易解决。不过写点小东西的时候,还是觉得Python比较好用,不过就是速度慢了点,很早之前写过一个游戏的移植,脚本语言重构真的是个大问题。Go在上家公司的时候也使用过,不过也都是小的测试了,并没有拿来写过完整点的项目。不过语言的简洁性和C的这种传承,还是很有好感,不过在当时重写公司系统时,最后还是用了Java,主要是大部分的对接都有Java的实现。C一直是我研究的语言,虽然实际写东西不多,但是却看了很多相关的书籍,另外最近研究操作系统和学习汇编语言后,对C的认识又有了新的认识,C的厉害地方就是简洁的设计,性能强大,他的诞生就是为写Unix,缺点是实用性不强,另外内存管理,实际上成熟的项目在内存管理上都有自己的框架,所以对于C/C++之类的语言光学习了语言其实并不能做什么,离应用的距离还很远。

回到正题,我实际上想把Go作为业务研究的语言了,虽然脑海中一直印象他的性能不错,但具体表现怎样能,好像我也并不清楚。最后选择对比的语言是Java、Go、C、Rust四个语言,程序都是运行100000000循环,循环内执行冒泡排序。

Java 代码

public class Test {
	public static void main(String[] args) {
		// 记录开始时间
		long start = System.nanoTime();
		int num = 100000000;
		for (int i = 0; i < num; i++) {
			int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
			bubbleSort(arr);
		}
		// 打印耗时时间
		System.out.println(System.nanoTime() - start);
	}

	// 排序
	public static void bubbleSort(int[] arr) {
		int length = arr.length - 1;
		for (int i = 0; i < length; i++) {
			for (int j = 0; j < length - i; j++) {
				if (arr[j] < arr[j + 1]) {
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}
	}
}

运行时间

3052757121

Go 代码

package main

import (
	"fmt"
	"time"
)

func main() {
	start := time.Now().UnixNano()
	const NUM int = 100000000
	for i := 0; i < NUM; i++ {
		arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
		bubbleSort(arr)
	}
	// 打印消耗时间
	fmt.Println(time.Now().UnixNano() - start)
}

// 排序
func bubbleSort(arr []int) {
	for j := 0; j < len(arr)-1; j++ {
		for k := 0; k < len(arr)-1-j; k++ {
			if arr[k] < arr[k+1] {
				arr[k], arr[k+1] = arr[k+1], arr[k]
			}
		}
	}
}

运行时间

3804215000 # 纳秒

C 代码

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#include <sys/time.h>
#include <unistd.h>

void bubbleSort(int* array, int length) {
    for (int i = 0; i < length -1 ; i++) {
        for(int j=0; j < length -1 -i; j++) {
            if(array[j] < array[j+1]) {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}

int main() {
    struct timeval t_val;
    gettimeofday(&t_val, NULL);
    printf("start, now, sec=%ld m_sec=%d \n", t_val.tv_sec, t_val.tv_usec);

    for (int i = 0; i < 100000000; i++) {
        int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        int length = sizeof(array)/sizeof(int);
        bubbleSort(array, length);
    }
    
    struct timeval t_val_end;
    gettimeofday(&t_val_end, NULL);

    struct timeval t_result;
    timersub(&t_val_end, &t_val, &t_result);

    double consume = t_result.tv_sec + (1.0 * t_result.tv_usec)/1000000;
    printf("end.elapsed time= %fs \n", consume);

    return 0;
}

运行时间

start, now, sec=1617327646 m_sec=57219 
end.elapsed time= 1.746955s 

Rust代码

use std::time::Instant;

pub fn bubble_sort<T: Ord>(arr: &mut [T]) {
    for i in 0..arr.len() {
        for j in 0..arr.len() - 1 - i {
            if arr[j] < arr[j + 1] {
                arr.swap(j, j + 1);
            }
        }
    }
}

fn main() {
    let start = Instant::now();

    for _i in 0..100000000 {
        let mut numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
        bubble_sort(&mut numbers);
    }

    println!("time cost: {:?} ms", start.elapsed().as_millis()); // ms
    println!("time cost: {:?} us", start.elapsed().as_micros()); // us
    println!("time cost: {:?} ns", start.elapsed().as_nanos()); // us
}

运行时间

time cost: 1763 ms
time cost: 1763298 us
time cost: 1763300912 ns

Swift 代码

import Foundation

func bubbleSort<T: Comparable>(with array: inout [T]) -> [T] {
    for i in 1..<array.count {
        for j in 0..<array.count-i where array[j] < array[j+1] {
            array.swapAt(j, j+1)
        }
    }
    return array
}


let start = DispatchTime.now()
for _ in 0..<10000000 {
    var intArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    bubbleSort(with: &intArray)
}
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
let timeInterval = Double(nanoTime) / 1_000_000_000
print("Time: \(timeInterval) seconds")

运行时间

Time: 2.391004642 seconds

总结

最早我对比的是Java和C,因为C是在Debug模式下跑的,反而比Java的慢,这个让我无法接受,不应该啊,后来让朋友跑了下,说应该用Release模式,这才反应过来,Release + O3优化后速度一下子就上来了,明显比Java快。Rust和C基本上都有Debug和Release的区分,现在看来Rust和C是一个级别,Java和Go是另一个档次上的同一级别。现在的Java运行默认是解释和编译的混合模式,所以速度上自然也不错,和Java对标的Go数据上来看略微差点,不过考虑到Java的运行时开销,Go其实还是很有诱惑的,后续有可能赶超Java。

企业开发的话用Java依旧最好的选择,个人创业项目的话用Go其实不错。对于Rust和C,不好选择C语法上比较简洁,很多语言特性跟底层更靠近,需要一定底层知识,写操作系统的话,C依旧是首选。Rust呢?个人觉得语法上比较复杂,混杂的语言特性过于多了,既想兼容底层有想高级别的抽象上靠近现代语言,不过这样的情况下依然保持高性能,还是很令人惊讶的。这个不好选,但Rust更充满诱惑,但复杂的语法让人畏惧。

Go语言虽然有C的传承,不过在类型定义上使用后缀的形式跟C语言正好相反,有时还是比较抵触的,另外因为GC的缘故,性能毕竟还是处于应用语言的级别。Rust太复杂,Swift没有用GC是引用计数,速度上比Rust差些,语法上其实跟Rust也差不多,不过作为一个曾经的iOS开发者,虽然Swift在一段时间大家都非常看好,不过最近几年过去了,其实Swift更多还是Oc的一种替代品,并没有突破苹果的范围,服务端也没有掀起什么风浪,Swift终究是Oc的替代品,也正因为此,他也跟苹果生态深度的绑定,成了某种无法脱颖而出的枷锁。

我心中语言其实就是C语言的改良版,内存管理上希望在语言层面上解决,比如用引用计数。语法上不要太加很多的特性,有Go的那样就可以了,但不是Go。不过目前来看是没有的,要不就是自己写一个,也不是没有可能。但实在要是也没有的Go和Rust总还是不错的选择。

Go学习笔记

2021-03-29 14:46

开始用Go应该是17年底的时候,那会做游戏,后台是用Go写的,虽然我主要负责Unity客户端方面的工作,不过也是经常帮忙给后端的Go小伙解决一些问题,自己也写一些Go的小东西。

后来又做物流系统,重写系统时有考虑用Go,不过因为大部分的外部接口对接都是Java的,为了省事就使用了Java。不过如果是自己的项目的话,还是愿意用Go,毕竟Java还是有点重。

IDE

原来有用过GoLand,为了轻量点,先用VS Code,众所周知的原因安装插件老失败,最后设置Go的代理就可以了。

go env -w GOPROXY=https://goproxy.cn,direct

C程序员的礼物

直接试着写个搜索二叉树,其实除了语法上跟C的一些类型定义次序上有点不习惯外,其他方面对于C程序员来说,还是熟悉的用法,比如指针的指针这样的概念,毕竟Go是原来C的那帮人搞的,兼顾C的同时也结合不少Python这样的现代语言的特性。

package main

import "fmt"

type Node struct {
	data      int
	leftNode  *Node
	rightNode *Node
}

func insert(tree **Node, value int) {
	if *tree == nil {
		var tempNode Node
		tempNode.data = value
		*tree = &tempNode
		return
	}

	if (*tree).data > value {
		insert(&(*tree).leftNode, value)
	} else if (*tree).data < value {
		insert(&(*tree).rightNode, value)
	}
}

func perOrder(tree *Node) {
	if tree != nil {
		fmt.Printf("data: %d\n", tree.data)
		perOrder(tree.leftNode)
		perOrder(tree.rightNode)
	}
}

func main() {
	var tree *Node

	insert(&tree, 9)
	insert(&tree, 4)
	insert(&tree, 15)
	insert(&tree, 6)
	insert(&tree, 12)
	insert(&tree, 17)
	insert(&tree, 2)

	perOrder(tree)
}

《孙正义传》读后感

2021-03-28 22:55

对于孙正义和软银我还是蛮好奇的,首先我不太清楚这是个什么样的公司,“孙正义”三个字听着好像不像日本人。带着疑惑我迫不及待的购买了这本由彬本贵司撰写的《孙正义传》,大概用一天的时间很快的读完了,以下是一些总结。

孙正义是爷爷辈来到日本谋生的,到他这是第三代,日本姓是“安本”。作为归化的日本人,已经在文化上深深的日本化了,比如他对明治维新时代的倒幕派人士坂本龙马就很崇拜。因为早年在美国学习的经历让孙正义很有国际视野,无论是从技术还是经营上。

孙正义在美国有学习计算机,但总的来说他不属于技术牛人那种,属于有技术背景的商人。从他当年跟大学的教授合作开发翻译机,获得了夏普的资金,赚了笔钱,接着创办软银,开始做软件代理业务,看到这我才发现软银原来是这个意思。

软银这家公司感觉好像并没有创造什么,主要的业务在日本,包括软件流通、宽带、手机运营商,日本雅虎等。感触最深的就是他不停的收购公司,资金调度这块还是很牛了。另外孙正义也很有投资眼光,对雅虎和阿里巴巴这些企业的投资着实赚了不少。

总体来看就是要做朝阳产业,提前布局,而且布的都是大局,大资金的事。这点还是值得去学习,花点时间好好研究下未来的走向也许更重要。

《Orange'S:一个操作系统的实现》笔记

2021-03-24 15:18

使用FreeDOS运行COM

FreeDOS运行COM文件记录,主要是拷贝软盘里面,然后执行。

sudo mount -o loop xxx.img /mnt/floppy # 挂载,挂在失败的话在mnt目录下创建floppy文件夹
sudo cp xxx.com /mnt/floppy/ # 拷贝文件
sudo umount /mnt/floppy # 卸载

网上书中的源码中的freedos.img并不包含debug这样的工具,不过debug并不能调试保护模式的代码。

bochsrc配置文件中添加

magic_break: enabled = 1

最后在汇编代码需要断点的地方添加

xchg bx, bx

在FreeDOS执行COM文件后,就会进入到bochs调试。

Bochs

有用的调试系统的命令
sreg # 显示段寄存器的内容
info gdt  # 显示GDTR寄存器的内容
creg # 显示CR0寄存器的内容
print-stack # 查看栈的内容

参考

如何调试操作系统

NASM 笔记

2021-03-22 14:57

编译

目前使用的是64位的Ubuntu环境,默认会编译成64位的,编译成32位的在参数上有些差异。

nasm -f elf32 foo.asm -o foo.o # NASM编译成ELF32位目标文件
gcc -m32 -g -c bar.c -o bar.o # GCC编译成32的目标文件
ld -m elf_i386 foo.o bar.o -o foobar # 链接目标文件,生成执行文件

GDB

一些常用的GDB调试的命令

set disassembly-flavor intel # 默认AT&T格式,可以设置为Intel格式
disassemble _start # 反汇编,这一步主要是为了后续设置断点用
break * _start + 10 # 设置断点,此处是_start开始的第10行代码处
run # 执行程序,设置断点后会停在断点处
step # 单步调试,可以简写为s
info registers # 显示寄存器
layout reg # 一般我会使用这个
q # 退出程序
x 0xffffd070 # 查看0xffffd070地址处的内存

参考

ASM 110: Gdb (30 pts)
GDB命令基础,让你的程序bug无处躲藏

《保护方式下的80386及其编程》笔记

2021-03-11 09:42

《保护方式下的80386及其编程》即《Programming the 80386》的中文翻译版,不过当年并没有标注翻译出处。这本书对于写一个基于x86操作系统非常有用,另外对于研究早期的Linux内核也很有参考,因为林纳斯当年也是参考《Programming the 80386》这本书的,包括386BSD的作者也是参考这本书了。

这本书是了解80386的保护模式最好的书籍,关于8086的书籍和汇编语言资料不少,但是关于80386的保护模式书籍很少,很多后来的书籍的知识都是转来转去的,有些只是讲个大概,没有把细节说明清楚,往往是一知半解了。不同的是此书可以说是讲解非常细致,概念很清晰,主要是此书的作者是80386的总设计师,所以这本书如果讲不明白,那还有其他书籍吗。

如何写一个操作系统

2021-02-04 22:21

缘起

做程序员也有些年头了,做过的项目各式各样,但都不是什么成功项目,所以到底如何证明自己呢?于是想到了尝试写个操作系统试试,也算通用了。

大概7、8年前买过一本《Orange’S 一个操作系统实现》,主题很吸引我,可惜没读过三章就停滞不前了,汇编以及体系方面的东西让我这个半路出家的程序员看的一脸苦相,但我还是试图试试,于是就买了另一本关于汇编的书籍,王爽老师的《汇编语言.第三版》,依旧记得去乾陵一日游大巴上,我努力的读着,可惜作为一个刚接触脚本语言的人来说,汇编语言太难了,中间需要跨越的东西太多了。渐渐就放弃了,后来直接把这两本书给卖掉了。

今年突然间又想,我应该尝试写一个简单的操作系统,多年的工作以及自学,使得我对编程有了一个相对完整的认识,这个想法似乎不像当年那么不靠谱。但该如何下手呢?刚开始我只想能跑一个Hello wrold的例子也可以,网上找了下最多的居然是我当年卖掉的那本《Orange’S 一个操作系统实现》书中的例子,可惜当年我也没怎么看懂,只是买了本书而已。

例子

最简单的例子,来自《Orange’S 一个操作系统实现》一书。


	org 07c00h			; 告诉编译器程序加载到7c00处
	mov ax, cs
	mov ds, ax
	mov es, ax
	call DispStr			; 调用显示字符串例程
	jmp $			; 无限循环
DispStr:
	mov ax, BootMessage
	mov bp, ax			; ES:BP = 串地址
	mov cx, 16			; CX = 串长度
	mov ax, 01301h		; AH = 13,  AL = 01h
	mov bx, 000ch		; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
	mov dl, 0
	int 10h			; 10h 号中断
	ret
BootMessage:		db	"Hello, OS world!"
times 	510-($-$$)	db	0	; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw 	0xaa55				; 结束标志

这就是一个直接运行到硬件上的一段程序,而不是我们平时在操作系统写应用代码那样。对于一个实践者而言,我很想把这段程序跑起来,这很关键,只要能跑起来,我们就可以试图修改下,再跑起来,这样认识上才能有提高。其实跑这个例子也是费了点功夫,首先在虚拟机上安装了一个的Linux发行版Ubuntu系统,书中用到的汇编是跟MASM很相似的NASM,首先NASM的教程不多,不过还好可以参考MASM汇编的书籍。另外就是软盘的制作相关的东西,最后是安装Bochs虚拟机,这个虚拟机跟平时用的有些特别,不过还是了解如何操作,最后成功的把这个例子跑了起来。

汇编和CPU

似乎可以继续一路向前了,可惜看了头三章后,我发现了些问题。首先我对NASM汇编的东西几乎是一片空白,书的前几章几乎离不开汇编语言了,而看的非常吃力。另一个是我对PC的体系缺乏细节的了解,虽然之前也有了解但实操的过程中遇到的问题更具体。

NASM的书籍不多,《汇编语言.基于Linux环境.第三版》是一本从体系的角度来讲解NASM的书籍,一方面学习了NASM,也让我重新思考从8086到80386的一些变化,好几个点很有启发。王爽老师的《汇编语言》,也是一本非常不错的参考书,汇编语言在形式上与高级语言还是有着巨大差异,一开始我把心思放在那些寄存器之类上了,而忽略了一个编程语言应该是能够写点东西出来这事上。《汇编语言》这书很好的带我进入如何写点东西的正轨上。

8086方面的书籍,8086总设计师莫尔斯写的《8086初阶-体系结构、系统设计和程序设计入门》。王爽老师《汇编语言》也主要是讲8086的汇编的。80386的书籍不多,80386的设计师写的《Programing the 80386》一书,非常不错。中文版是93年出版的《保护方式下80386及其编程》一书,虽然没有备注是翻译,但我跟英文版对照后,确认就是翻译自《Programing the 80386》,主要是学习保护模式。

这里需要注意的是实模式与保护模式的差别,很多时候一开始如果研究的是8086的,往往需要转换下思维,最好的书籍依旧是《保护方式下80386及其编程》一书,于渊的《Orange’S 一个操作系统实现》细节上还是有点模糊。

总结

最终操作系统并没有完整的实验下去,主要是因为最难的部分也是最容易忽略的如何在特定架构上如何开始,把这一步迈过去后,基本上跟大多操作系统讲的差不多了,主要部分就是使用C语言来实现。

虽然实验已经过去了几个月了,最近一直在看Java并发的东西,发现实际写过点操作系统还是很有帮助的,比如更能从CPU的角度来思考。

在CentOS 8上Mysql 8.0的主从配置实践

2021-01-05 10:47

主要试了下基于二进制日志文件的方法

主库设置

主要的坑,一般教程上写的是编辑/etc/my.cnf。安装的Mysql 8的配置略微有些问题,my.cnf文件导入的是另一配置!includedir /etc/my.cnf.d,所以以下配置我是写在了/etc/my.cnf.d/mysql-server.cnf里面。

###主从数据库配置核心部分
# 设置同步的binary log二进制日志文件名前缀,默认为binlog
log-bin=mysql-bin
# 服务器唯一id,默认为1  主数据库和从数据库的server-id不能重复
server-id=1          

###可选配置
# 需要主从复制的数据库
binlog-do-db=test
# 复制过滤:也就是指定哪个数据库不用同步(mysql库一般不同步)
binlog-ignore-db=mysql
# 为每个session分配的内存,在事务过程中用来存储二进制日志的缓存
binlog_cache_size=1M
# 主从复制的格式(mixed,statement,row,默认格式是statement。建议是设置为row,主从复制时数据更加能够统一)
binlog_format=row
# 设置二进制日志自动删除/过期的天数,避免占用磁盘空间。默认值为0,表示不自动删除。
expire_logs_days=7
# 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
# 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062