☰
cgminer 3.1.1 Windows 运行、调参与 API 监控排障指南
2026/10/30 1:14:04 网站建设 项目流程

简介:cgminer 3.1.1 Windows 版是一份面向加密货币爱好者、旧平台维护者与挖矿程序研究者的 Windows 挖矿工具包,主要用于多线程、多矿池环境下的比特币及其衍生币挖矿,并提供 ATI GPU 监控、超频与风扇转速调节支持,兼容 GPU、FPGA、ASIC 等硬件后端;使用时需注意不要同时接入多条区块链。压缩包共 43 个文件、约 6.83MB,包含可执行程序与动态链接库、OpenCL 内核脚本、FPGA 比特流及二进制文件,以及配置示例、API 说明和多份技术文档,覆盖安装构建、接口调用、SCRYPT 与 ASIC/FPGA 使用等主题。已有 560 人学习下载。借助示例配置、API 接口说明和各硬件后端文档,读者可了解多池切换、设备监控与参数调整思路,也能参考 OpenCL 内核和 FPGA 相关文件研究不同算法后端的组织方式,适合旧版挖矿工具复现、排错与二次开发参考,对学习旧版挖矿软件架构也有一定参考价值。

1. 拿到 cgminer-3.1.1-windows 先别双击:这个包在什么机器上还能跑

cgminer 3.1.1 windows这个组合词在搜索框里出现的频率,比它今天真正能跑起来的机器数量高得多。它是一个 C 写的、基于 OpenCL 的算力客户端,界面是字符终端,配置靠一个 JSON 文件和几十个命令行开关,属于那个「显卡还能当通用计算设备用」的年代。它解决的问题很具体:把 GPU 上的并行计算任务按固定节奏提交给远端作业端,同时把每张卡的频率、温度、接受/拒绝计数实时打回屏幕。

适合三类人:手上有台装老驱动的工作机、想搞清楚 OpenCL 参数怎么影响吞吐、或者想拿一个真实的 C 工程练编译和 API 对接。不适合的是指望双击出结果的场景——这个包对驱动版本、目录结构、参数组合三者都挑,任意一个不对,表现都是「窗口一闪没了」。下面按「先让它跑起来,再让它跑稳,最后能远程看住」的顺序拆。

2. 把 cgminer 3.1.1 跑起来:解压路径、依赖 DLL 与第一次启动

2.1 解压后先确认三件事:主程序、配置模板、运行库

Windows 版通常是预编译二进制,不是让你现场编译的源码树。解压完不要急着把 exe 拖到桌面,DLL 的搜索顺序是「exe 所在目录 → 系统目录 → PATH」,一旦你把主程序单独挪走,同目录的运行库就找不到了,这是新人第一次翻车最常见的原因。

典型发布包里能看到这几类文件:

