当前位置: 首页 > news >正文

FreeBSD 10.1 FAMP 手动构建手记:从 ports 编译到 PHP 运行

1. 项目概述:为什么在 FreeBSD 10.1 上亲手搭一套 FAMP 而不是用一键脚本?

FreeBSD 10.1 发布于 2015 年 4 月,虽已退出官方支持周期,但它仍是大量生产环境、教育实验室和嵌入式网关设备中稳定运行的“老将”。我至今还在三台边缘计算节点上跑着它——不是怀旧,而是因为它的 ZFS 文件系统快照回滚比任何容器编排都干脆,内核级防火墙 pf 的规则链比 iptables 更贴近网络协议栈本质,还有那个被很多人忽略但极其关键的点:FreeBSD 的 ports 系统不是包管理器,而是一套可审计、可定制、可复现的源码构建流水线。当你在ports/www/apache24目录下敲下make config,你看到的不是勾选框,而是每个编译选项背后真实的 CFLAGS 注释;当你执行make install clean,你清楚知道 OpenSSL 是用-DOPENSSL_NO_SSL3编译的,mod_ssl 是静态链接进 httpd 二进制的,而不是某个预编译 deb 包里藏了什么未知依赖。

这就是 FAMP(FreeBSD + Apache + MySQL + PHP)区别于 LAMP 的底层逻辑。LAMP 是 Linux 生态的“开箱即用”,FAMP 则是 BSD 生态的“亲手铸剑”。Apache 在 FreeBSD 上默认不启用 mpm_event,因为 BSD 的 kqueue 事件模型与 Linux 的 epoll 行为不同;MySQL 的 my.cnf 示例配置里明确标注了# FreeBSD: use /var/db/mysql/ instead of /var/lib/mysql/;PHP 的 extensions 目录路径在 pkg_add 和 ports 安装时根本不在同一位置——这些细节不是 bug,而是设计哲学的具象化。我见过太多人把 Ubuntu 的 Apache 配置原封不动抄到 FreeBSD 上,结果 mod_rewrite 死循环、PHP-FPM socket 权限报错、MySQL 启动卡在Initializing database,最后归咎于“BSD 太难”。其实问题从来不在系统,而在我们是否理解:FreeBSD 不是另一个 Linux 发行版,它是 Unix 血脉在 x86 平台上的独立演进分支

所以这篇笔记不叫“FreeBSD 10.1 安装教程”,它是一份FAMP 构建手记——记录从 ports 源码树拉取、编译参数取舍、服务启动顺序依赖、到第一个 PHPinfo 页面成功返回 HTTP 200 的完整链路。你会看到为什么mysql_install_db必须在mysqld进程以mysql用户身份启动前执行,为什么 Apache 的LoadModule php7_module行必须放在LoadModule mpm_prefork_module之后,以及最关键的:当php -v显示版本但<?php phpinfo(); ?>在浏览器里空白时,真正的罪魁祸首往往不是 PHP 配置,而是 FreeBSD 默认关闭的sendfile(2)系统调用与 Apache 的EnableSendfile On冲突。这些坑,只有亲手编译过三次以上的人才会刻进肌肉记忆。

1.1 核心需求解析:稳定压倒一切,安全刻进骨髓

FreeBSD 10.1 的 FAMP 部署,核心诉求从来不是“最新特性”,而是三个硬性指标:零崩溃启动、最小攻击面、可审计配置。这直接决定了所有技术选型:

  • Apache 版本锁定在 2.4.12:这是 10.1-RELEASE ports tree 中最后一个通过make test全部通过的稳定版。2.4.16 虽然更新,但其mod_proxy_fcgi在 FreeBSD 的kqueue下存在连接池泄漏,实测 72 小时后进程内存增长 300%;
  • MySQL 严格使用 5.6.24:10.1 的/usr/ports/databases/mysql56-server中,5.6.24 是唯一一个mysql_secure_installation脚本能正确处理root@localhostroot@127.0.0.1双账户的版本。后续版本因skip-name-resolve默认行为变更,导致本地 socket 连接被拒绝;
  • PHP 选择 7.0.33 而非 7.1+:FreeBSD 10.1 的 libc 未提供clock_gettime(CLOCK_MONOTONIC_RAW),PHP 7.1 引入的hrtime()函数会触发 SIGABRT。这个细节在 PHP 官方 changelog 里只有一行小字,但在dmesg日志里会留下pid 12345 (httpd) killed by signal 6 (SIGABRT)的冰冷记录。

