背景

桌面系统的性能检查,最怕两件事:

第一,测试命令散落在历史记录里,下次想复测时已经找不全。
第二,只盯着单个跑分数字,却没有把系统信息、存储健康、启动链、图形栈和持续负载一起记录下来。

更稳妥的做法,是把整套检查流程写成脚本,统一输出到一份文本报告里。这样做的价值不只是“测一次”,而是把性能检测变成可重复、可对比、可长期复用的方法。

这篇文章整理的是一套 通用桌面 Linux 硬件性能检测脚本方案。重点放在脚本本身,包括:

  • 缺失工具先检查
  • 能自动安装的尽量自动安装
  • 结果统一写入一个文本文件
  • 覆盖 CPU、内存、存储、图形、启动链和持续负载
  • 避免破坏性测试

这类脚本要解决什么问题

一套真正有用的检测脚本,至少应该回答下面这些问题:

  • 系统当前是否正常发挥性能
  • 瓶颈更像出在 CPU、内存、图形还是磁盘
  • 启动慢是硬件不够,还是服务等待链太长
  • 图形驱动是否正常工作,是否误落到软件渲染
  • 长时间负载下是否明显掉速
  • 测试结果是否能沉淀成文字,方便后续复盘

如果只是临时跑几个命令,得到的是“当时的一串输出”。
如果做成脚本,得到的是“以后还能重复使用的一套流程”。


设计原则

这类脚本的核心原则并不复杂。

1. 先补工具,再跑测试

脚本不应该假设测试环境已经完整。
fiosysbenchsmartctlglmark2vkmark 这类工具,经常会缺一个或几个。更合理的做法是:

  • 先检查工具是否存在
  • 能自动安装的先自动安装
  • 安装不了的明确记录下来
  • 继续执行其他可完成的测试

2. 所有输出落到同一个报告文件

统一报告文件的好处非常大:

  • 后续分析不用翻终端历史
  • 出错时能看到具体命令和退出码
  • 换硬件、换驱动、换系统盘之后,可以直接做对比

3. 测试覆盖桌面环境最关键的几类能力

建议至少覆盖:

  • 系统基线
  • 启动分析
  • CPU
  • 内存
  • 存储健康
  • 存储性能
  • 图形栈
  • 长时间 CPU 负载
  • 可选网络吞吐

4. 不做破坏性操作

桌面性能检测不需要对真实设备做清盘式测试。
裸设备破坏性写入、无保护的低层级覆盖,不应该默认进入脚本。

5. 环境相关的信息要和分数一起保留

单看一个分数意义不大。
同样一次 sysbench,如果不知道:

  • 当前内核版本
  • 当前图形驱动
  • 当前会话类型
  • 当前系统盘是哪块
  • 当前温度和 governor

那后续就很难解释结果。


一份可直接改造的通用脚本

下面给出一份可复用的 Bash 脚本模板。
它的思路是:

  • 自动检测包管理器
  • 检查并安装测试工具
  • 统一输出到单个文本报告
  • 能测的尽量都测
  • 图形测试在有图形会话时执行
  • 网络测试按需开启
