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