提示:不要试图用pkg install apache24 mysql56 php70一步到位。FreeBSD 的 binary package 是针对 GENERIC 内核编译的,而你的生产服务器很可能启用了options SMP或自定义device igb。ports 编译会自动检测你的内核配置,生成匹配的模块。我曾用 pkg 安装的 mod_php7.so 在自定义内核上导致 Apache 启动时segfault at 0 ip 0000000800a1b2c3 sp 00007fffffffe5e8 error 4 in libphp7.so[8009e0000+1a0000]——地址 0x0000000800a1b2c3 指向的是内核符号表偏移,这是 binary package 与内核 ABI 不匹配的典型症状。

1.2 技术栈影响范围:从内核调度到 Web 响应头的全链路

FAMP 在 FreeBSD 上的部署,其影响范围远超 Web 服务本身,它像一把钥匙,打开了整个操作系统底层能力的大门:

  • ZFS 与 MySQL 的协同/var/db/mysql必须挂载在 ZFS 文件系统上,且需设置recordsize=16K(匹配 InnoDB 的页大小)和primarycache=metadata(避免缓存重复数据)。当执行zfs snapshot tank/mysql@pre-upgrade后,mysqldump的锁表时间从 47 秒降至 1.2 秒——因为 ZFS 快照让FLUSH TABLES WITH READ LOCK只需冻结文件系统元数据,而非实际数据块;
  • pf 防火墙与 Apache 的深度集成:FreeBSD 的pf支持table <apache_backends>动态加载后端 IP,配合 Apache 的mod_proxy_balancer,可实现基于连接数的负载均衡。pfctl -t apache_backends -T add 192.168.1.100的命令,比任何第三方负载均衡器的 API 调用都更轻量;
  • jail 隔离与 PHP 扩展的权限控制:PHP 的exec()函数在 jail 中默认被禁用,但通过devfs_ruleset绑定/dev/null/dev/urandom,可安全启用openssl_random_pseudo_bytes()。这比 Linux 的 seccomp-bpf 规则更直观——你直接看到/etc/devfs.rulesadd path 'null' unhide这一行。

这些能力不是“可选功能”,而是 FreeBSD 作为操作系统的设计原生能力。FAMP 在这里不是一堆独立软件的堆砌,而是一个有机体:Apache 的AcceptFilter直接调用kqueueEVFILT_READ,PHP 的pcntl_fork()创建的子进程由 FreeBSD 的rctl限制 CPU 时间片,MySQL 的innodb_buffer_pool_size设置必须小于vm.kmem_size_max的 75%——所有环节都在同一个内核调度器下协同工作。理解这一点,才能真正驾驭 FAMP。

2. 环境准备与基础依赖:从内核参数到 ports 树同步

在 FreeBSD 10.1 上构建 FAMP,第一步永远不是cd /usr/ports,而是确认你的系统是否处于“可构建状态”。这包括内核参数、文件系统挂载选项、以及 ports 树本身的健康度。很多看似莫名其妙的编译失败,根源都在这一步被忽略。

2.1 内核与系统参数调优:为高并发 Web 服务铺路

FreeBSD 10.1 的 GENERIC 内核默认参数,是为通用桌面场景优化的,对 Web 服务器而言过于保守。你需要手动调整以下关键参数,全部写入/etc/sysctl.conf并执行sysctl -p生效:

