Unix Timestamp(Unix时间戳)

UNIX时间,或称POSIX时间是UNIX或类UNIX系统使用的时间表示方式:从UTC 1970年1月1日0时0分0秒起至现在的总秒数,不考虑闰秒。在多数Unix系统上Unix时间可以透过date +%s指令来检查。(Unix time (also known as Epoch timePOSIX timeseconds since the Epoch, or UNIX Epoch time) is a system for describing a point in time. It is the number of seconds that have elapsed since the Unix epoch.)

从这个解释可以看出来,同一时刻,在全世界任一时区,获取的Unix时间戳是相同的。

所以,针对PHP而言,time()函数获取的到时间戳与时区无关。

time ( ) : int

Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).

那么,进一步延伸,对于一个给定的日期时间字符串,例如:2020-01-01 00:00:00,那么获取这个日期时间对应的时间戳就是与时区有关的。因为不同时区下的2020-01-01 00:00:00距离UTC的1970-01-01 00:00:00的时间间隔是不一样的。

strtotime ( string $datetime [, int $now = time() ] ) : int

对于PHP而言,在使用strtotime函数时,如果日期时间字符串中没有包含时区信息,那么会使用默认的时区date_default_timezone_get()。(Each parameter of this function uses the default time zone unless a time zone is specified in that parameter. Be careful not to use different time zones in each parameter unless that is intended. )

$dateStr = '2020-01-01 00:00:00';

$timezone = 'Asia/Shanghai';
date_default_timezone_set($timezone);
echo sprintf("%13s)%25s ==> %s\n", $timezone, $dateStr, strtotime($dateStr));

$dateStrWithTimezone = '2020-01-01T00:00:00+08:00';
date_default_timezone_set($timezone);//优先读取日期时间字符串里的时区信息,此处单独设置的时区对下一行的strtotime无效
echo sprintf("%13s)%25s ==> %s\n", $timezone, $dateStrWithTimezone, strtotime($dateStrWithTimezone));

$timezone = 'UTC';
date_default_timezone_set($timezone);
echo sprintf("%13s)%25s ==> %s\n", $timezone, $dateStr, strtotime($dateStr));
echo sprintf("%13s)%25s ==> %s\n", $timezone, $dateStrWithTimezone, strtotime($dateStrWithTimezone));

output:
Asia/Shanghai)2020-01-01 00:00:00 ==> 1577808000
Asia/Shanghai)2020-01-01T00:00:00+08:00 ==> 1577808000
UTC)2020-01-01 00:00:00 ==> 1577836800
UTC)2020-01-01T00:00:00+08:00 ==> 1577808000 //优先读取日期时间字符串里的时区信息,runtime设置的时区对strtotime无效

 

date ( string $format [, int $timestamp = time() ] ) : string

Format a local time/date

同样的道理,同一个时间戳在不同的时区下,对应的日期时间字符串是不一样的。

date_default_timezone_set('UTC');
$timestamp = 1577836800;//UTC 2020-01-01 00:00:00
$sourceDatetime = new \DateTime(date('Y-m-d H:i:s', $timestamp));
echo sprintf("source datetime:%s(%s)\n", $sourceDatetime->format('Y-m-d H:i:s'), $sourceDatetime->getTimezone()->getName());

$timezone = 'Asia/Shanghai';
$targetDatetime = (new \DateTime(date('Y-m-d H:i:s', $timestamp)))
    ->setTimezone(new \DateTimeZone($timezone));
echo sprintf("target datetime:%s(%s)\n", $targetDatetime->format('Y-m-d H:i:s'), $targetDatetime->getTimezone()->getName());

output:
source datetime:2020-01-01 00:00:00(UTC)
target datetime:2020-01-01 08:00:00(Asia/Shanghai)

 

References:

https://zh.wikipedia.org/wiki/UNIX时间

记录cp命令在GNU/Linux和BSD(MacOS)下表现不一致的一个行为

重现步骤如下:

现有的目录结构:

├── source

│   └── source.txt

├── target

 

