2026年2月28日星期六

Received Postcrossing 122

 




Received Postcrossing 121

 





Received Postcrossing 120

 



Received Postcrossing 119

 



Received Postcrossing 118

 



Received Postcrossing 117

 



Received Postcrossing 116

 



Received Postcrossing 115

 





Received Postcrossing 114

 



Received Postcrossing 113

 



Received Postcrossing 112

 



Received Postcrossing 111

 





Received Postcrossing 110

 




2026年2月11日星期三

关于APUE第3版tell/wait函数的探讨

 今天我读完了APUE(第3版)一书的第10章信号,在做习题10.6的时候遇到了问题。这道题是要求用本章基于信号实现的tell/wait函数,实现父进程与子进程之间的同步,依次把一个数字加1。

出于偷懒的目的,我从APUE一书的官网下载了配套的源代码,这样我就不用把tell/wait函数敲进去了。我发现在下载的源代码的lib路径下有个tellwait.c。放在这里想必是作者对这个代码很有信心吧,将其作为了库函数。经比对,与书中的代码一致。

程序写好后,编译运行,程序跑了几下就停在那里了。我反复检查代码,感觉我写的没有问题。把代码提交给Gemini后,它发现代码存在如下问题。

1. tellwait.c中用signal()函数注册信号处理程序,正如这一章开头作者大讲的不可靠信号的问题。可是不知道为什么作者后面写的代码又用了不可靠的信号。

用signal()注册的SIGUSR1SIGUSR2信号处理程序,在执行一次之后需要重复注册,否则系统对于这个信号就采用默认处理方式了,也就是结束进程。我写的程序,子进程在处理了一次信号后,又一次收到信号,就直接退出了,所以永远不会TELL_PARENT(), 父进程也就永远停在了那里。

要解决这个问题,需要把signal()函数替换为sigaction().

2. WAIT_PARENT()和WAIT_CHILD()中,sigsuspend()返回后,调用sigprocmask()恢复了原来的信号屏蔽。所以如果再一次调用这两个函数,那就出现了书中讲的竞争问题及不可靠信号。

如果要反复调用这两个函数的话,就得把这两个WAIT_xxx函数对sigprocmask()的调用取消,这样SIGUSR1和SIGUSR2在整个程序运行中一直是屏蔽状态,只有sigsuspend()的时候才能接收SIGUSR1和SIGUSR2并处理。

综上,我认为作者在信号这一章比较靠后的位置,已经讲了信号的大部分知识后,写的这个tellwait.c是不严谨的。这些函数,只有第一次调用才好用,如果反复调用就会出问题,需要按我上面说的进行修改。

2026年2月10日星期二

Google Cloud上的FreeBSD支持IPv6指南

 我前几天突然想把我在Google Cloud上的的FreeBSD 15.0虚拟机开通IPv6,在初步咨询Gemini可行后,花了接近一天的时间,在大量咨询Gemini和DeepSeek以及很多资深FreeBSD用户后才成功。我差点都要放弃了。特把主要步骤和遇到的坑记录一下,以方便后人。

1. 先决条件

要使用Google Cloud的高等级网络才能开通IPv6. 这种网络比较贵,且每月不送流量。如果你使用的是标准网络,且不愿意改为高等级网络,就不用往下看了。


2. VPC网络配置

2.1 在VPC网络/VPC网络界面,将“子网创建模式”改为“自定义子网”;确保“最佳路径选择模式”为“旧式”,也就是1节里说的高等级网络。这种网络,数据包尽可能多在Google的内网中传输,少在ISP的网络中传输。而另一个选项恰恰相反。

2.2 在“子网”标签,将你的虚拟机所在的区域(如我的在us-west1),改为"IPv4和IPv6(双栈)", IPv6的权限类型为“External”。其他没有虚拟机的区域不需要理会。

2.3 在VPC网络/防火墙界面,新增一条防火墙规则,方向“入站”,对匹配项执行的操作“允许”,IPv6地址范围"::/0" (填写时不带引号), "指定的协议和端口"填写58(坑1,不支持填写ICMPv6字符串,只能用协议代码)。这一步对于后续虚拟机获取IP地址至关重要,不可省略。


3. 虚拟机配置

在Compute Engine/虚拟机实例界面,关闭虚拟机。然后修改虚拟机配置,网卡配置改为"IPv4和IPv6(双栈)"。然后启动虚拟机。


4. FreeBSD配置

4.1 虚拟机启动后,执行ifconfig, 应该可以看到虚拟机的网卡(默认名称vtnet0)上有了一个fe80开头的IPv6地址。出现这个地址说明3节的配置正确。但这个地址还不能对外通信。

4.2 执行

pkg install dhcp6

安装dhcp6这个软件。(坑2,FreeBSD即使写作本文时最新的15.0版本,自带的dhcp客户端dhclient也不支持IPv6. 而Google Cloud需要使用DHCP获取IPv6地址,我一开始之所以不成功,主要是不知道这个坑)。

4.3 创建一个/usr/local/etc/dhcp6c.conf配置文件,内容如下:

interface vtnet0 {

    send ia-na 0;

};

id-assoc na 0 { };

4.4 编辑/etc/rc.conf文件,增加如下内容:

dhcp6c_enable="YES"

dhcp6c_interfaces="vtnet0"

ifconfig_vtnet0_ipv6="inet6 accept_rtadv"

rtsold_enable=YES

4.5 重启FreeBSD系统

4.6 重启后,再次执行ifconfig, 应该可以看到虚拟机的网卡上多了一个2600开头的IP地址,此时在其他电脑上ping -6或ping6这个IP地址,应该就通了。


5. 其他注意事项

5.1 Google Cloud的防火墙对于IPv4和IPv6是分开设置的,如果你的FreeBSD做服务端,需要在防火墙上针对IPv6开放对应的端口,才能实现IPv6对这个端口的访问。

5.2 Google Cloud的标准虚拟机镜像Debian 13, 在按2~3节配置好后,不需要在Debian系统里做任何别的配置,就能ping通IPv6地址。以此可作为调试依据。用一个标准镜像创建一个虚拟机,如果能ping通IPv6地址,那就是FreeBSD配置的问题;反之则是Goolge Cloud配置的问题。

5.3 如果对本文还有其他问题,欢迎在评论区留言。也可以咨询AI.在这个问题上,DeepSeek比Gemini回答得更好。

井盖图录222——北京电视, TV, 2007