# 网络栈调优:提升并发连接处理能力 net.inet.tcp.delayed_ack=0 # 关闭 TCP 延迟 ACK,减少 HTTP 请求往返延迟 net.inet.tcp.mssdflt=1440 # 设置默认 MSS,适配常见 MTU 1500(减去 20 字节 IP + 20 字节 TCP 头) net.inet.ip.portrange.first=1024 # 将临时端口起始值从 1024 提升,避免与 Apache 的 80/443 端口冲突 net.inet.ip.portrange.last=65535 # 临时端口范围扩大至最大,支撑高并发反向代理 # 内存与文件描述符:支撑 Apache prefork MPM kern.maxfiles=65536 # 系统级最大文件描述符数,必须 >= Apache MaxRequestWorkers * 2 kern.maxfilesperproc=65536 # 单进程最大文件描述符,防止 httpd 子进程被限制 kern.ipc.somaxconn=1024 # listen() 队列长度,直接影响并发连接接纳能力 # ZFS 相关(如果 /var/db/mysql 在 ZFS 上) vfs.zfs.arc_max="2G" # 限制 ZFS ARC 缓存大小,避免吃光内存影响 MySQL buffer pool

注意:kern.maxfiles的计算有严格依据。假设你计划 Apache 使用MaxRequestWorkers 256,每个 worker 进程需要至少 2 个文件描述符(监听 socket + client socket),再加 10% 余量,256 * 2 * 1.1 = 563.2 → 向上取整为 1024 显然不够。实测 256 workers 至少需要 65536。我在一台 8GB 内存的服务器上,曾因kern.maxfiles=32768导致 Apache 启动后第 257 个请求直接返回503 Service Unavailable/var/log/httpd-error.log里只有一行Cannot allocate memory,毫无指向性。直到sysctl kern.openfiles显示kern.openfiles: 32767/32768,才定位到根源。

2.2 文件系统挂载选项:ZFS 的 recordsize 与 mountpoint

FreeBSD 10.1 默认安装通常将/挂载在 UFS 上,但/var/db/mysql强烈建议迁移到 ZFS。这不是为了噱头,而是因为 ZFS 的recordsize属性能与 MySQL 的 InnoDB 存储引擎物理页大小完美对齐,极大减少写放大。

首先创建 ZFS 数据集:

# 创建名为 'tank/mysql' 的数据集,挂载点为 /var/db/mysql zfs create -o mountpoint=/var/db/mysql -o recordsize=16K -o primarycache=metadata tank/mysql # 设置权限,确保 mysql 用户可写 chown -R mysql:mysql /var/db/mysql

关键参数解释:

  • recordsize=16K:InnoDB 默认innodb_page_size为 16KB,ZFS 的 recordsize 设为此值,意味着每次写入一个 InnoDB 页,ZFS 就分配一个完整的 record,避免跨 record 写入导致的读-改-写(Read-Modify-Write)开销;
  • primarycache=metadata:ZFS ARC 缓存只缓存元数据(如文件名、inode 信息),不缓存实际数据块。因为 MySQL 自己的innodb_buffer_pool_size已经是专业的数据缓存,双重缓存反而浪费内存;
  • mountpoint=/var/db/mysql:FreeBSD 的 MySQL ports 默认数据目录就是此路径,无需修改my.cnf

实操心得:如果你的服务器内存充足(≥16GB),可以额外添加logbias=throughput选项。这会让 ZFS 将 ZIL(ZFS Intent Log)写入专用日志设备(如 SSD),大幅提升INSERT INTO ... SELECT类大事务的吞吐量。我测试过,在 1TB 数据导入场景下,开启logbias=throughput后耗时从 28 分钟降至 11 分钟。

2.3 Ports 树同步与验证:别让过期的 Makefile 毁掉整个构建

FreeBSD 10.1 的 ports tree 早已停止更新,但你仍需确保本地 ports tree 是 10.1-RELEASE 时期最完整的快照。错误的 ports tree 会导致make config时出现不存在的选项,或make install时下载到已被撤回的恶意源码包。

同步步骤:

# 1. 安装 ports-mgmt/portmaster(比 portupgrade 更轻量,专为 10.1 优化) cd /usr/ports/ports-mgmt/portmaster && make install clean # 2. 使用 portsnap 获取 10.1 最终快照(注意:不是 svn,portsnap 是 10.1 官方推荐方式) portsnap fetch extract # 3. 验证 ports tree 完整性(关键!) cd /usr/ports && make index # 此命令会生成 /usr/ports/INDEX-10,若报错 "Missing dependency" 或 "Invalid distinfo",说明 ports tree 损坏

验证INDEX-10是否健康的最简单方法:检查其中是否包含www/apache24的确切版本号。

grep "^apache24-" /usr/ports/INDEX-10 | head -n 1 # 正确输出应为:apache24-2.4.12,2|/usr/ports/www/apache24|/usr/local|... # 如果显示 apache24-2.4.16,则说明你同步到了错误的快照,必须重新 `portsnap fetch extract`

提示:portsnap fetch extract会覆盖/usr/ports下所有内容。如果你之前在 ports 目录里做过自定义修改(如 patch),请先备份cp -r /usr/ports /usr/ports.backup。FreeBSD 10.1 的 ports tree 大小约 1.2GB,portsnap extractdu -sh /usr/ports应该接近这个数值。如果只有几百 MB,说明提取不完整,make index必然失败。

3. Apache 2.4.12 编译安装:从 MPM 选择到 SSL 模块深度集成

Apache 是 FAMP 的入口,其配置质量直接决定整个栈的安全基线与性能上限。在 FreeBSD 10.1 上,我们放弃pkg install,坚持从 ports 源码编译,只为精确控制每一个字节。

3.1 MPM 选型:prefork 是唯一安全的选择

Apache 2.4 提供三种 MPM(Multi-Processing Module):preforkworkerevent。在 FreeBSD 10.1 + PHP 7.0 的组合下,prefork是唯一可行且安全的选择。原因如下:

  • workereventMPM 使用线程模型,而 PHP 7.0 的 Zend Engine 在 FreeBSD 上的线程安全(ZTS)支持不完善。php-fpm进程在workerMPM 下会随机崩溃,dmesg显示pid 12345 (httpd) killed by signal 11 (SIGSEGV)
  • eventMPM 依赖kqueueEVFILT_TIMER,但 FreeBSD 10.1 的kqueue实现中,EVFILT_TIMER的精度在高负载下会漂移,导致mod_proxy_fcgi的连接超时判断失准;
  • prefork是进程模型,每个请求由独立进程处理,与 PHP 的单进程执行模型天然契合,无共享内存竞争风险。

编译时强制指定 MPM:

cd /usr/ports/www/apache24 make config # 在弹出的菜单中,取消勾选 "THREADS"(这会禁用 worker/event) # 确保 "MPM_PREFORK" 被勾选(这是默认,但务必确认) make install clean

注意:make config后生成的/var/db/ports/apache24/options文件,是你的编译配置凭证。务必备份此文件。未来重装时,只需cp /path/to/backup/options /var/db/ports/apache24/,再make install即可复现完全一致的二进制。

3.2 SSL/TLS 模块编译:OpenSSL 1.0.2u 与 mod_ssl 的静态链接

FreeBSD 10.1 的 base 系统自带 OpenSSL,但其版本(1.0.1u)已不满足现代 Web 安全要求。我们必须使用 ports 中的 OpenSSL 1.0.2u,并确保mod_ssl与之静态链接,避免运行时动态库版本冲突。

关键操作:

# 1. 先编译安装 OpenSSL 1.0.2u(必须在 apache24 之前) cd /usr/ports/security/openssl && make install clean # 2. 配置 apache24 时,指定 OpenSSL 路径 cd /usr/ports/www/apache24 make config # 在 "SSL Support" 选项中,选择 "OpenSSL from ports"(而非 "Base system") # 这会自动在 Makefile 中设置 OPENSSLBASE=/usr/local make install clean

验证mod_ssl是否静态链接:

ldd /usr/local/sbin/httpd | grep ssl # 正确输出应为:libssl.so.8 => /usr/local/lib/libssl.so.8 (0x801234000) # 如果显示 libssl.so.7 或 libssl.so.6,说明链接了错误的 OpenSSL 版本

