懒惰是人类进步的源动力

  • GSX查询“已更换的序列号” 苹果成功申诉记录

    0

    妹子的手机准备出售,买家要求查询GSX信息,然后找某家查了一下,结果如下:

    当前查询:苹果验机报告
    
    零件说明:IPHONE 11 PRO MAX MG 256GB-CHN
    型号:iPhone 11 Pro Max 256GB Midnight Green (A2220)
    IMEI:===
    IMEI2:===
    序列号:===
    类型:iPhone12,5
    激活状态:已激活
    预估购买日期:2019-10-25
    保修状态:Out Of Warranty (过保)
    保修结束日期:已过期
    保修剩余:0天
    技术支持:已过期
    是否延保:否
    是否官换机:否
    已更换产品的序列号:是
    激活锁:关闭
    ID黑白:Off (白)
    运营商:Unlock
    网络锁:Unlocked (无锁)
    下次激活策略ID:10
    网络型号:A2220
    网络制式:全网通 (4G/3G/2G)
    产品类型:零售机
    购买地点:China
    AppleCare购买资格:无
    维修状态:曾经维修
    制造商:Foxconn
    生产日期:2019-09-09
    借出设备:否

    一开始以为是GSX查询出问题了,结果苹果官网一查,果然是一条红色信息:很抱歉,这是已更换产品的序列号。这就头大了。

    1

    于是决定申诉Apple,直接找客服。客服验证了这一查询结果后,反馈说这个需要高级顾问解决问题,然后直接等回电。回电询问手机的购买凭据、包装盒是否齐全,手机有没有经第三方维修过。还好是天猫旗舰店买的机子,而且盒子没扔掉。
    然后要求提供几样东西的照片:

    • 购买凭据。我提供了发票,但一开始发票上没有注明序列号,顾问让打印出电子发票后手写在备注里,再拍照上传;
    • 包装盒背面有条形码的位置拍照;
    • 三包卡照片;
    • 手机“关于本机”页面的截图;
      然后提交资料,等待客服处理。

    2

    约4个工作日后,原来的客服继续回电。对方提示案例正在进展。手机需要操作恢复出厂设置后,重新执行激活操作,操作完成后回邮件并等待下一步通知。

    3

    继续等待2个工作日后,客服回电说案例已经解决。随后官网再查询,恢复正常了。这时候我又查了一遍GSX,结果如下:

    当前查询:苹果验机报告
    
    零件说明:IPHONE 11 PRO MAX MG 256GB-CHN
    型号:iPhone 11 Pro Max 256GB Midnight Green
    IMEI:===
    IMEI2:===
    序列号:===
    网络:Cellular
    类型:iPhone12,5
    激活状态:已激活
    激活日期:2019-09-20
    有效购买日期:是
    保修状态:Out Of Warranty (过保)
    保修结束日期:2020-09-19
    保修剩余:0天
    技术支持:已过期
    是否延保:否
    是否官换机:否
    已更换产品的序列号:否
    激活锁:开启
    ID黑白:Clean (白)
    运营商:Unlock
    网络锁:Unlocked (无锁)
    下次激活策略ID:10
    网络型号:A2220
    网络制式:全网通 (4G/3G/2G)
    产品类型:零售机
    购买地点:China
    AppleCare购买资格:无
    维修状态:无
    制造商:Foxconn
    生产日期:2019-09-09
    借出设备:否

    后记

    与客服对话期间,询问为什么会出现这种情况。对方询问是否使用了第三方软件(XX助手)用来查看部件的序列号或是闪存芯片种类保修期等信息,我一想还真是。对方表示有些软件会将这些信息上传,然后可能会被拿去骗保换机,苹果内部也在查这个事情。

  • (使用VirtualBox)构建Ubuntu下的clash旁路由服务

    效果图一张

    截屏2020-11-16 下午10.55.42.png

    几个月前写了一篇文章,讲怎么在Windows下面利用HyperV安装OpenWRT软路由。最近发现了几篇旁路由的文章,另外发现其实clash完全可以在ubuntu下面跑,不用再费那么多事情去研究OpenWRT了,于是乎就有了本文。

    目标

    本文的目标是利用一台Windows主机(或直接使用Linux主机)搭建一个旁路由服务器,这个服务器与我的光纤猫LAN口连接,提供网关服务,并且能够通过网页ui对clash服务进行配置修改。
    需要使用旁路由服务的终端(或无线AP),可以通过手工设定网关地址与DNS地址来接驳到旁路由上;同时,需要默认路由服务的终端,只需使用默认DHCP服务即可。
    施工完成后,家里的网络拓扑大致如下:

    光纤猫 192.168.1.1
    |- 192.168.1.21 旁路由服务器
    |- 192.168.1.20 无线路由器(接驳旁路由网关192.168.1.21)
    |                  |- 192.168.50.xxx 手机、iPad、笔电等终端
    |- 192.168.1.19 电视机(无需旁路由服务,使用DHCP的网关192.168.1.1)
    |- ...

    硬件需求

    任意主机,直接运行Ubuntu系统或使用Windows系统。服务器需要有一个网口。本人用来跑旁路由服务的主机是一台Intel J1900,4GB内存的主机。

    VirtualBox配置

    首先需要安装一个Ubuntu,我选择的是20.04LTS Server版本,所以没有图形化界面。

    在系统安装好之后,关闭虚拟机,需要在VirtualBox里配置网络接口类型。默认的网络接口类型应该是NAT,这边找到主机连接光猫的那个网络接口,改为桥接。桥接的意思是,把虚拟主机看做一个独立的主机接入光纤猫的LAN网络,于是乎他就有了一个独立于物理机器的一个“平等的”IP地址了。这个IP地址也就是旁路由的网关地址。
    VirtualBox-Bridge-Network.jpg

    如果你的光猫可以配置静态IP地址分配的白名单,建议把这个旁路由的MAC地址对应的IP分配成静态的,避免因为光纤猫DHCP变动导致旁路由的IP地址不是一个静态值。

    Ubuntu配置

    允许ip转发

    sudo vim /etc/sysctl.conf

    找到 net.ipv4.ip_forward=1 这一行,取消掉注释。如果没有找到的话直接新增一行也可以的。随后执行

    sudo sysctl -p

    使修改生效。

    配置iptables

    依次执行下面的指令,注意注释的部分,修改成自己的网络配置

    iptables -t nat -N clash
    iptables -t nat -N clash_dns
    
    # 这个是fake-ip对应的dns地址,一般不用动
    iptables -t nat -A PREROUTING -p tcp --dport 53 -d 198.19.0.0/24 -j clash_dns
    iptables -t nat -A PREROUTING -p udp --dport 53 -d 198.19.0.0/24 -j clash_dns
    iptables -t nat -A PREROUTING -p tcp -j clash
    
    # 这里需要注意的是,下面两行最后的 192.168.1.21 是当前旁路由的 IP 地址,请根据你自己的实际情况修改
    # 如果你自己的旁路由 IP 跟下面的 IP 地址不对的话会造成无法翻墙
    iptables -t nat -A clash_dns -p udp --dport 53 -d 198.19.0.0/24 -j DNAT --to-destination 192.168.1.21:53
    iptables -t nat -A clash_dns -p tcp --dport 53 -d 198.19.0.0/24 -j DNAT --to-destination 192.168.1.21:53
    
    # 绕过一些内网地址
    iptables -t nat -A clash -d 0.0.0.0/8 -j RETURN
    iptables -t nat -A clash -d 10.0.0.0/8 -j RETURN
    iptables -t nat -A clash -d 127.0.0.0/8 -j RETURN
    iptables -t nat -A clash -d 169.254.0.0/16 -j RETURN
    iptables -t nat -A clash -d 172.16.0.0/12 -j RETURN
    iptables -t nat -A clash -d 192.168.0.0/16 -j RETURN
    iptables -t nat -A clash -d 224.0.0.0/4 -j RETURN
    iptables -t nat -A clash -d 240.0.0.0/4 -j RETURN
    
    # 注意, 这边的7892对应后续clash配置里的redir-port
    iptables -t nat -A clash -p tcp -j REDIRECT --to-ports 7892

    执行完上面的 iptables 命令之后,就完成了旁路由的路由功能了,但是此时 iptables 并没有永久保存,下次开机上面的配置就会丢失。为了使得重启之后 iptables 命令仍然存在,我们需要安装软件来实现:

    sudo apt install iptables-persistent

    安装的过程中会提示你是否需要保存 iptables 配置,直接选是就行。这时候即使电脑重启了也会应用这些路由规则。

    如果后面你有需要重新修改 iptables 的配置,那么只需要在执行完 iptables 之后再执行:

    sudo iptables-save > /etc/iptables/rules.v4

    即可将最新的 iptables 规则保存下来。

    下载Clash

    # 下载最新版 clash,注意根据自己的系统下载对应的版本,我的是 64 位的,所以下载的是 linux-amd64 这个版本
    wget https://github.com/Dreamacro/clash/releases/download/v1.3.0/clash-linux-amd64-v1.3.0.gz
    # 解压并且把二进制文件放到 /usr/bin ,并且加上可执行权限
    gzip -d clash-linux-amd64-v1.3.0.gz
    sudo mv clash-linux-amd64-v1.3.0 /usr/bin/clash
    sudo chmod +x /usr/bin/clash
    # 为 clash 添加绑定低位端口的权限,这样运行 clash 的时候无需 root 权限
    sudo setcap cap_net_bind_service=+ep /usr/bin/clash

    DlerCloud用户需要注意,这边要下载Premium tag的版本以支持rule-set,否则启动会报配置错误。下载地址在https://github.com/Dreamacro/clash/releases/tag/premium。有兴趣了解此中区别的可以参考这篇Issue.

    配置clash服务

    运行 clash 之前需要先创建配置文件,否则 clash 无法启动:

    # 创建文件夹
    mkdir -p ~/.config/clash
    cd ~/.config/clash
    # 创建配置文件
    touch config.yaml
    vim config.yaml

    在 config.yaml 文件里面填入下面的配置:

    # 以下部分不要修改!
    port: 7890
    socks-port: 7891
    redir-port: 7892
    allow-lan: true
    
    mode: Rule
    
    log-level: info
    # external-controller 主要是用于 web 端管理页面,必须监听在 0.0.0.0
    external-controller: 0.0.0.0:9090
    
    # secret 是进入管理面板所需要的密码,可填可不填,建议填上
    secret: "secret-password"
    
    # external-ui 表示管理面板的路径
    external-ui: dashboard
    
    dns:
      enable: true
      ipv6: false
      listen: 0.0.0.0:53
      enhanced-mode: fake-ip
      fake-ip-range: 198.18.0.1/16
      nameserver:
        - '192.168.1.1'
    
    # 下面部分则是代理的设置跟规则的设置,这里忽略不写。
    Proxy:
    #...

    实际操作中,如果遇到报错说53端口已经被占用了,可以把0.0.0.0这个通配的IP地址修改为主机所在的内网IP,对应我本人的情况就是192.168.1.21:53.
    [更正] 这边最好不要改0.0.0.0,不然可能会遇到本机DNS出错的问题,一个办法是改用root用户启动服务.

    如果需要校验配置文件是否合规,可以运行clash -t, 成功的话程序会输出”successful”字样。

    (选修)DlerCloud配置文件自动下载及覆盖

    一些使用DlerCloud的朋友可以拿到官方配置好的clash yaml,这边有个小的python脚本能够帮你自动下载原始的yaml然后覆盖一些配置,使得它能在服务器上的clash客户端使用。

    import os
    import subprocess
    import shutil
    import sys
    import yaml
    
    g_dler_config_url = '这边填入能够下载到yaml文件的网址'
    
    g_items_to_update = {
        'port': 7890,
        'socks-port': 7891,
        'redir-port': 7892,
        'external-controller': '192.168.1.21:9090',
        'external-ui': 'dashboard',
        'secret': 'THE_HOLY_PASSWORD',  # 填写你的clash网页端管理密码
        'dns': {
            'enable': True,
            'ipv6': False,
            'listen': '192.168.1.21:53',
            'enhanced-mode': 'fake-ip',
            'fake-ip-range': '198.18.0.1/16',
            'nameserver': [
                '192.168.1.1',
            ],
        },
        'log-level': 'warning',
    }
    
    g_keys_to_remove = [
        'mixed-port',
    ]
    
    def is_root():
        if sys.platform == 'linux':
            return os.geteuid() == 0
        else:
            raise ValueError('not linux!')
    
    def update_yaml_and_save(input_path, output_path):
        with open(input_path, encoding='utf-8') as f:
            y = yaml.load(f, Loader=yaml.FullLoader)
        y.update(g_items_to_update)
        for key in g_keys_to_remove:
            del y[key]
        with open(output_path, 'w', encoding='utf-8') as f:
            yaml.dump(y, f, allow_unicode=True)
    
    if __name__ == '__main__':
        # NOTE: 这边的文件路径按需修改
        input_path = '/home/ubuntu/Download/dler_orig.yaml'
        subprocess.run(['wget', g_dler_config_url, '-O', input_path])
        output_path = '/home/ubuntu/.config/clash/config.yaml'
        shutil.copy(output_path, output_path + '.bak')
        update_yaml_and_save(input_path, output_path)
        print('saved to %s, restarting service...'%output_path)
        subprocess.run(['sudo', 'systemctl', 'restart', 'clash.service'])
        print('done')
    

    配置clash网页前端

    这边选用第三方开发的yacd面板,源码在https://github.com/haishanh/yacd
    具体安装方式就是把源码拷贝到上面配置文件中写好的位置:

    # 先进入到配置文件的目录
    cd ~/.config/clash
    # 下载前端代码压缩包,如果要使用官方的管理面板则把链接替换成 https://github.com/Dreamacro/clash-dashboard/archive/gh-pages.zip
    wget https://github.com/haishanh/yacd/archive/gh-pages.zip
    # 解压缩并且把目录名改成 dashboard
    unzip gh-pages.zip
    mv yacd-gh-pages/ dashboard/

    配置clash服务

    sudo touch /etc/systemd/system/clash.service
    sudo vim /etc/systemd/system/clash.service

    然后填入下面的内容。注意把2处YOUR USER NAME的部分换成你自己的用户名
    [更新] Ubuntu 18 实测如果用非root用户启动,会有DNS绑定低端口53失败的问题,最后我直接把下面的User改成User=root然后用sudo启了。不知道有没有更好的解法,希望大佬看到了能留个正解。

    [Unit]
    Description=clash daemon
    
    [Service]
    Type=simple
    User=YOUR USER NAME
    ExecStart=/usr/bin/clash -d /home/YOUR USER NAME/.config/clash/
    Restart=on-failure
    
    [Install]
    WantedBy=multi-user.target

    保存退出。然后启动clash服务并设置为开机启动:

    # 启动 clash
    sudo systemctl start clash.service
    # 启动开机自启
    sudo systemctl enable clash.service

    如无意外的话这时候 clash 已经运行起来了,通过浏览器访问 http://192.168.1.80:9090/ui 就能看到 clash 的管理面板了。

    运行Clash的机器本身无法联网?

    这是因为使用了fake-ip模式,任何DNS请求都会响应成我们的fake-ip.

    参考Ubuntu的代理配置,走的是bash分用户的配置文件。所以分别在自己用户和root下配置一个简单的function。打开~/.bashrc然后在最后加入以下内容:

    # proxyon
    proxyon() {
        export https_proxy=http://127.0.0.1:7890
        export http_proxy=http://127.0.0.1:7890
        export all_proxy=socks5://127.0.0.1:7891
        echo "HTTP/HTTPS Proxy on"
    }
    
    # proxyoff
    proxyoff() {
        unset http_proxy
        unset https_proxy
        unset all_proxy
        echo "HTTP/HTTPS Proxy off"
    }
    

    保存后source ~/.bashrc让配置文件生效。 当需要使用代理时直接执行proxyon即可。

    修改客户端的旁路由配置

    不论是什么系统,若要使用旁路由作为网关,只要修改以下项目:

    • 手动配置IP地址(关闭DHCP),主机的IP地址可以是照抄DHCP分配给你的那个,或者在同一网段里自选一个不冲突的,按文中例子是192.168.1.X其中X!=1 && X!=21;
    • 子网掩码255.255.255.0
    • 网关地址192.168.1.21,也就是虚拟机旁路由的IP地址;
    • DNS配置198.19.0.1,如果要填次要DNS,可以填198.19.0.2

    我把华为路由器接在了光猫的另一个LAN口(和旁路由平级),这边给一个华为路由器上的参考配置:
    router-manual-ip.jpg

    测速

    家里的宽带是300Mbps,无线路由器使用旁路由作为上层网关后,亲测性能上是没问题的。Speedtest为证:
    speedtest.jpg

    参考文献

  • ACR122U M1 IC卡 破解 复制 写卡软件 下载

    有些无良淘宝店,卖了设备居然还要收费卖软件,这软件又不是他家的版权,服了。有些无良网站,软件下载里面挂满了马,也是服了。

    (20231110更新链接)

    链接:https://pan.baidu.com/s/1-g1pTVGjKcjTFf0VtOIzWQ?pwd=nyjh
    解压密码:unique-ptr.com

    另: 某些可执行文件会可能被某些防病毒软件告警,请自行判断是否执行

    包含文件如下:

    │  IC卡复制操作教程.exe
    │  M1卡破解和复制教程(屏幕录像).exe
    │  中文破解软件说明.exe
    │  
    └─中文破解软件
        │  ACR122U写卡软件_T.exe
        │  clrear.dump
        │  dumpfile 23cead19 (2020-11-07 13_00_37) 1K.dump
        │  Dump转txt.exe
        │  EasyKey.exe
        │  IC卡复制克隆说明书.doc
        │  MiFare Card Programming625版本2.0.20.exe
        │  msvcp100.dll
        │  msvcr100.dll
        │  nfc.dll
        │  NFC读卡器.exe
        │  PCSC 123.exe
        │  RICHTX32.OCX
        │  TTHexEdit.exe
        │  UID克隆卡专用软件.exe
        │  winhex.exe
        │  二代超级卡.zip
        │  初始化错误.rar
        │  复制软件.exe
        │  特种卡写卡工具.exe
        │  破解软件.exe
        │  
        ├─ACR122U驱动
        │  │  32位驱动.zip
        │  │  64位驱动.zip
        │  │  
        │  └─32位
        │      │  ACR122U_MSI_Winx86_1120_P.zip
        │      │  
        │      └─ACR122U读写器驱动
        │          │  ReadMe.txt
        │          │  Setup.exe
        │          │  Setup.ini
        │          │  
        │          ├─redist
        │          │      InstMsiW.exe
        │          │      
        │          └─x86
        │                  ACR38_100_122_PCSC_Driver.msi
        │                  Arabic.mst
        │                  Chinese (Simplified).mst
        │                  Chinese (Traditional).mst
        │                  Czech.mst
        │                  Danish.mst
        │                  Dutch.mst
        │                  Finnish.mst
        │                  French.mst
        │                  German.mst
        │                  Greek.mst
        │                  Hebrew.mst
        │                  Hungarian.mst
        │                  Italian.mst
        │                  Japanese.mst
        │                  Korean.mst
        │                  Norwegian.mst
        │                  Polish.mst
        │                  Portuguese (Brazil).mst
        │                  Portuguese (Portugal).mst
        │                  Russian.mst
        │                  Spanish.mst
        │                  Swedish.mst
        │                  Turkish.mst
        │                  Ukrainian.mst
        │                
        │      
        ├─NFCGUI UID破解软件
        │  └─NFCGUI_Free
        │      ├─NFCGUI_Free
        │      │  │  .DS_Store
        │      │  │  libnfc.dll
        │      │  │  libusb0.dll
        │      │  │  nfc-anticol.exe
        │      │  │  nfc-dep-initiator.exe
        │      │  │  nfc-dep-target.exe
        │      │  │  nfc-emulate-forum-tag2.exe
        │      │  │  nfc-emulate-forum-tag4.exe
        │      │  │  nfc-emulate-tag.exe
        │      │  │  nfc-emulate-uid.exe
        │      │  │  nfc-list.exe
        │      │  │  nfc-mfclassic.exe
        │      │  │  nfc-mfsetuid.exe
        │      │  │  nfc-mfultralight.exe
        │      │  │  nfc-poll.exe
        │      │  │  nfc-probe.exe
        │      │  │  nfc-read-forum-tag3.exe
        │      │  │  nfc-relay-picc.exe
        │      │  │  nfc-relay.exe
        │      │  │  NFCGUI_FREE_v0.3.exe
        │                                  
        ├─二代超级卡
        │  │  MFOC_GUI.exe
        │  │  nfc.dll
        │  │  Sniffer_Card.exe
        │  │  二代超级卡使用视频.zip
        │          
        └─初始化错误
                vcredist_x86(1).exe
                vcredist_x86(2).exe
  • [C++] 在linux或windows上使用direct io

    首先

    Direct IO是一种不用内核缓存的IO, 它可以做到直接将用户空间的内存直接写入磁盘或者将磁盘数据直接读到用户空间的缓冲区,这种策略就是不用内核的缓存而使用用户自己设计的缓存. 需要注意的是,使用DirectIO会完全绕过系统的预取(prefetch)以及页缓存机制,如果不是必须,那么我认为还是优先考虑普通的read或者直接mmap吧

    Linux

    几个方面注意一下就可以了

    • 在调用[open]1时,把O_DIRECT加上。比如int fd = open("/path/to/file", O_DIRECT, O_RDONLY);
    • 用于文件读写的buffer,必须和磁盘的块大小对齐(保守起见一般可以设为4KB)。有两种方法能拿到地址对齐的内存块:

      • 直接使用posix_memalign;
      • 直接new一段内存,然后根据返回的内存地址,往后找到第一个满足对齐要求的地址就可以。这种方法会浪费前面一段空间,不过其实posix_memalign在系统操作的时候”浪费”了;
      • 借用mmap申请MAP_ANONYMOUS匿名映射,addr参数填NULL的话mmap出来的地址是页对齐的(至少是4K对齐),所以可以直接拿来用;
    • 如果需要lseek之类的操作,注意seek的文件位置偏移量必须是磁盘块大小的整数倍;
    • 2.4内核下, 每次文件读写的长度必须是块大小的整数倍(e.g. N * 4KB). Linux 2.6.0+无此要求;

    Windows下

    参考这篇文章,可以知道Windows下也可以启用类似的机制,对应的打开文件flag是FILE_FLAG_NO_BUFFERING.
    用法类似,与Linux不同的地方在于:

    • CreateFileA(...)调用的dwFlagsAndAttributes参数里把FILE_FLAG_NO_BUFFERING填上;
    • 用于文件读写的buffer也是需要对齐的

      • 也可以使用类似的方法_aligned_malloc申请对齐的内存,但是必须注意要使用_aligned_free释放内存,否则runtime error;
  • [洋垃圾专业户] 夏普Aquos R2 (SHV42)

    于2020年秋天购入了一款洋垃圾,白色的夏普Aquos R2 (SHV42)。二手货还是比较便宜的,这款手机用了六百多块钱。

    基本参数

    • CPU: 高通骁龙845
    • 内存(RAM): 4GB
    • 内置存储(ROM): 64GB(UFS),支持插TF卡
    • 相机:2260万像素(主摄像头),1630万(副摄像头)
    • 系统:出厂Android 8,官方升级至Android 10
    • 重量:約181g
    • 网络支持:单卡。电信4G(VoLTE), 移动2/4G,联通4G
    • 屏幕:6.0吋 WQHD+ (1,440 × 3,040像素)IGZO 100HZ 高刷LCD屏
    • 电池容量:3130mAh
    • 快充支持:Quick Charge 3.0 / USB Power Delivery
    • 中文支持:原生中文支持

    印“相”

    微信图片_20201101155220.jpg
    微信图片_20201101155230.jpg
    微信图片_20201101155239.jpg
    (防窥模式)

    上手感觉

    • 机器重量适中,握持感还可以,就是厚度有点点厚。指纹识别位于机身下部,形成了一个下巴有那么一点尴尬,识别速度很快;
    • 屏幕素质一流。虽然是二手机器,但是目测没有发现什么斑点。LCD屏的细腻程度是相当不错的,细看的话可以轻松秒杀手持的iPhone 12 Pro,但是亮度没有OLED屏高。这个机器最大的卖点估计是屏幕,但是我觉得这个挖孔真的是够碍眼。另外这个屏幕支持防窥模式,见上面第三张图,主要就是侧面看的时候能够看到很多图案;
    • 摄像头:中规中矩,没什么特色;
    • 响应速度:配合谷歌全家桶,响应速度还是很不错的。4GB的RAM只要不装太多国产渠道的App,没有发现不够用。但是由于是买来玩的机器,没有完全覆盖日常使用的场景;另外Android10给我的感觉挺不错,全屏幕手势的支持也比较到位;
    • 续航:续航有点不大够用,不知道是这个二手机的电池本身已经不大行了还是怎么的,屏幕开启(高刷)的情况下耗电还是比较厉害的,估计是一天两充的水平。
  • Boost Python的C++对象, Pickle支持及其原理

    默认用boost python包裹的C++对象是不支持pickle的,如果要用pickle.dumps(obj)的话那会提示错误

    Pickling of "xxx" instances is not enabled.

    这边吐槽一下在最新的代码里,给的reference链接其实还是不可用的。真正正确的是https://www.boost.org/doc/libs/1_74_0/libs/python/doc/html/reference/topics/pickle_support.html

    让你的class支持Pickle协议

    若要让你的C++ Class支持Pickle协议,比较“正统”的方法是利用boost提供的boost::python::pickle_suite. 拿代码说话:

    struct world_t {
        world_t(const string& country) { ... }
    };
    
    struct world_pickle_suite : boost::python::pickle_suite
      {
        static
        boost::python::tuple
        getinitargs(const world_t& w)
        {
          // [可选实现] 返回一个boost::python::tuple元组,其值被用来构造
          // 如果一个类的构造函数**不需要参数**的话,可以不用重载这个方法。
          return boost::python::make_tuple(w.country());
        }
    
        static
        boost::python::tuple
        getstate(const world_t& w)
        {
          // [可选实现] 如果对象的构造函数并不能完全恢复对象的状态,
          // 那么要用此函数返回其状态值
        }
    
        static
        void
        setstate(world_t& w, boost::python::tuple state)
        {
          // [可选实现] 如果对象的构造函数并不能完全恢复对象的状态,
          // 那么要用此函数把state里的状态恢复到w
        }
      };
    
    boost::python::class_<world_t>("world", args<const std::string&>())
      // 定义world_t的boost python包装类
      .def_pickle(world_pickle_suite());
      // ...
    

    需要注意的是,如果在suite里定义了getstate/setstate并且这个类的__dict__属性非空,boost是会报错的。一种可能的情况是你包装的类是一个子类,这时候还需要自己实现__getstate_manages_dict__属性。这边不赘述,可参考这里

    原理简释

    总所周知,Python3里若要在自定义的类里实现可Pickle,至少需要:

    • 它的__dict__属性是可pickle的,或;
    • 调用__getstate__()拿到的返回值是可pickle的,随后会调用__setstate__()方法在unpickle的时候恢复状态.
      但在这些函数的背后,再稍微底层一点其实是通过__reduce__()方法实现的(更严格一点,它会先去寻找__reduce_ex__()是否可用)。这个方法简单来说就是返回一个tuple, 这个tuple有从构建对象到恢复状态所需的各种元素.

    In fact, these methods are part of the copy protocol which implements the __reduce__() special method.
    https://docs.python.org/3/library/pickle.html

    所以boost::python其实自己实现了一个对象的__reduce__()方法,在src/object/pickle_support.cpp里(源文件在这)。主要做了几件事

    1. 拿到当前对象的__class__属性(也就是class object);
    2. 检查对象是否有__safe_for_unpickling__属性。如果没有的话,就是本文最早提到的报错了;
    3. 检查对象是否有__getinitargs__()方法,若有则取值。没有的话,unpickle的时候按照无参数构造来处理;
    4. 检查对象是否有__getstate__()方法,若有则取值;
    5. 检查对象是否有__dict__属性,若有则会检查是否有__getstate_manages_dict__属性并获取这两个属性的值。
    6. 返回tuple(),内容依次为1-5里拿到的值。

    可以看到,这个__reduce__()方法是遵循了Python里的object.__reduce__()协定的。当然了,如果某些使用者觉得继承一个suite类都觉得麻烦或者达不到自己的需求,也可以通过其他手段完成,只要记得自己整一个__reduce__()方法,只需满足Python的协定即可.

    class_obj.attr("__reduce__") = boost::python::object(/*YOUR REDUCE FUNCTION*/);

    再深一点~

    如果你跟我一样无聊,那就来看看cpython自己想要__reduce__()的时候是怎么用到__getstate__()的吧.

    cpython/Objects/typeobject.c这边有个函数叫

    static PyObject* _common_reduce(PyObject *self, int proto)

    ,在pickle protocol>2时会调用`static PyObject *
    reduce_newobj(PyObject *obj)`方法,这个方法是Python里大多数默认对象的默认__reduce__逻辑。其中有一句

    state = _PyObject_GetState(obj,
                    !hasargs && !PyList_Check(obj) && !PyDict_Check(obj));

    _PyObject_GetState里大致是从__dict__或者__slots__属性去获取对象的状态。

    这边再往上一些还能看到有调用_PyObject_GetNewArguments()的,里面的逻辑就会去拿__getnewargs_ex__或是__getnewargs__属性的值作为__new__一个新对象时候的传入参数。这两个函数是Pickle协议文档里介绍的四个推荐覆盖的“高阶”函数之一。与之对应,Python并不希望一般人去自己写一个__reduce__()出来.

    看到这里,再回想一下boost::python,pickle_suite里面的getinitargs()getstate()就分别对应到__getnewargs__()__getstate__()

  • Apache2配置微信公众号的反向代理

    题外话

    迫于妹子生日要到了,今年手头又比较紧,所以打算做个微信公众号的小东西骗骗她😜
    目前跑网站的服务器虽然配置不咋地,但是服务器上也就一个typecho的php项目要跑,平时访问量也门可罗雀,所以打算利用原有的服务器搭个反向代理到微信公众号的服务端。

    目标

    主域名example.com:

    • 80/443端口直接到原有的网站

    子域名wechat.example.com

    • 80端口反向代理到本地服务http://localhost:8766/

    实作

    其实很简单,新建一个VirtualHost即可。
    创建一个/etc/apache2/sites-available/wechat.conf内容如下:

    <VirtualHost *:80>
            ServerName wechat.example.com
    
            ServerAdmin webmaster@localhost
    
            ProxyPass / http://localhost:8766/
            ProxyPassReverse / http://localhost:8766/
        
    
            ErrorLog ${APACHE_LOG_DIR}/error.log
            CustomLog ${APACHE_LOG_DIR}/access.log combined
    </VirtualHost>
    

    其中的关键是ProxyPass以及ProxyPassReverse.

    完成之后,用a2ensite wechat启用这个VirtualHost, 再systemctl reload apache2便万事大吉。

  • 记录一下找了半天的huge page坑——fork越来越慢的原因

    背景

    之前发现Jupyter Notebook下面,如果数据占用多的话,开多进程池会特别的慢。一开始以为是Python的锅,但是把multiprocessing.pool改成直接用os.fork()调用以后,问题依旧。照理来说unix下面使用fork开进程,会启用copy-on-write机制,内存增长并不是特别明显,但是实际在htop下面看内存仍然会在fork之后增长,并且和进程数量是线性相关的。

    原因

    随后想了老半天,想到了可能和页表有关系。查了一下,跑的服务器上huge page确实被禁用了(不知为何…).

    fork的机制简单地说,是在创建新进程的时候把老的进程控制块(Process Control Block)里内存页表拷贝给了新的PCB——这边具体内存的信息是不拷贝的。由于当时Notebook跑的数据处理任务,里面已经用了不少内存(100GB+),所以拷贝的时候如果用默认的4KB内存页,将会有100 * 1024 * 1024 / 4 = 104,857,600个页表! 按典型一个页表项(Page Table Entry)大小4Bytes计算,一个进程开出来光页表会耗400MB内存.

  • 中国电信(澳门) 漫游流量卡 大湾区预付卡 测评

    ctmacao_prepaid.jpg

    主要是看到了“不过期数据包”的噱头,这边顺便就买了一张中国电信(澳门)的漫游流量卡。注意一下这边评测的是预付费,不是上台卡,尽管学生卡近期特别流行,不过一想手头的联通香港卡还在协议期,就不折腾了,万一毁约了不大好。

    激活

    大陆地区可以激活的,不过当时实验的结果最好在CDMA模式下(关闭4G)或者随便打个电话出去就能收到要你实名激活的消息了。随后就是微信上实名制验证,用身份证即可,验证之后会让选一个流量套餐,这时候选那个单价最便宜的只能买一次的包就行,因为后面再也没机会选到那么便宜的包了~

    不过期数据包

    最吸引人的地方。具体套餐如下:
    ctm_prepaid_data_package.png

    网速(限速)

    是的,我体验到限速了。下面两张图是本地的电信卡和ctm卡的测速比较,使用Galaxy A90 5G手机测速:
    ctm_speedtest.jpg

    chinatelecom_speed.jpg

    测速的信号不是特别好,不过依然能看出来区别。这个就跟联通香港的上台卡形成了区别,要知道CuniqHK的上台卡在大陆漫游可是有5G权限的,4G下面也有满速可以跑。
    但是这边是预付费卡,不确定上台卡情形如何。

  • 苹果的生态系统,有替代品吗——我的“离场成本”

    苹果公司的厉害之处,从大学毕业用到现在,感觉有那么两个点

    1. 恰到好处的软硬件结合,一点不多一点不少
    2. 完善的生态环境,这包括软件生态和硬件生态

    在我这个产品设计的外行来看,这两个是相辅相成的。第1点是保障产品设计出来既能得到消费者欢迎,又能节约成本。产品线的设计(包括同款产品不同的子品牌以及规格配置)都做过严苛的调查,保证最大程度榨取利润。第2点最大化了用户粘性和“离场”成本,并且能逐渐增加用户在苹果产品上的消费水平——例如越来越不够用的iCloud空间。

    而且我越来越觉得设备只是平台,接入各种服务把你绑定得死死的平台。

    鄙人目前的苹果设备以及服务:

    • 手机若干: 主力机,车上导航的机器
    • 平板若干:主力平板,给儿子放歌的平板,给老婆看剧的平板
    • 笔记本一台:非主力,因为好奇macOS买的
    • 手表一块:基本日常携带,主要就是看时间然后算“运动”
    • iCloud:基本上我的照片都在上面
    • Apple Music:一般性使用
    • Airpods: 一直在用

    那么我的“离场成本”是什么,有什么能够替代呢?

    手机:【难度★★★★★】主力机我尝试过换成三星(当时的Note10旗舰)、华为的(当时的P30Pro旗舰)。三星当时的感觉是都中规中矩,拍照其实已经很好了,后来换回苹果的原因是觉得还是有点卡;P30pro当时直接买的顶配(并且老婆大人也有一台),后来替换的原因是觉得屏幕素质太差了。这两台印象中,各用了半年。此外最不能忍的是安卓的通知系统,有人会说你挂个代理然后走FCM推送呀~怎么说呢,还是有一些App会让我很不爽。手机还有一个问题是我的相册,从iCloud导出再导入是一种解法,不过当时耗了我大半天时间先把iCloud照片的原件同步到电脑上然后再传到手机上….

    备用机倒是无所谓,三星的A90 5G也被我拿来当导航机子使过,只不过后来买了SE2没地方用,就放车上了…

    平板:【难度★★★】平板不是我的生产力工具,所以我只要屏幕好+看剧爽+偶尔能连个远程桌面就行了。这点其实我没什么粘性。为什么目前是苹果产品呢?因为懒得挑其他牌子了。

    笔记本:【难度★】这个最好“脱瘾”,因为我的真正生产力是在Windows本上。这个MBA现在是“有空临幸一下”的状态。

    手表【难度★★★】:其实三星也有Galaxy Watch可选,而且看同事的表也挺酷炫的,还有些运动狂人直接用专业的运动表——他们看不起AppleWatch的很,哈哈~ 但怎么说呢,苹果的运动三环,这个设计比较容易让人“上瘾”。我之前为了拿到2020年的全民运动日奖章,晚上8点去西湖骑了一趟…要知道自从小屁孩出生,我已经快半年没有骑行了。

    iCloud【难度★★★★】:我的备忘录,待办事项,日程,照片全在上面。诚然其他厂牌的预装程序里肯定有“从iPhone”转移一项,但是iCloud龌龊的地方就在于他会使绊子让你不能轻松地完全把数据转过去。比如,照片本地存储自动压缩的功能。

    Apple Music【难度★★】:我怀疑Android客户端做那么难用是故意的。另外最近发现Spotify免费又好用,再加上国区的Apple Music有稀奇古怪的缺歌,所以这个粘性不是很大。

    Airpods【难度★★★】:用过其他竞品,这又是一个软硬件结合的产物,有点难戒除

    目前觉得要换设备,只能先从手机开始“脱瘾”。且看今年苹果的新款是否有亮点把,Cook的模具用了那么多代也要换了?