文件/目录作用缺失后的表现
cgminer.exe主程序,字符界面 + API 服务—
*.dll运行库,常见是 curl、pthread、zlib、OpenCL 加载层启动即闪退,报 0xc000007b 或「找不到 xxx.dll」
*.confJSON 格式的配置模板只能用全命令行参数启动,长参数容易敲错
*.bat启动脚本样例不影响运行,但少了参数写法的参考
README/*.txt参数与算法说明调参全靠猜,效率极低

建议路径定成纯英文、无空格,例如D:\tools\cgminer-3.1.1\。带中文或空格的路径在加载 conf、写日志文件时会出现「文件明明在那却打不开」的玄学现象,本质是命令行分词把空格当成了参数分隔符。

2.2 第一次启动用 cmd,不要用资源管理器双击

双击启动失败时窗口会立刻关闭,你连报错都看不到。正确做法是开一个 cmd,cd进目录再执行,错误信息就留在屏幕上了。

:: 进入目录,注意 /d 允许跨盘符切换 cd /d D:\tools\cgminer-3.1.1 :: 最小启动:算法、作业端地址、凭据、四个核心调优参数 cgminer.exe --scrypt ^ -o stratum+tcp://pool-host:3333 ^ -u worker-name ^ -p x ^ -I 13 -g 1 -w 256 --thread-concurrency 8192 ^ -T --log-file run.log

参数逐个说明:

  • --scrypt:切换算法分支。3.1.x 之后才把 scrypt 并进主线,不加这个开关走的是另一套内核,显存占用和参数含义完全不同,这是最容易漏的一个。
  • -o:远端作业端地址,stratum+tcp://前缀表示走 stratum 协议,后面的端口要和对方给的一致。
  • -u/-p:凭据。很多作业端对密码不校验,随便填,但用户名不能错。
  • -I:强度,0~20,scrypt 下有效区间大概 8~20,越高显存访问越激进。
  • -g:每张卡起几组线程,scrypt 一般 1,设成 2 反而抢显存。
  • -w:work group 大小,必须是 64 的倍数,常见 128 或 256。
  • --thread-concurrency:并发线程数,直接决定显存占用,后面单独讲。
  • -T:纯文本模式,不走 ncurses 全屏,日志能正常滚动,排查阶段必开。
  • --log-file:落盘日志,出问题时有据可查。

启动成功后屏幕会分区刷新:顶部是全局汇总(运行时长、平均算力、接受/拒绝数),中间是每个设备一行(温度、风扇、频率、强度),底部是作业端连接状态。终端里按Q退出,按D切设备详情,按P切作业端列表。字符界面看着粗糙,但它刷新频率低、不占资源,反而比图形界面更适合长时间挂着。

2.3 把命令行固化成 cgminer.conf

每次敲一长串参数不现实,出错率也高。把参数搬进 JSON 配置文件,启动时只给-c。

{ "pools": [ { "url": "stratum+tcp://pool-host:3333", "user": "worker-name", "pass": "x" }, { "url": "stratum+tcp://backup-host:3333", "user": "worker-name", "pass": "x", "quota": "1" } ], "scrypt": true, "intensity": "13", "gpu-threads": "1", "worksize": "256", "thread-concurrency": "8192", "lookup-gap": "2", "temp-cutoff": "95", "temp-overheat": "85", "temp-target": "75", "auto-fan": true, "api-listen": true, "api-port": "4028", "api-allow": "W:127.0.0.1", "log-file": "run.log" }

几个容易踩的格式点:conf 里的值绝大多数写成字符串,"13"而不是13,虽然解析器对布尔值和数字有一定容忍度,但混着写会出现「配置加载了却没生效」的假象;pools是数组,顺序即优先级,quota控制切换权重;scrypt是布尔值,写true不带引号。

启动命令就变成:

cgminer.exe -c cgminer.conf

如果同时给了命令行参数和 conf,命令行优先级更高,会覆盖同名配置项。这个特性在调试时有用——你可以用 conf 固化一套稳定参数,临时加-I 15试一下高一点的强度,退出后 conf 不受影响。

3. cgminer 参数调优:-I、-g、-w 与 thread-concurrency 到底谁管谁

3.1 从一次 OpenCL 内核调度看四个参数的分工

要调参先得知道它们在硬件层面分别动的是什么。scrypt 这类算法是显存密集型:每个并发线程在计算过程中要占一块固定大小的 scratchpad,通常按每线程 128 KB 估算。所以thread-concurrency不是随便填的数字,它直接乘出显存占用,超过可用显存就是崩溃或驱动重置。

四个参数的分工大致是:

  • thread-concurrency(TC):一条提交里并发跑多少个线程,决定显存占用的总量。
  • -w(worksize):每个 work group 里多少个线程,必须是 64 的整数倍,影响调度粒度和 LDS 使用效率。
  • -I(intensity):内核内部每个线程做多少轮查找,影响单线程的指令密度和寄存器压力。
  • -g(gpu-threads):每张卡同时跑几条这样的流水线,等于把上面三项再复制一份。

它们的组合关系是相乘的,所以调参不能四项一起动。血泪经验是:先把g固定为 1,把w固定为 256,然后用 TC 卡住显存上限,最后才用 I 往上顶。顺序反了,你会在一堆变量里迷失,每次崩了都说不清是谁的锅。

3.2 按显存反推 thread-concurrency 的起步值

算一下就知道上限在哪。以 2 GB 显存为例,扣除显示输出和驱动自身占用,可用按 1.7 GB 估,除以每线程 128 KB:

1.7 * 1024 * 1024 KB / 128 KB ≈ 13926

也就是说理论上限在一万四左右,实际要留余量给驱动和桌面合成,取 60%~70% 比较稳。不同卡的建议起步值不是拍脑袋来的,是照这个公式再按经验系数收一收:

显存TC 起步值-I起步值-wlookup-gap
1 GB409691282
2 GB8192132562
3 GB12288132562
4 GB 及以上16384152562

lookup-gap是控制 scratchpad 里各轮数据之间间隔的,作用类似调显存时序。默认 2 最稳,改到 1 或 3 属于精调阶段的事,先用 2 把所有变量跑通。表里的数字只是起点,同一块芯片在不同驱动版本下最优值能差一档,必须自己扫。

还有一个硬约束:TC最好能被w整除,否则最后一次 work group 会不满员,效率打折。8192 / 256 = 32,整齐;如果你把 TC 设成 8000,除不尽,尾组浪费就出现了。

3.3 一套可复现的爬坡流程:用 --runtime 做对照实验

调参最怕「改了之后忘了原来多少」。稳定做法是每换一组参数,固定跑一段时间,把日志留下来对比。--runtime参数可以指定运行多少分钟后自动退出,配合--log-file就成了一次可重复的实验。

@echo off setlocal enabledelayedexpansion set CGM=D:\tools\cgminer-3.1.1\cgminer.exe set POOL=stratum+tcp://pool-host:3333 :: 固定 TC 和 w,只扫 intensity,每次跑 8 分钟 for %%I in (9 11 13 15 17) do ( echo === testing intensity %%I === "%CGM%" --scrypt -o %POOL% -u worker-name -p x ^ -I %%I -g 1 -w 256 --thread-concurrency 8192 ^ --runtime 8 -T --log-file log-I%%I.txt :: 间隔几秒,让驱动彻底释放显存再进下一轮 timeout /t 10 /nobreak >nul ) echo done, check log-I*.txt

这段脚本的逻辑很直白:外层循环只改-I,其余参数锁死,这样每一轮的差异只能归因于强度;--runtime 8让每轮自动收尾,不会挂在那里等你手动 Ctrl+C;--log-file每轮一个独立文件,事后可以横向比。跑完之后在日志里找汇总行,重点看三个数:平均算力、被拒比例、硬件错误数。

判读标准我一般这么定:算力涨了但硬件错误开始出现,说明这一档已经过界,退回去;算力不涨反而拒单变多,说明内核在空转,也退回去;只有在算力上升且错误数保持为 0 的那一档,才是可用值。别贪最后一格,那格通常是拿稳定性换的。

同理可以再扫一轮 TC:固定-I为上一轮的最优值,TC从 4096 开始每档加 2048 往上走,一旦出现驱动重置或程序退出,上一档就是该卡的显存天花板。两轮扫完,一张卡的参数画像就有了,抄进 conf 即可。

4. cgminer API 不是黑匣子:4028 端口怎么开、怎么读、怎么当监控用

4.1 两个开关:--api-listen 与 --api-allow

字符界面只能人盯着,机器一多就不现实。cgminer 内置了一个 TCP 服务,默认端口 4028,收发都是单行 JSON,以\0结尾。这个接口默认是关的,要显式打开,并且要配置允许访问的来源。

cgminer.exe -c cgminer.conf ^ --api-listen ^ --api-port 4028 ^ --api-allow W:127.0.0.1,W:192.168.1.0/24

--api-listen是总开关;--api-port可以改成别的端口,避免和同机其他服务打架;--api-allow的格式是「权限:来源」,W表示可写(能执行切换、重启这类命令),R表示只读。只给本机和内网段,不要开成W:0.0.0.0/0,这个接口没有任何认证,谁能连上谁就能改你的运行状态。

注意:端口开了但 Windows 防火墙拦着,本地能连、远程连不上,这是第二个常见困惑点。排查时先在本机telnet 127.0.0.1 4028,通了再查防火墙入站规则。

4.2 用 Python 拉 summary / devs / pools

协议简单到不需要任何 SDK,一个 socket 就够。

import json import socket def cgminer(command, host="127.0.0.1", port=4028, timeout=3): """发送单条命令,读回到 \\0 为止,返回解析后的 dict。""" payload = json.dumps({"command": command}) + "\n" with socket.create_connection((host, port), timeout=timeout) as sock: sock.sendall(payload.encode("utf-8")) buf = b"" while b"\x00" not in buf: # 协议用 NUL 作为报文结束符 chunk = sock.recv(4096) if not chunk: # 对端提前关闭,避免死循环 break buf += chunk body = buf.split(b"\x00", 1)[0] return json.loads(body.decode("utf-8", "ignore")) if __name__ == "__main__": s = cgminer("summary") print("算力:", s["SUMMARY"][0]["MHS av"]) print("接受/拒绝:", s["SUMMARY"][0]["Accepted"], s["SUMMARY"][0]["Rejected"]) # devs 返回每张卡的独立状态,用来定位「是哪张卡拖后腿」 for dev in cgminer("devs")["DEVS"]: print(dev["GPU"], dev["Temperature"], dev["Intensity"], dev["MHS av"])

几个实现细节:返回值是{"STATUS": [...], "SUMMARY": [...]}这种结构,命令名大写后作为键,值一律是列表,所以取第一条;STATUS里有个STATUS字段,S表示成功、E表示出错、W表示警告,判断成败看这里而不是看有没有异常;MHS av这个键名是历史遗留,scrypt 下数值极小,实际看的时候按 KH/s 理解更直观。

4.3 三个值得长期盯的字段,以及 switchpool 兜底

监控不是把所有字段都打印出来,那样只会刷屏。真正需要盯的是三个:

  • 平均算力:看趋势而不是绝对值。连续多个采样点下滑,通常是散热降频或者作业端在发低难度任务。
  • 拒绝比例:Rejected / (Accepted + Rejected)。超过 2% 就该查了,要么是本地时钟漂了,要么是强度过高导致提交超时。
  • 温度:这是唯一会导致硬件损伤的指标,超过temp-overheat就该降频,别指望auto-fan能救一切。

作业端挂了要能自动切,这是无人值守的前提:

def failover(): """遍历作业端,发现当前优先级不可用就切到下一个。""" pools = cgminer("pools")["POOLS"] current = next((p for p in pools if p["Status"] == "Alive"), None) if current: return f"alive: {current['URL']}" for p in sorted(pools, key=lambda x: int(x["Priority"])): if p["Status"] != "Alive": cgminer(f"switchpool|{p['POOL']}") return f"switched to {p['URL']}" return "no pool available"

switchpool的参数是池序号,不是 URL,这个细节坑过不少人。切完之后建议隔十秒再读一次pools确认状态真的变了,有些作业端切换后需要重新握手,立刻读会看到还在Dead。

5. 避坑与排查:cgminer 3.1.1 在 Windows 上的五类翻车现场

5.1 五条「现象 → 原因 → 解决」

第一条:双击 exe,黑框一闪就没了。现象是窗口存在时间不到一秒,什么都看不清。原因是流程根本没走到主循环——要么同目录缺 DLL,要么参数解析失败,要么路径里有中文。解决:开 cmd 进目录执行,报错会留下;看到「找不到 xxx.dll」就补运行库,看到参数错误就去掉所有可选参数先跑最小命令;顺手把整个目录挪到纯英文路径下。

第二条:日志里出现No devices detected或clGetDeviceIDs failed。现象是程序能启动,但设备列表是空的。原因通常是显卡驱动没装 OpenCL 运行时,或者装的是纯显示驱动,或者 32 位程序加载了 64 位的 OpenCL 加载层。解决:重装厂商完整驱动包,确认系统目录和程序目录下的 OpenCL 加载层位数与 exe 一致(任务管理器里看进程是不是带 32 位标记)。老版本程序对新架构显卡识别不出来也是常态,这种情况换平台比死磕划算。

第三条:跑几分钟屏幕黑一下,程序报错退出。这是 Windows 的 TDR 机制在起作用——显卡单次计算超过默认的 2 秒,系统认为驱动挂死,强制重置。挖矿内核本来就是长时占用,撞上这个太正常了。解决顺序是:先降-I一到两档、把-w提到 256,多数情况能压到 2 秒以内;还不行就降 TC 减少并发压力。改注册表延长超时属于最后手段,会让真正的驱动故障也变成长时间黑屏,风险自负,我一般不推荐在生产机上这么做。

第四条:本机能连 4028,另一台机器连不上。现象是telnet 127.0.0.1 4028通,换成局域网 IP 就超时。原因是三选一:没加--api-listen(只写了端口没用)、--api-allow没包含对方网段、Windows 防火墙拦了入站。解决:依次确认启动日志里有没有「API listening」这行、allow 列表里有没有对方 IP 段、防火墙有没有放行该端口的入站规则。三步都过了一般就通了。

第五条:改了 conf,重启后参数没变。现象是文件明明保存了,界面上的强度还是老值。原因可能是:程序实际加载的是另一个同名 conf(工作目录不对)、JSON 里键名拼错被静默忽略、或者启动命令行里还带着会覆盖 conf 的旧参数。解决:用-T启动看回显,程序在初始化阶段会打印实际生效的关键参数;再不行就在 conf 里故意写一个非法值,如果程序没报错,说明这个文件压根没被读到。

5.2 出问题先看日志的哪几行

排查效率高低,差别就在知不知道该看哪里。启动阶段的日志看三样:设备枚举结果(识别到几张卡、每张卡的名称)、作业端连接结果(连上还是超时)、实际生效的参数回显。运行阶段的日志看两样:每次提交的结果(接受/拒绝/过期),以及温度触发的降频事件。

:: 找所有与设备相关的行 findstr /i "device GPU detected" run.log :: 找拒绝和错误 findstr /i "rejected error stale" run.log :: 找温度与降频 findstr /i "temp throttl fan" run.log

提示:把-T和--log-file一起用。纯文本模式下日志是逐行追加的,全屏模式会不断重绘终端,重定向出来的文件里全是控制字符,事后没法读。

还有一个容易被忽略的点:日志文件默认是追加写的。同一目录下反复启动,旧日志会越滚越大,找当次记录时容易看串行。习惯做法是每次实验前把旧的挪走或者改个名,别让两次运行的数据混在一个文件里。

6. 老版本的进阶用法:把参数验证做成三层校验

参数抄进 conf 不等于生效,生效不等于稳定,稳定不等于最优。这三件事要分开验证,我一般做成三层,每层都有明确的判据。

层级做什么判据不通过怎么办
第一层:配置回显-T启动,看初始化日志关键参数与 conf 一致查工作目录、键名拼写、命令行覆盖
第二层:设备状态调devs接口读每卡字段Intensity、Thread Concurrency与预期一致说明该卡没吃到配置,查是否被单独限制
第三层:运行质量跑满 30 分钟看日志均值硬件错误为 0,拒绝比例 < 2%回退一档强度或降低 TC

第一层最容易被跳过,但它是唯一能证明「程序读到了你的文件」的证据。第二层用来抓「多卡不同配置」的问题——cgminer 支持按设备序号单独指定参数,一旦某张卡被单独限制过,全局参数对它就不生效了,光看汇总数据发现不了。第三层是唯一有统计意义的验证,短时间跑出来的算力波动很大,至少半小时再下结论。

再补一个实用技巧:把 API 拉数和参数实验合到一个脚本里,跑完自动出对比表,省得手工翻日志。

import time from statistics import mean def bench(minutes=10, sample=30): """采样指定时长,返回均值与稳定性指标。""" speeds, rejects = [], 0 deadline = time.time() + minutes * 60 while time.time() < deadline: s = cgminer("summary")["SUMMARY"][0] speeds.append(float(s["MHS av"])) rejects = int(s["Rejected"]) time.sleep(sample) avg = mean(speeds) # 用极差比衡量稳定性,比值越小越稳 spread = (max(speeds) - min(speeds)) / avg if avg else 0 return {"avg": round(avg, 3), "spread": round(spread, 3), "rejected": rejects}

这段的判读逻辑是:avg用来横向比不同参数组合,spread用来判断这一档稳不稳——算力忽高忽低通常意味着散热在边界上反复触发降频,或者显存已经接近满载开始出现重试。spread超过 0.15 我就认为这档不可用,哪怕它的均值最好看。拒绝数只在整段实验里累计,不和算力放一起比。

最后说个我自己的教训。早年调这类老程序,我总想一步到位找到「最佳参数」,结果是在一堆变量里反复横跳,改完 A 忘 B,出问题连回滚到哪都不知道。后来强制自己走一遍「固定三项、只动一项、每轮留日志、跑满半小时」的流程,看着慢,实际比乱试快得多,而且每一步都有据可查。现在哪怕只是改一个数字,我也会先把当前的 conf 复制一份带时间戳的备份再动手——这个习惯救过我很多次。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询