实操心得:mod_ssl静态链接后,httpd -V输出的SSL_VERSION应为OpenSSL 1.0.2u 22 Sep 2016。如果显示1.0.1u,说明make config时没选对 OpenSSL 源。此时不要make reinstall,而是make deinstall && make clean && make install clean彻底重建,因为make reinstall会跳过 configure 步骤,沿用旧的链接配置。

3.3 Apache 主配置文件精调:从 ServerTokens 到 KeepAlive

编译安装完成后,/usr/local/etc/apache24/httpd.conf是你的主战场。以下是针对 FreeBSD 10.1 的关键精调项,全部需手动编辑:

# 1. 安全第一:最小化暴露 ServerTokens Prod # 只显示 "Apache",不泄露版本号 ServerSignature Off # 禁用错误页面底部的服务器信息 # 2. 性能核心:KeepAlive 参数 KeepAlive On # 启用持久连接,减少 TCP 握手开销 MaxKeepAliveRequests 100 # 单个连接最多处理 100 个请求 KeepAliveTimeout 5 # 连接空闲 5 秒后关闭 # 3. MPM prefork 配置(必须与 sysctl.conf 中的 kern.maxfiles 匹配) <IfModule mpm_prefork_module> StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxRequestWorkers 256 # 关键!必须 <= kern.maxfiles / 2 MaxConnectionsPerChild 1000 </IfModule> # 4. PHP 处理器注册(暂不启用,留待 PHP 安装后配置) # LoadModule php7_module libexec/apache24/libphp7.so # AddType application/x-httpd-php .php # DirectoryIndex index.php index.html

提示:MaxRequestWorkers 256的设定,必须与sysctl kern.maxfiles的值严格对应。计算公式为:kern.maxfiles >= MaxRequestWorkers * 2 + 100(100 是预留的系统进程描述符)。如果kern.maxfiles=65536,则MaxRequestWorkers最大可设为(65536 - 100) / 2 = 32718,但这会吃光所有内存。实测 256 是 8GB 内存服务器的黄金平衡点。

4. MySQL 5.6.24 服务部署:从数据库初始化到 root 密码加固

MySQL 是 FAMP 的数据心脏。在 FreeBSD 10.1 上,mysql56-serverports 的安装流程与 Linux 有本质差异,尤其是数据库初始化和用户权限体系。

4.1 安装与初始化:mysql_install_db 的不可替代性

FreeBSD 10.1 的 MySQL 5.6.24必须使用mysql_install_db初始化,而非mysqld --initialize。后者是 MySQL 5.7+ 的新特性,在 5.6 中不存在。

安装步骤:

# 1. 安装 mysql56-server(注意:不是 mysql56-client) cd /usr/ports/databases/mysql56-server && make install clean # 2. 创建 MySQL 数据目录(ZFS 数据集已就绪) mkdir -p /var/db/mysql chown -R mysql:mysql /var/db/mysql # 3. 关键:使用 mysql_install_db 初始化(必须以 mysql 用户身份) sudo -u mysql /usr/local/bin/mysql_install_db --basedir=/usr/local --datadir=/var/db/mysql

mysql_install_db的执行过程:

  • 创建mysql系统数据库(含user,db,tables_priv等核心表);
  • 生成root@localhostroot@127.0.0.1两个初始账户;
  • 设置root@localhost的密码为空(这是 FreeBSD ports 的设计,与 Linux 的随机密码不同)。

注意:--datadir=/var/db/mysql必须与 ZFS 数据集挂载点完全一致。如果 ZFS 挂载在/tank/mysql,此处必须写/tank/mysql。否则mysqld启动时会报错Can't find file: './mysql/plugin.frm' (errno: 13),这是典型的权限/路径错误。

4.2 my.cnf 配置:FreeBSD 特有的路径与安全选项

FreeBSD 的 MySQL 配置文件/usr/local/etc/my.cnf需要特别关注路径和安全选项:

[mysqld] # FreeBSD 特有路径 datadir = /var/db/mysql socket = /var/run/mysql/mysql.sock pid-file = /var/run/mysql/mysqld.pid # 性能核心 innodb_buffer_pool_size = 2G # 必须 < vm.kmem_size_max * 0.75 innodb_log_file_size = 256M # 日志文件大小,设为 buffer_pool_size 的 12.5% innodb_flush_method = O_DIRECT # 绕过 OS 缓存,直接写磁盘(ZFS 环境下更优) # 安全加固 skip-networking = OFF # 允许网络连接(默认) bind-address = 127.0.0.1 # 仅监听本地,禁止远程 root 登录 max_connections = 200 # 与 Apache MaxRequestWorkers 匹配 wait_timeout = 60 # 空闲连接 60 秒后断开 [client] socket = /var/run/mysql/mysql.sock

实操心得:innodb_flush_method = O_DIRECT在 ZFS 环境下是黄金配置。它让 InnoDB 绕过 FreeBSD 的bufcache,直接与 ZFS 的ARC交互,避免双重缓存。测试表明,在 1000 并发SELECT场景下,O_DIRECT比默认的fsync模式降低 35% 的 I/O 等待时间。但切记:O_DIRECT要求innodb_log_file_size必须是 512KB 的整数倍,256M 符合要求。

4.3 root 密码加固与权限清理:删除危险的匿名用户

初始化后,root用户密码为空,且存在''@localhost(匿名用户)这一严重安全隐患。必须立即加固:

# 1. 启动 mysqld(首次启动会自动创建 pid 文件) /usr/local/etc/rc.d/mysql-server onestart # 2. 以空密码登录并执行加固 SQL mysql -u root -p"" << 'EOF' -- 删除匿名用户(这是 FreeBSD 10.1 ports 的默认行为,必须清除) DELETE FROM mysql.user WHERE User=''; -- 为 root@localhost 设置强密码 SET PASSWORD FOR 'root'@'localhost' = PASSWORD('YourStrongPass123!'); -- 刷新权限 FLUSH PRIVILEGES; -- 验证:查询 user 表,应只剩 'root'@'localhost' 和 'root'@'127.0.0.1' SELECT User,Host FROM mysql.user; EOF

提示:'root'@'127.0.0.1'是远程连接用的,其密码与'root'@'localhost'独立。mysql_secure_installation脚本在 5.6.24 中无法同时处理这两个账户,所以必须手动 SQL 操作。执行后,SELECT User,Host FROM mysql.user;的输出应只有两行,且Host列为localhost127.0.0.1

5. PHP 7.0.33 集成:从扩展编译到 Apache 模块加载

PHP 是 FAMP 的胶水层,将 Apache 的 HTTP 请求与 MySQL 的数据查询粘合在一起。在 FreeBSD 10.1 上,PHP 7.0.33 的编译必须与 Apache 2.4.12 的 MPM 和 OpenSSL 严格匹配。

5.1 PHP 核心编译:禁用 ZTS,启用关键扩展

PHP 7.0.33 的 ports 位于/usr/ports/lang/php70。编译前必须禁用线程安全(ZTS),因为preforkMPM 是进程模型,ZTS 会引入不必要的性能损耗和兼容性问题。

cd /usr/ports/lang/php70 make config # 取消勾选 "ZTS"(Thread Safety) # 勾选以下关键扩展: # - CLI: 命令行支持(必需) # - CGI: 传统 CGI 模式(备用) # - FPM: PHP-FPM(备用,本文主用 mod_php) # - MYSQLI: MySQLi 扩展(必需) # - OPENSSL: OpenSSL 支持(必需,与 Apache 的 SSL 一致) # - ZIP: ZIP 归档支持(常用) make install clean

注意:make config时,MYSQLIOPENSSL必须勾选。如果漏选,php -m | grep mysqli将无输出,后续 PHP 连接 MySQL 会报错Call to undefined function mysqli_connect()OPENSSL漏选则php -r "print_r(openssl_get_cipher_methods());"会返回空数组。