cp -r source target1   //target1 dir not exists

GUN/BSD
├── source

│   └── source.txt

├── target

└── target1

└── source.txt

 

cp -r source target   //target dir exists

GUN/Linux BSD(MacOS)
├── source

│   └── source.txt

├── target

└── source

└── source.txt

├── source

│   └── source.txt

├── target

│   └── source.txt

测试环境:

GNU coreutils 8.22(CentOS)

GNU coreutils 8.30(Ubuntu 20.04)

BSD macOS Catalina 10.15.7

 

总结/Summary:

使用cp -r 复制目录时,如果目标目录不存在,则GUN/Linux&BSD(mac OS)的cp行为一致;如果目录已经存在,则BSD(mac OS)下会和目标目录不存在时行为一致,但是GUN的cp会创建在目标目录下创建子目录。

 

PS:

如果目标目录已经存在,可以使用 cp -r source/.  target  or   cp -r source/* target

 

Console and TTY and PTY

Console:

在早期的电脑上,往往具有带有大量开关和指示灯的面板,可以对电脑进行一些底层的操作,这个面板就叫做Console。其概念来自于管风琴的控制台。一台电脑通常只能有一个Console,很多时候是电脑主机的一部分,和CPU共享一个机柜。

 

Terminal and TTY(终端):

一台大型主机往往需要支持许多用户同时使用,每个用户所使用操作的设备,就叫做Terminal——终端,终端使用通信电缆与电脑主机连接,甚至可以通过电信网络(电话、电报线路等等)连接另一个城市的电脑。

TTY是电传打字机Teletypewriter的缩写,TTY曾经是最流行的终端设备。现在大多数场景TTY基本就是表示terminal。

Linux有7个TTY?
从控制台切换到 X 窗口,一般采用Ctrl+Alt+F7 ,为什么呢?因为系统默认定义了6个虚拟控制台(Ctrl+Alt+F1 ~ F6), 7个用于x-windows实际上,很多人一般不会需要这么多虚拟控制台的,关闭多余的控制台减少对系统资源的占用,可以自己更改配置文件减少它的数量(在支持systemd的系统里,配置文件位置:/etc/systemd/logind.conf, https://freedesktop.org/software/systemd/man/logind.conf.html,所以具体系统开启了多少个tty,要看配置文件,可能各个发行版本不一样,比如Ubuntu18.04/20.04就是默认6个,而且默认是把tty1留给了x-windows(Ctrl+Alt+F1))。

 

PTY(Pseudo TerminalPseudo TTY, or PTY, 虚拟终端/伪终端):

pts/ptmx(pts/ptmx结合使用,进而实现pty)

所谓伪终端是逻辑上的终端设备,多用于模拟终端程序。例如,我们在X Window下打开的终端,以及我们在Windows使用telnet 或ssh等方式登录Linux主机,此时均在使用pty设备(准确的说在使用pty从设备)

 

echo test > /dev/tty1会把单词test发送到连接在tty1的设备上。

1、/dev/pts0, /dev/pts1, …

2、/dev/tty0, …

3、/dev/console 表示物理终端(控制台)

4、/dev/ttyS0, …  表示串行终端

tty命令可以查看当前登录的终端类型,比如/dev/tty2,/dev/pts/0

Go to WebAssembly

什么是WebAssembly?

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.

让我们用tinygo来写一个WebAssembly的模块,然后用js调用吧。首先下载TINYGO:(https://tinygo.org/,请下载对应版本的二进制文件)。然后将bin/tinygo(or tinygo.exe)加入到PATH路径。

然后先创建一个main.go文件(文件名必须是main.go,因为js里有用到文件名),文件内容如下:

package main

// This calls a JS function from Go.
func main() {
    println("Go: Hello World") // expecting 5
    println("Go: add(2+3):", add(2, 3)) // expecting 5
    println("Go: multiply(2*3):", multiply(2, 3)) // expecting 6
}

// This function is imported from JavaScript, as it doesn't define a body.
// You should define a function named 'main.add' in the WebAssembly 'env'
// module from JavaScript.
func add(x, y int) int

// This function is exported to JavaScript, so can be called using
// exports.multiply() in JavaScript.
//go:export multiply
func multiply(x, y int) int {
    return x * y;
}

然后用tinygo编译这个go文件,生成wasm文件。

目前最新版的tinygo0.12.0,最高只支持golang 1.13(目前最新版本的golang1.14),编译的命令中文件名必须是main.go,不能写”./main.go”

tinygo build -o wasm.wasm -target wasm main.go

Error:  tinygo build -o wasm.wasm -target wasm ./main.go

 

然后用go写一个简单的文件服务器(用于启动http服务器,因为wasm加载必须在http/https的环境下),注意对于wasm文件,要设置Content-Type为application/wasm

package main

import (
    "log"
    "net/http"
    "strings"
)

const dir = "./html"

func main() {
    fs := http.FileServer(http.Dir(dir))
    log.Print("Serving " + dir + " on http://localhost:8080")
    http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
        resp.Header().Add("Cache-Control", "no-cache")
        if strings.HasSuffix(req.URL.Path, ".wasm") {
            resp.Header().Set("Content-Type", "application/wasm")
        }
        fs.ServeHTTP(resp, req)
    }))}

然后在服务器目录下创建html目录,目录里新建index.html文件(注意js代码中设置env的代码):

<html>
<head>
    <title>WebAssembly</title>
</head>
<body>
<script src="wasm_exec.js"></script>
<script type="text/javascript">
    const go = new Go(); // Defined in wasm_exec.js
    const WASM_URL = 'wasm.wasm';
    go.importObject.env['main.go.add'] = function (x, y) {
        return x + y
    };
    var wasm;

    console.log("'instantiateStreaming' in WebAssembly => " + ('instantiateStreaming' in WebAssembly ? "Y" : "N"));
    if ('instantiateStreaming' in WebAssembly) {
        WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
            wasm = obj.instance;
            go.run(wasm);
            console.log('JS: multiplied two numbers:', wasm.exports.multiply(5, 3));
        }).catch((err) => {
            console.error(err);
        });
    } else {
        fetch(WASM_URL).then(resp =>
            resp.arrayBuffer()
        ).then(bytes =>
            WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
                wasm = obj.instance;
                go.run(wasm);
            })
        )
    }

</script>
</body>
</html>

index.html里面引用的wasm_exec.js文件就在tinygo下载包里,wasm.wasm就是我们刚刚编译生成的文件。

浏览器里打开这个网页 http://localhost:8080/index.html,发现控制台就能输出:

23:25:58.955 ‘instantiateStreaming’ in WebAssembly => Y

23:25:58.964 Go: Hello World

23:25:58.964 Go: add(2+3): 5

23:25:58.964 Go: multiply(2*3): 6

23:25:58.964 JS: multiplied two numbers: 15

23:25:58.966 Fetch finished loading: GET “http://localhost:8080/wasm.wasm”.

References:

https://webassembly.org/

https://tinygo.org/

 

《带着爸爸去留学》观影感言

时光如梭,一看,上一篇的读书笔记已是一年之前的了。过去一年忙于生存,少有闲暇和心情看书/剧、思考,鼠年加油!

想来看这部电视剧,是因为在网上看到一个片段: 董美玲(黄小栋他妈)请求黄成栋不要再说了(真相),请他消消气,要发火就对她发火,然后他们在餐馆点菜,黄成栋点了一桌绿色菜。

本着吃瓜群众的好奇(八卦)心态,在这个春节假期就看了这部剧。

前面几集感觉就是:毁三观。感觉几个小孩都欠揍。之前家庭里都太惯着他们,让他们没有培养独立处理事情的能力,本剧是借着留学来说这些事情,在我看来即使不留学,在国内他们也会做出一些类似的奇葩事情来。不过结局我觉得倒是不错,虽然过程曲折,孩子们受过了不少伤,但是孩子们都成长了,开始能够成熟的面对生活中遇到的事情。

看了这部剧,更坚定了我的看法:夫妻关系才是一个家庭的第一位,尤其不能将对小孩的爱凌驾于夫妻关系之上。夫妻相互扶持,对待小孩要爱护,但是也要适当放手,要培养他们独立的生活能力。要知道将来陪TA过完一生的肯定不是父母,TA只有知道如何独立,如何对待亲人、朋友、另一半(如果有),他可能有幸福的生活。总结起来就是:父母爱子女则为之计深远。

爱就是克制。

PHP中foreach循环时使用引用的一个坑

记录一个广为流传的关于使用PHP引用的坑。

$arr = ['abc', 'ben', 'cam', 'day'];
foreach($arr as $key=>&$val){
    $val = strtoupper($val);
}
foreach($arr as $val){
    echo "$val\n";
    print_r($arr);

}
echo "===END===\n";
print_r($arr);

解释:

//第二次使用$val做foreach循环时,此时$val还是保持着对$arr数组里的最后一个元素($arr[3])的引用。
//所以对于这个foreach,
//1st ==> $arr[3]=’abc’
//2nd ==> $arr[3] = ‘ben’
//3rd ==> $arr[3] = ‘cam’
//4th ==> $arr[3] = ‘day’

所以正确的做法是在使用引用的foreach结束后马上unset($val)
或者干脆不用使用引用,foreach时使用$key=>$val格式,然后使用$arr[$key]方式修改变量并保存。

Wall Clock and Monotonic Clock

Wall clock(time) VS Monotonic clock(time)

Wall clock(time)就是我们一般意义上的时间,就像墙上钟所指示的时间。

Monotonic clock(time)字面意思是单调时间,实际上它指的是从某个点开始后(比如系统启动以后)流逝的时间,jiffies一定是单调递增的!

而特别要强调的是计算两个时间点的差值一定要用Monotonic clock(time),因为Wall clock(time)是可以被修改的,比如计算机时间被回拨(比如校准或者人工回拨等情况),或者闰秒( leap second),会导致两个wall clock(time)可能出现负数。(因为操作系统或者上层应用不一定完全支持闰秒,出现闰秒后系统时间会在后续某个点会调整为正确值,就有可能出现时钟回拨(当然也不是一定,比如ntpdate就有可能出现时钟回拨,但是ntpd就不会))

 

PHP 7.3新增了hrtime函数

hrtime ([ bool $get_as_number = FALSE ] ) : mixed

Returns the system’s high resolution time, counted from an arbitrary point in time. The delivered timestamp is monotonic and can not be adjusted.

<?php


while(true){
    var_dump(time());
//    $micro = microtime(true);
//    var_dump($micro);
//    echo PHP_EOL;

    //var_dump(hrtime());
    $nanosecond = hrtime(true);
    var_dump($nanosecond/1000/1000/1000);//nanosecond => second
    echo PHP_EOL;
    echo PHP_EOL;
    echo PHP_EOL;
    echo PHP_EOL;

    sleep(5);
}

执行此脚本,然后在过程中手动更改系统时间。执行结果如下

 

……

int(1554975423)

float(481987.58385405)

 

int(1554975428)

float(481992.58812)

 

int(1554975433)    ==>    北京时间:2019/4/11 17:37:13

float(481997.59318082)

 

int(1554889031)   ==>      北京时间:2019/4/10 17:37:11

float(482002.59569814)

 

int(1554889036)

float(482007.59825222)

……

 

hrtime()的返回值就是一直单调递增的,而time()的返回值就可能出现跳跃。所以在计算时间差时要用hrtime(),注意PHP版本至少要7.3 。

 

 

《重构 改善既有代码的设计》读书笔记

 

本书可以被看成是一本重构方法的手册。且因为本书成书较早,所以很多重构手段都被大家耳熟能详并从入门开始都被当成的编码规范。所以很大程度上阅读本书也算是温习。

  1. Kent Beck:我不是个伟大的程序员,我只是个有着一些优秀习惯的好程序员。
  2. (大多数时候)不需要专门安排时间重构,重构应该随时随地进行。你不应该为重构而重构,你之所以重构,是因为你先做别的什么事,而重构可以帮助你把那些事做好(比如效率更高,能容易扩展)。重构与编码总是伴随进行的。重构不是一劳永逸的,我们需要在系统发展中不断的进行,不断重构出适应未来的代码(而且这个未来往往也只能指未来一段时间,而非永远)。
  3. “三次法则,事不过三,三则重构。” 实际上,有了IDE的帮助,但第二次相同代码出现时,IDE就会提示需要重构了。
  4. 把未完成的重构工作形容为「债务」。
    Ward Cunningham对此有一个很好的看法。他把未完成的重构工作形容为「债务」。很多公司都需要借债来使自己更有效地运转。但是借债就得付利息,过于复杂的代码所造成的「维护和扩展的额外开销」就是利息。你可以承受一定程度的利息,但如果利息太高你就会被压垮。把债务管理好是很重要的,你应该随时通过重构来偿还一部分债务。
    如果项目己经非常接近最后期限,你不应该再分心于重构,因为己经没有时间了。不过多个项目经验显示:重构的确能够提高生产力。如果最后你没有足够时间,通常就表示你其实早该进行重构。
  5. 从重构联想到软件复用和技术传播
    Geoffrey Moore 把「技术的接纳过程」描述为一条钟型(bell-shaped )曲线:前段包括先行者(innovators )和早期接受者(early adopters),中部急剧增加的人群包括早期消费群体(early majority )和晚期消费群体( late majority),后段则是那些行动缓慢者(laggards)。一个思想或产品如果要成功,必须得到早期消费者和晚期消费者的广泛支持。另一方面,许多对于先行者和早期接受者很有吸引力的想法, 最终彻底失败,因为它们没能跨越鸿沟,让早期消费者和晚期消费者接纳它们。之所以有这样的鸿沟是因为,不同的消费人群有着不同的消费动机。先行者和早期接受者感兴趣的是新技术、「范式移转和突破性思想」的愿景(visions of paradigm shifts and breakthroughs)。早期和晚期消费群则主要关心成熟度、成本、支持,以及这种新思想或新产品是否被「与他们有着相似需求」的其他人成功套用。
    要打动并说服软件开发者,所需方式和打动并说服软件研究者是完全不同的。软件研究者通常是Moore 所说的「先行者」,软件开发者(尤其是软件经理)则往往属于早期或晚期消费者。如果想要让你的思想深入所有人心,了解这一差异是非常重要的。是的,无论软件复用或重构,要想打动软件开发者,这一点都至关重要。

 

《网络安全——输不起的互联网战争》读书笔记

  1. 作者认为,因为物理设备的多样性,网络攻击很难大面积展开。
  2. Buckshot Yankee
  3. 很难定位网络攻击的真实来源,即使定位了物理上的来源,但是由于来源设备可能存在被第三方入侵,所以不能代表设备拥有者的意图。当然有时攻击方也会利用这种情况否认己方发动了攻击。
  4. 作者认为现在网络的攻击的危害有限,当然他同时也告诫我们不能放松警惕。我是不太认同作者的这个观点的。作者说认为现在网络攻击对普通民众的日常生活的影响微乎其微,相比物理上的攻击危害(比如恐怖袭击、核弹……)也小很多。但是我认为现在互联网正在占领各个传统领域(被传统领域应用),比如国内的交通、支付、甚至日常工作……所以网络攻击的危害已经非常大了。

结合第一点,即使物理设备的多样性,但是由于单一种类设备的使用基数大(在不同场景下),那么攻击者还是愿意投入精力开发攻击软件的。另外,现在物理设备越来越智能化,能够运行小型的OS(Unix或Linux),也给攻击者提供了很多发挥的空间。

  1. 恢复能力、组织松散性

“即使在私营企业中,恢复能力与提高效率的目标也是矛盾的,因为冗余明显是一种浪费。”

“专家们注意到,哪些最具效率的企业其实很脆弱,而且受到适应变化能力差及创新能力不足的制约。这些企业缺少”组织松散性“,这种松散性往往能产生积极的文化,并促使企业成才。同样建立“恢复能力”的组织特征也有助于增强“组织松散性”。同样,充分进行安全评估,已经让员工了解企业的远期目标,有助于提高恢复能力.”

  1. 全球范围内,很难在网络安全领域达成一致的协议。很多国家都希望能够独立掌控本国范围内的网络,他们认为网络不存在“没有主权”的公共空间。

 

总的来说,网络安全对一个国家而言是十分重要的,虽然目前时间,网络攻击没有像真实武器那样危害深刻且明显,但是我们一定要在战略上予以重视。在战略层面搭建网络安全框架,与世界网络强国保持紧密联系,尝试与各国一起建立统一的网络安全规范,努力追求网络更高层次的安全。

 

stuxnet(震网病毒)

震网(Stuxnet),又称作超级工厂,是一种Windows平台上的计算机蠕虫,2010年6月首次被白俄罗斯安全公司VirusBlokAda发现,其名称是从代码中的关键字得来,它的传播是从2009年6月开始甚至更早,首次大范围报道的是Brian Krebs的安全博客。它是首个针对工业控制系统的蠕虫病毒,利用西门子公司控制系统(SIMATIC WinCC/Step7)存在的漏洞感染数据采集与监控系统(SCADA),能向可编程逻辑控制器(PLC)写入代码并将代码隐藏。

这是有史以来第一个包含PLC Rootkit的计算机蠕虫,也是已知的第一个以关键工业基础设施为目标的蠕虫。此外,该蠕虫的可能目标为伊朗使用西门子控制系统的高价值基础设施。据报道,该蠕虫病毒可能已感染并破坏了伊朗纳坦兹的核设施,并最终使伊朗的布什尔核电站推迟启动。不过西门子公司表示,该蠕虫事实上并没有造成任何损害。

赛门铁克安全响应中心高级主任凯文·霍根(Kevin Hogan)指出,在伊朗约60%的个人计算机被感染,这意味着其目标是当地的工业基础设施。俄罗斯安全公司卡巴斯基实验室发布了一个声明,认为Stuxnet蠕虫“是一种十分有效并且可怕的网络武器原型,这种网络武器将导致世界上新的军备竞赛,一场网络军备竞赛时代的到来。”并认为“除非有国家和政府的支持和协助,否则很难发动如此规模的攻击。”伊朗成为了真实网络战的第一个目标。

References:

https://zh.wikipedia.org/wiki/%E9%9C%87%E7%BD%91

《微信思维》读书笔记

在微信逐渐变成的臃肿、垄断、甚至有点烦人的今天,我们回头看看微信发展与成功,应该更深刻。

微信的成功之处在于连接,让商家和顾客通过微信这个平台实现了一对一的交流。然后通过微信支付,实现了线上流程从下单到支付的整个流程覆盖。对于日趋火爆的O2O行业来说,使用微信后,商家只需要提供线下服务即可,其他的皆可在微信上完成。

微信虽然有公众号和服务号,但是相比微博,服务号还是它的大杀器。商家和顾客一对一的交流形式,衍生了极大的操作空间。比如作为在线客服,线上服务(下单、支付、查询、售后、消费提醒……)……

本书在很多章节都在反复宣传一个观点:企业一定要有明确的自我定位,是想做宣传还是想做服务。想做宣传请使用公众号;要做服务,最好申请服务号。服务号的核心是提供服务,不要经常推送宣传文案打扰用户。

到了2018年再来看看微信,发现它已经是一个巨物了。一方面,微信作为腾讯的独家产品,有时候背负了整个公司的战略目标,比如打压竞争对手,扶持内部业务;另一方面,亲友交流、公司业务沟通、各种企业服务、各种企业推广都占据了你的微信,微信已经成为了信息洪水。不知道,在完成连接商家与顾客连接之后,下一步是不是减负?