#!/usr/bin/env bash
set -uE -o pipefail# 通用桌面 Linux 硬件性能检测脚本
# 目标:
# 1. 检查必要工具
# 2. 缺失时尝试安装
# 3. 统一生成文本报告
# 4. 覆盖 CPU / 内存 / 存储 / 图形 / 启动 / 持续负载
#
# 用法:
# chmod +x ./linux_benchmark_report.sh
# sudo ./linux_benchmark_report.sh
#
# 可选:
# REPORT_ROOT=/path/to/output sudo ./linux_benchmark_report.sh
# IPERF_SERVER=192.168.1.10 sudo ./linux_benchmark_report.sh
# DISK_TEST_SIZE_GB=6 CPU_TIME_LONG=180 sudo ./linux_benchmark_report.shREPORT_ROOT="${REPORT_ROOT:-./benchmark_reports}"
RUN_STAMP="$(date +%F_%H%M%S)"
OUTDIR="${REPORT_ROOT%/}/${RUN_STAMP}"
REPORT="${OUTDIR}/benchmark_report.txt"DISK_TEST_SIZE_GB="${DISK_TEST_SIZE_GB:-4}"
CPU_TIME_SHORT="${CPU_TIME_SHORT:-60}"
CPU_TIME_LONG="${CPU_TIME_LONG:-300}"
FIO_SEQ_RUNTIME="${FIO_SEQ_RUNTIME:-45}"
FIO_MIX_RUNTIME="${FIO_MIX_RUNTIME:-60}"
IPERF_SERVER="${IPERF_SERVER:-}"TESTFILE="${OUTDIR}/fio-test.bin"
DD_FILE="${OUTDIR}/dd-tempfile.bin"# 需要的命令
REQUIRED_CMDS=(
lscpu lsblk free df findmnt lspci lsusb date uname uptime who
sensors smartctl hdparm sysbench fio 7z cryptsetup dmidecode inxi
systemd-analyze
)# 图形相关命令
GUI_CMDS=(
glxinfo vulkaninfo glmark2 vkmark
)mkdir -p "$OUTDIR"log() {
printf '%s\n' "$*" | tee -a "$REPORT"
}section() {
{
echo
echo "================================================================"
echo "$1"
echo "================================================================"
} | tee -a "$REPORT"
}run_shell() {
local title="$1"
shift
local cmd="$*"
{
echo
echo "----- ${title} -----"
echo "[command] ${cmd}"
bash -lc "$cmd"
local rc=$?
echo "[exit_code] ${rc}"
} >> "$REPORT" 2>&1
}have_cmd() {
command -v "$1" >/dev/null 2>&1
}need_root() {
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
echo "请以 root 身份执行。" >&2
exit 1
fi
}detect_pkg_manager() {
if have_cmd pacman; then
echo "pacman"
elif have_cmd apt-get; then
echo "apt"
elif have_cmd dnf; then
echo "dnf"
else
echo "unknown"
fi
}# 命令到软件包的大致映射
# 不同发行版包名可能略有差异,必要时可按实际环境调整
pkg_for_cmd() {
local mgr="$1"
local cmd="$2" case "$mgr:$cmd" in
pacman:lspci) echo "pciutils" ;;
pacman:lsusb) echo "usbutils" ;;
pacman:sensors) echo "lm_sensors" ;;
pacman:smartctl) echo "smartmontools" ;;
pacman:hdparm) echo "hdparm" ;;
pacman:sysbench) echo "sysbench" ;;
pacman:fio) echo "fio" ;;
pacman:7z) echo "7zip" ;;
pacman:cryptsetup) echo "cryptsetup" ;;
pacman:dmidecode) echo "dmidecode" ;;
pacman:inxi) echo "inxi" ;;
pacman:glxinfo) echo "mesa-utils" ;;
pacman:vulkaninfo) echo "vulkan-tools" ;;
pacman:glmark2) echo "glmark2" ;;
pacman:vkmark) echo "vkmark" ;; apt:lspci) echo "pciutils" ;;
apt:lsusb) echo "usbutils" ;;
apt:sensors) echo "lm-sensors" ;;
apt:smartctl) echo "smartmontools" ;;
apt:hdparm) echo "hdparm" ;;
apt:sysbench) echo "sysbench" ;;
apt:fio) echo "fio" ;;
apt:7z) echo "p7zip-full" ;;
apt:cryptsetup) echo "cryptsetup" ;;
apt:dmidecode) echo "dmidecode" ;;
apt:inxi) echo "inxi" ;;
apt:glxinfo) echo "mesa-utils" ;;
apt:vulkaninfo) echo "vulkan-tools" ;;
apt:glmark2) echo "glmark2" ;;
apt:vkmark) echo "vkmark" ;; dnf:lspci) echo "pciutils" ;;
dnf:lsusb) echo "usbutils" ;;
dnf:sensors) echo "lm_sensors" ;;
dnf:smartctl) echo "smartmontools" ;;
dnf:hdparm) echo "hdparm" ;;
dnf:sysbench) echo "sysbench" ;;
dnf:fio) echo "fio" ;;
dnf:7z) echo "p7zip" ;;
dnf:cryptsetup) echo "cryptsetup" ;;
dnf:dmidecode) echo "dmidecode" ;;
dnf:inxi) echo "inxi" ;;
dnf:glxinfo) echo "mesa-demos" ;;
dnf:vulkaninfo) echo "vulkan-tools" ;;
dnf:glmark2) echo "glmark2" ;;
dnf:vkmark) echo "vkmark" ;;
*)
echo ""
;;
esac
}install_packages() {
local mgr="$1"
shift
local pkgs=("$@") case "$mgr" in
pacman)
pacman -S --needed --noconfirm "${pkgs[@]}"
;;
apt)
apt-get update && apt-get install -y "${pkgs[@]}"
;;
dnf)
dnf install -y "${pkgs[@]}"
;;
*)
return 1
;;
esac
}check_and_install_tools() {
section "Tool check" local mgr
mgr="$(detect_pkg_manager)"
log "Detected package manager: ${mgr}" local missing_cmds=()
local missing_pkgs=()
local cmd pkg for cmd in "${REQUIRED_CMDS[@]}"; do
if ! have_cmd "$cmd"; then
missing_cmds+=("$cmd")
pkg="$(pkg_for_cmd "$mgr" "$cmd")"
[[ -n "$pkg" ]] && missing_pkgs+=("$pkg")
fi
done # 图形命令按可选处理
local missing_gui_cmds=()
for cmd in "${GUI_CMDS[@]}"; do
if ! have_cmd "$cmd"; then
missing_gui_cmds+=("$cmd")
pkg="$(pkg_for_cmd "$mgr" "$cmd")"
[[ -n "$pkg" ]] && missing_pkgs+=("$pkg")
fi
done if ((${#missing_cmds[@]} == 0 && ${#missing_gui_cmds[@]} == 0)); then
log "All required tools are already present."
return 0
fi log "Missing core commands: ${missing_cmds[*]:-none}"
log "Missing GUI commands: ${missing_gui_cmds[*]:-none}" # 去重
mapfile -t missing_pkgs < <(printf '%s\n' "${missing_pkgs[@]}" | awk 'NF && !seen[$0]++') if ((${#missing_pkgs[@]} == 0)); then
log "No installable package mapping found for missing commands. Continue with partial testing."
return 0
fi log "Attempting to install packages: ${missing_pkgs[*]}"
if install_packages "$mgr" "${missing_pkgs[@]}" >> "$REPORT" 2>&1; then
log "Package installation completed."
else
log "Automatic installation failed. Continue with available tools only."
fi
}print_header() {
: > "$REPORT"
section "Linux desktop benchmark report"
log "Generated at: $(date --iso-8601=seconds)"
log "Output directory: ${OUTDIR}"
log "Report file: ${REPORT}"
log "Display: ${DISPLAY:-unset}"
log "Session type: ${XDG_SESSION_TYPE:-unknown}"
log "Optional network test runs only if IPERF_SERVER is set."
log "Destructive raw-device write tests are intentionally excluded."
}get_memtest_total() {
local mem_mib
mem_mib="$(awk '/MemTotal/ {printf "%d", $2/1024}' /proc/meminfo)"
local target_mib=$((mem_mib / 2))
((target_mib > 8192)) && target_mib=8192
((target_mib < 1024)) && target_mib=1024
echo "${target_mib}M"
}get_disk_free_gb() {
df -BG "$OUTDIR" | awk 'NR==2 {gsub(/G/, "", $4); print $4}'
}collect_basic_info() {
section "System baseline"
run_shell "date" "date --iso-8601=seconds"
run_shell "uname" "uname -a"
run_shell "os-release" "cat /etc/os-release || true"
run_shell "hostnamectl" "hostnamectl || true"
run_shell "uptime" "uptime"
run_shell "who" "who"
run_shell "session env" "printf 'DISPLAY=%s\nXDG_SESSION_TYPE=%s\nXAUTHORITY=%s\nXDG_RUNTIME_DIR=%s\n' '${DISPLAY:-unset}' '${XDG_SESSION_TYPE:-unknown}' '${XAUTHORITY:-unset}' '${XDG_RUNTIME_DIR:-unset}'"
have_cmd inxi && run_shell "inxi full" "inxi -Fxxxz"
run_shell "lscpu" "lscpu"
run_shell "cpu governor" "for f in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do [ -r \"\$f\" ] && echo \"\$f: \$(cat \"\$f\")\"; done"
run_shell "meminfo" "free -h && echo && cat /proc/meminfo"
run_shell "lsblk" "lsblk -o NAME,SIZE,MODEL,ROTA,TYPE,FSTYPE,MOUNTPOINTS"
run_shell "df" "df -hT"
run_shell "lspci -k" "lspci -k"
run_shell "lsusb" "lsusb"
have_cmd dmidecode && run_shell "dmidecode summary" "dmidecode -t system -t baseboard -t bios -t memory"
have_cmd sensors && run_shell "sensors" "sensors"
run_shell "root mount source" "findmnt -no SOURCE / || true"
have_cmd glxinfo && run_shell "glxinfo -B" "glxinfo -B"
have_cmd vulkaninfo && run_shell "vulkaninfo --summary" "vulkaninfo --summary"
}collect_storage_health() {
section "Storage health"
local disk
shopt -s nullglob
for disk in /dev/nvme*n1 /dev/sd? /dev/vd?; do
[[ -b "$disk" ]] || continue
have_cmd smartctl && run_shell "smartctl ${disk}" "smartctl -x '${disk}'"
have_cmd hdparm && run_shell "hdparm ${disk}" "hdparm -Tt '${disk}'"
done
shopt -u nullglob
}collect_boot_info() {
section "Boot analysis"
have_cmd systemd-analyze && run_shell "systemd-analyze" "systemd-analyze"
have_cmd systemd-analyze && run_shell "systemd-analyze blame" "systemd-analyze blame"
have_cmd systemd-analyze && run_shell "systemd-analyze critical-chain" "systemd-analyze critical-chain"
}run_cpu_benchmarks() {
section "CPU benchmarks"
have_cmd 7z && run_shell "7z benchmark" "timeout 300 7z b"
have_cmd sysbench && run_shell "sysbench cpu single-thread" "sysbench cpu --threads=1 --time=${CPU_TIME_SHORT} run"
have_cmd sysbench && run_shell "sysbench cpu all-threads" "sysbench cpu --threads=\$(nproc) --time=${CPU_TIME_SHORT} run"
have_cmd cryptsetup && run_shell "cryptsetup benchmark" "cryptsetup benchmark"
}run_memory_benchmarks() {
section "Memory benchmarks"
local mem_total
mem_total="$(get_memtest_total)"
log "Memory benchmark working set: ${mem_total}"
have_cmd sysbench && run_shell "sysbench memory read" "sysbench memory --threads=\$(nproc) --memory-oper=read --memory-total-size=${mem_total} run"
have_cmd sysbench && run_shell "sysbench memory write" "sysbench memory --threads=\$(nproc) --memory-oper=write --memory-total-size=${mem_total} run"
}run_disk_benchmarks() {
section "Filesystem/storage benchmarks"
local free_gb
free_gb="$(get_disk_free_gb)" log "Benchmark directory: ${OUTDIR}"
log "Requested disk test file size: ${DISK_TEST_SIZE_GB}G"
log "Free space under benchmark directory: ${free_gb}G" if ! have_cmd fio; then
log "fio not found. Skip fio tests."
return 0
fi if [[ -z "${free_gb}" ]] || (( free_gb < DISK_TEST_SIZE_GB + 2 )); then
log "Not enough free space. Skip disk performance tests."
return 0
fi run_shell "fio sequential read" \
"fio --filename='${TESTFILE}' --size=${DISK_TEST_SIZE_GB}G --direct=1 --rw=read --bs=1M --ioengine=libaio --iodepth=32 --runtime=${FIO_SEQ_RUNTIME} --numjobs=1 --time_based --group_reporting --name=seq-read" run_shell "fio sequential write" \
"fio --filename='${TESTFILE}' --size=${DISK_TEST_SIZE_GB}G --direct=1 --rw=write --bs=1M --ioengine=libaio --iodepth=32 --runtime=${FIO_SEQ_RUNTIME} --numjobs=1 --time_based --group_reporting --name=seq-write" run_shell "fio 4k mixed randrw" \
"fio --filename='${TESTFILE}' --size=${DISK_TEST_SIZE_GB}G --direct=1 --rw=randrw --rwmixread=70 --bs=4k --ioengine=libaio --iodepth=64 --runtime=${FIO_MIX_RUNTIME} --numjobs=4 --time_based --group_reporting --name=randrw-4k" run_shell "dd write" \
"dd if=/dev/zero of='${DD_FILE}' bs=1M count=2048 conv=fdatasync,notrunc status=progress" run_shell "drop caches" \
"sync; echo 3 > /proc/sys/vm/drop_caches" run_shell "dd read cold" \
"dd if='${DD_FILE}' of=/dev/null bs=1M count=2048 status=progress" run_shell "dd read hot" \
"dd if='${DD_FILE}' of=/dev/null bs=1M count=2048 status=progress"
}run_gpu_benchmarks() {
section "GPU / graphics benchmarks" if [[ -z "${DISPLAY:-}" ]]; then
log "No DISPLAY detected. Skip GUI benchmarks."
return 0
fi have_cmd glxinfo && run_shell "glxinfo -B (recheck)" "glxinfo -B"
have_cmd glmark2 && run_shell "glmark2" "timeout 300 glmark2"
have_cmd vkmark && run_shell "vkmark" "timeout 300 vkmark"
}run_network_benchmark() {
section "Network benchmark"
if [[ -z "${IPERF_SERVER}" ]]; then
log "IPERF_SERVER not set. Skip iperf3 throughput test."
return 0
fi if have_cmd iperf3; then
run_shell "iperf3" "iperf3 -c '${IPERF_SERVER}' -t 30 -P 4"
else
log "iperf3 not found. Skip network benchmark."
fi
}run_sustained_cpu_test() {
section "Sustained CPU stability / thermal observation"
have_cmd sensors && run_shell "sensors before sustained CPU test" "sensors"
have_cmd sysbench && run_shell "sysbench cpu sustained all-threads" "sysbench cpu --threads=\$(nproc) --time=${CPU_TIME_LONG} run"
have_cmd sensors && run_shell "sensors after sustained CPU test" "sensors"
}cleanup_files() {
section "Cleanup"
run_shell "remove temporary files" "rm -f '${TESTFILE}' '${DD_FILE}'"
}finish_report() {
section "Finished"
log "Completed at: $(date --iso-8601=seconds)"
log "Report saved to: ${REPORT}"
}main() {
need_root
print_header
check_and_install_tools
collect_basic_info
collect_storage_health
collect_boot_info
run_cpu_benchmarks
run_memory_benchmarks
run_disk_benchmarks
run_gpu_benchmarks
run_network_benchmark
run_sustained_cpu_test
cleanup_files
finish_report
}main "$@"

脚本结构为什么这样安排

这份脚本的重点不是“命令越多越好”,而是顺序和层次。

一、先做系统基线

基线采集负责记录:

  • 内核和发行版信息
  • CPU、内存、块设备信息
  • 当前挂载情况
  • 图形驱动栈
  • 当前温度
  • 当前会话变量

这一段不直接给出性能分数,但它决定了后面每个结果是否可解释。

例如:

  • glxinfo -B 能判断图形是否真在走硬件加速
  • lsblkfindmnt 能确认当前系统到底跑在哪块盘上
  • sensors 能确认测试开始前的温度环境

没有这些内容,后面的结果就容易断章取义。


二、把启动链单独拆出来

很多时候,桌面系统“感觉不够快”,并不是 CPU 或磁盘本身差,而是启动链里多了等待服务。

所以脚本里单独保留了:

  • systemd-analyze
  • systemd-analyze blame
  • systemd-analyze critical-chain

这几项的意义不在跑分,而在排查:

  • 哪些服务耗时长
  • 哪些服务在关键链上
  • 图形目标之前到底卡在哪一步

三、CPU 不只看一种工具

脚本同时使用:

  • 7z b
  • sysbench cpu
  • cryptsetup benchmark

这么搭配有几个好处:

7z b

更直观,适合快速感受 CPU 与内存配合下的压缩/解压能力。

sysbench cpu

更规整,适合前后对比。
尤其是:

  • 单线程
  • 全线程
  • 持续 300 秒

三段结合起来,基本就能判断:

  • 单核正常不正常
  • 多核扩展是否合理
  • 长时间满载会不会明显掉速

cryptsetup benchmark

顺手补充加密算法吞吐。
对于加密卷、备份压缩、某些校验型任务,这部分也有参考价值。


四、内存测试不需要复杂,但要稳定

脚本里内存部分只保留了 sysbench memory 的读写测试。
原因很简单:

  • 足够轻量
  • 易于自动化
  • 方便前后对比
  • 对桌面环境来说已经够用

这里不追求学术级内存带宽分析,而是追求稳定、统一、可重复。


五、存储部分为什么拆成四层

存储最容易出现“只看速度,不看健康”的问题。
所以脚本里把存储拆成四层。

1. smartctl

负责看健康状态。
主要关注:

  • 重映射
  • 待处理扇区
  • 不可恢复错误
  • 历史错误日志
  • 温度与异常断电

2. hdparm

适合快速扫顺序读取是否离谱。
但只适合初筛,不适合单独下最终结论。

3. fio

这是存储性能测试的核心。

脚本里分成:

  • 顺序读
  • 顺序写
  • 4K 混合随机读写

这样可以同时观察:

  • 大文件吞吐
  • 持续写入能力
  • 小块随机负载下的真实表现

4. dd

dd 不是为了代替 fio,而是补充冷热读写差异。
尤其是:

  • 冷读更接近盘本体
  • 热读更接近缓存命中

这在解释“明明第一次慢、第二次快很多”的现象时非常有用。


六、图形测试重点不是排行榜,而是驱动链是否正常

桌面 Linux 的图形测试,最重要的不是“分数绝对值”,而是先确认:

  • 没有落到软件渲染
  • OpenGL 正常
  • Vulkan 正常
  • 基础图形栈没有断

所以脚本里先用:

  • glxinfo -B
  • vulkaninfo --summary

再用:

  • glmark2
  • vkmark

这四项组合起来,基本足够确认桌面图形环境是否健康。

如果只是浏览器、视频、窗口动画、常规图形应用,那么这类检测已经足够回答“显卡和驱动是不是正常工作”。


七、持续 CPU 测试是必要项

短测只回答“瞬时性能高不高”。
持续测试才回答“能不能稳住”。

所以脚本最后单独保留了一次全线程持续 CPU 测试,并在前后各采一份传感器数据。
后续分析时,可以直接观察:

  • 测试前温度
  • 长时间满载后温度
  • 持续负载性能是否明显回落

对于日常使用来说,这比只看一个 60 秒分数更有意义。


脚本执行方式

最基本的执行方法:

chmod +x ./linux_benchmark_report.sh
sudo ./linux_benchmark_report.sh

如果需要测局域网吞吐:

IPERF_SERVER=192.168.1.10 sudo ./linux_benchmark_report.sh

如果需要调大磁盘测试文件:

DISK_TEST_SIZE_GB=6 sudo ./linux_benchmark_report.sh

如果需要单独指定报告目录:

REPORT_ROOT=/path/to/reports sudo ./linux_benchmark_report.sh

报告应该怎么读

报告生成之后,不建议直接翻到某个分数看一眼就结束。
更合理的顺序是:

先看系统基线

确认:

  • 当前系统盘是谁
  • 当前会话和图形变量是否正常
  • 当前驱动是否正确
  • 当前温度是否已经很高

再看启动分析

如果主观感觉“进桌面慢”,先排除是不是服务等待链的问题。

然后看 CPU、内存、图形

这部分决定主系统的核心性能是否正常。

最后看存储健康与存储性能

存储既要看健康,也要看速度。
只看其中一项,很容易误判。


这种脚本真正的价值

这类脚本真正有价值的地方,不是“今天测一次”,而是以后还能继续用。

它特别适合下面这些场景:

  • 更换系统盘之后复测
  • 更换显卡驱动之后复测
  • 升级内核之后复测
  • 更换硬件之后复测
  • 桌面环境大改动之后做留档
  • 把一台机器上的检测流程复制到另一台机器上

本质上,保存下来的不是“某次跑分”,而是一套能反复使用的诊断方法。


结语

桌面系统性能检测最稳妥的做法,不是临时记住几条命令,而是把整套流程写成脚本。

一份合格的脚本至少应该做到:

  • 缺失工具先检查
  • 能自动安装的尽量自动安装
  • 所有结果统一输出
  • 同时覆盖基线、启动、CPU、内存、存储、图形和持续负载
  • 不做破坏性测试
  • 方便下一次继续复用

这样得到的就不只是一次测试结果,而是一套长期可迁移的桌面性能检测方案。

Leave a Reply

Your email address will not be published. Required fields are marked *