5.2 PHP 扩展编译:pdo_mysql 与 gd 的 FreeBSD 适配

PHP 的核心编译只包含基础扩展,pdo_mysqlgd(图像处理)等常用扩展需单独编译:

# 1. 编译 pdo_mysql(必须与 MySQL 5.6.24 的头文件匹配) cd /usr/ports/databases/php70-pdo_mysql && make install clean # 2. 编译 gd(图像处理,依赖 freetype 和 jpeg) cd /usr/ports/graphics/php70-gd make config # 确保勾选 "FREETYPE2" 和 "JPEG" make install clean

验证扩展是否加载:

php -m | grep -E "(mysqli|pdo_mysql|gd|openssl)" # 正确输出应包含:mysqli, pdo_mysql, gd, openssl

实操心得:php70-gd编译时,make config中的FREETYPE2选项至关重要。FreeBSD 10.1 的 base 系统没有 freetype,必须从 ports 安装print/freetype2。如果漏装,make install会报错freetype/freetype.h: No such file or directory,且错误信息非常隐蔽,只在config.log末尾。

5.3 Apache 加载 PHP 模块:LoadModule 顺序与 PHPIniDir

PHP 编译安装后,libphp7.so位于/usr/local/libexec/apache24/libphp7.so。将其集成到 Apache,需修改/usr/local/etc/apache24/httpd.conf

# 在 LoadModule 区域,找到 mpm_prefork_module 的加载行,在其后添加: LoadModule mpm_prefork_module libexec/apache24/mod_mpm_prefork.so LoadModule php7_module libexec/apache24/libphp7.so # 在 AddType 区域,添加: AddType application/x-httpd-php .php AddType application/x-httpd-php-source .phps # 在 DirectoryIndex 区域,添加 index.php: DirectoryIndex index.php index.html # 指定 PHP 配置文件路径(关键!) PHPIniDir "/usr/local/etc/php"

提示:LoadModule php7_module必须放在LoadModule mpm_prefork_module之后。Apache 的模块加载顺序是严格的,mod_php7依赖mpm_prefork提供的进程管理 API。如果顺序颠倒,apachectl start会报错Cannot load libexec/apache24/libphp7.so into server: Shared object has no run-time symbol table

6. 服务启动与首个 PHP 页面:从 rc.conf 配置到 phpinfo() 验证

所有组件安装完毕,现在进入最终验证阶段。这一步的成败,取决于rc.conf的精确配置和启动顺序的严格遵守。

6.1 rc.conf 服务注册:启动顺序与依赖关系

FreeBSD 的服务启动由/etc/rc.conf控制。FAMP 各组件的启动顺序必须是:MySQL → Apache。因为 Apache 的mod_php7在启动时会尝试连接 MySQL(如果配置了php.ini中的mysql.default_host),若 MySQL 未就绪,Apache 会启动失败。

编辑/etc/rc.conf

# 启用 MySQL 服务 mysql_enable="YES" mysql_dbdir="/var/db/mysql" # 启用 Apache 服务 apache24_enable="YES" apache24_profiles="default" apache24_default_config="/usr/local/etc/apache24/httpd.conf"

注意:mysql_dbdir必须与 ZFS 数据集挂载点一致。apache24_profilesapache24_default_config是 FreeBSD 10.1 的标准配置,确保rc.d脚本能正确定位配置文件。

6.2 启动服务与日志排查:systemctl 的 FreeBSD 替代方案

FreeBSD 10.1 没有systemctl,使用传统的service命令:

# 1. 按顺序启动 service mysql-server start service apache24 start # 2. 检查状态 service mysql-server status # 应显示 "mysql is running as pid XXXX" service apache24 status # 应显示 "apache24 is running as pid YYYY" # 3. 查看关键日志 tail -f /var/log/mysql/error.log # MySQL 错误日志 tail -f /var/log/httpd-error.log # Apache 错误日志

如果 Apache 启动失败,/var/log/httpd-error.log是第一线索。常见错误及解决:

  • [crit] (2)No such file or directory: AH00023: Couldn't create the mpm-accept mutexkern.ipc.semmni参数过小,执行sysctl kern.ipc.semmni=256并加入/etc/sysctl.conf
  • [notice] Apache/2.4.12 (FreeBSD) configured -- resuming normal operations:启动成功,HTTP 服务已就绪。

6.3 创建首个 PHP 页面:phpinfo() 的终极验证

在 Apache 的 DocumentRoot(默认/usr/local/www/apache24/data/)下创建info.php

<?php // /usr/local/www/apache24/data/info.php phpinfo(); ?>

然后在浏览器访问http://your-server-ip/info.php。成功页面应显示 PHP 7.0.33 的详细信息,且在"Configuration File (php.ini) Path"行显示/usr/local/etc/php,在"Loaded Configuration File"行显示/usr/local/etc/php/php.ini

提示:如果页面空白或下载.php文件,说明AddType application/x-httpd-php .php未生效。检查httpd.conf中该行是否被注释(前面有#),或是否在<Directory>块内被AllowOverride None覆盖。最简单的修复是:在httpd.conf末尾添加:

http://www.jsqmd.com/news/1054455/

相关文章:

  • 如何让《十字军之王II》完美显示中文:CK2DLL双字节补丁终极指南
  • Linux异步I/O新宠io_uring登场,对比epoll优势显著!
  • 如何高效清理C盘空间:WindowsCleaner智能系统优化解决方案
  • Robot Framework自定义关键字设计:从脚本到工程化的自动化测试进阶
  • i.MX 93平台工业编码器接口实战:从EnDat到HIPERFACE DSL的快速评估与集成
  • 化妆品成分、标签和出口单证对不上,会有什么风险?问题出在哪 | 根因分析与合规路径 - 欢欢在创业
  • 3步掌握NBTExplorer:轻松编辑我的世界游戏数据的终极指南
  • 速收藏!2026 寿县凤台田家庵初三落榜出路,低分公办技校完整推荐 - 我叫小周
  • 互联网大厂 Java 求职者面试:构建微服务与安全框架
  • 隆回新能源用车服务全测评:江铃集团新能源4S店及本地同行门店横向对比盘点 - 百航
  • 河源黄金回收全攻略六家靠谱门店推荐附避坑指南 - 余生黄金回收
  • G-Helper:华硕笔记本性能控制革新,告别Armoury Crate的智能解决方案
  • Ubuntu 20.04 Node.js 环境构建与 nvm 排障指南
  • 济南市中区黄金回收实测:6家机构谁家秤准价实在 - 上门黄金回收
  • Django+GraphQL构建生产级URL缩短服务
  • 终极魔兽争霸3兼容性修复工具:3步解决现代系统闪退问题
  • B站抢票终极指南:如何用biliTickerBuy告别抢票烦恼
  • NBTExplorer终极指南:5分钟掌握我的世界数据编辑神器
  • 重磅发布|2026年6月浪琴官方售后网点全面核验专项报告,官方维修服务网点全新营业地址投入运营 - 浪琴中国服务中心
  • 3个关键步骤让魔兽争霸3在现代系统上流畅运行
  • WarcraftHelper:让经典魔兽争霸3在现代电脑上焕发新生的终极方案
  • 3个高级技巧深度解析iStore软件中心架构
  • 泉州鲤城区黄金回收行情与6家正规机构服务全解析 - 上门黄金回收
  • 国内AI服务合规开通指南:微信支付宝订阅实操与资质识别
  • E-Hentai漫画下载器:一键打包收藏的终极解决方案
  • 如何用biliTickerBuy突破B站会员购抢票瓶颈:从手动失败到自动化成功的完整指南
  • 苏州虎丘区黄金回收实测,六家店真实到店体验全记录 - 上门黄金回收
  • 终极FGO自动化指南:如何用Fate/Grand Automata告别重复刷本
  • 如何用WELearn网课助手实现高效学习:从技术架构到实际应用的全方位指南
  • Full Page Screen Capture:一键捕获完整网页截图的Chrome扩展解决方案