sudo apt-get install stress-ng
stress-ng --cpu 0 --io 4 --vm 2 --vm-bytes 2G --timeout 8h --metrics-brief >stress-ng.log 2>&1 &
--cpu 0:使用所有可用 CPU 进行压力测试--io 4:启动 4 个 IO 工作线程--vm 2:启动 2 个内存压力线程--vm-bytes 2G:每个内存线程分配 2GB 内存--timeout 8h:测试持续 8 小时--metrics-brief:测试结束后输出简要统计信息想测试更全面的 I/O,可以将 --io 4 替换为 --iomix 4
切换到多用户(命令行)模式
sudo systemctl set-default multi-user.target
sudo reboot
重启后系统将进入命令行界面,不启动桌面环境
切换回图形界面(桌面模式)
sudo systemctl set-default graphical.target
sudo reboot
临时关闭图形界面
sudo systemctl stop display-manager
使用以下命令查看当前系统启动的是 multi-user.target 还是 graphical.target
systemctl get-default
输出结果为:
multi-user.target:当前为命令行模式graphical.target:当前为图形界面模式在 Ubuntu 22.04 上,普通用户默认通常 不能读取内核日志缓冲区,所以会报:
dmesg: read kernel buffer failed: Operation not permitted
让普通用户也能执行,先查看当前限制:
sysctl kernel.dmesg_restrict
如果输出是:
kernel.dmesg_restrict = 1
说明普通用户被禁止读取。
解决办法:
sudo sysctl -w kernel.dmesg_restrict=0
新建一个 sysctl 配置:
echo 'kernel.dmesg_restrict=0' | sudo tee /etc/sysctl.d/99-dmesg.conf
sudo sysctl --system
要满足“任何用户都可以执行,并看到其他用户占用情况”,建议用单文件 C 工具,安装为 setuid root。
原因是 Linux 上普通脚本通常不能安全地做 setuid,而且不提权时常常看不到别的用户进程的 /proc/<pid>/fd。
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <limits.h>
#include <pwd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#define CMD_BUF_SIZE 4096
struct device_entry {
char path[PATH_MAX];
dev_t rdev;
};
struct device_list {
struct device_entry *items;
size_t count;
size_t cap;
};
static bool is_numeric(const char *s) {
if (!s || !*s) return false;
while (*s) {
if (!isdigit((unsigned char)*s)) return false;
s++;
}
return true;
}
static void free_device_list(struct device_list *list) {
free(list->items);
list->items = NULL;
list->count = 0;
list->cap = 0;
}
static bool normalize_device_arg(const char *arg, char *out, size_t out_sz) {
if (!arg || !*arg) return false;
if (strncmp(arg, "/dev/", 5) == 0) {
return snprintf(out, out_sz, "%s", arg) < (int)out_sz;
}
return snprintf(out, out_sz, "/dev/%s", arg) < (int)out_sz;
}
static bool add_device(struct device_list *list, const char *path) {
struct stat st;
size_t i;
if (stat(path, &st) != 0) return false;
if (!S_ISCHR(st.st_mode)) return false;
for (i = 0; i < list->count; i++) {
if (strcmp(list->items[i].path, path) == 0) {
return true;
}
}
if (list->count == list->cap) {
size_t new_cap = list->cap ? list->cap * 2 : 8;
struct device_entry *new_items =
realloc(list->items, new_cap * sizeof(*new_items));
if (!new_items) return false;
list->items = new_items;
list->cap = new_cap;
}
snprintf(list->items[list->count].path,
sizeof(list->items[list->count].path), "%s", path);
list->items[list->count].rdev = st.st_rdev;
list->count++;
return true;
}
static bool load_all_ttyusb_devices(struct device_list *list) {
glob_t g;
size_t i;
int rc = glob("/dev/ttyUSB*", 0, NULL, &g);
if (rc == GLOB_NOMATCH) {
return true;
}
if (rc != 0) {
return false;
}
for (i = 0; i < g.gl_pathc; i++) {
add_device(list, g.gl_pathv[i]);
}
globfree(&g);
return true;
}
static ssize_t find_device_by_rdev(const struct device_list *list, dev_t rdev) {
size_t i;
for (i = 0; i < list->count; i++) {
if (list->items[i].rdev == rdev) {
return (ssize_t)i;
}
}
return -1;
}
static bool read_proc_uid(pid_t pid, uid_t *uid) {
char path[64];
char line[256];
FILE *fp;
snprintf(path, sizeof(path), "/proc/%ld/status", (long)pid);
fp = fopen(path, "r");
if (!fp) return false;
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "Uid:", 4) == 0) {
unsigned int r = 0, e = 0, s = 0, f = 0;
if (sscanf(line, "Uid:%u%u%u%u", &r, &e, &s, &f) >= 1) {
*uid = (uid_t)r;
fclose(fp);
return true;
}
}
}
fclose(fp);
return false;
}
static bool read_proc_cmd(pid_t pid, char *buf, size_t buf_sz) {
char path[64];
int fd;
ssize_t n;
size_t i;
FILE *fp;
if (buf_sz == 0) return false;
buf[0] = '\0';
snprintf(path, sizeof(path), "/proc/%ld/cmdline", (long)pid);
fd = open(path, O_RDONLY);
if (fd >= 0) {
n = read(fd, buf, buf_sz - 1);
close(fd);
if (n > 0) {
for (i = 0; i < (size_t)n; i++) {
if (buf[i] == '\0') buf[i] = ' ';
}
while (n > 0 &&
(buf[n - 1] == ' ' || buf[n - 1] == '\n' || buf[n - 1] == '\r')) {
n--;
}
buf[n] = '\0';
return true;
}
}
snprintf(path, sizeof(path), "/proc/%ld/comm", (long)pid);
fp = fopen(path, "r");
if (!fp) return false;
if (!fgets(buf, buf_sz, fp)) {
fclose(fp);
return false;
}
fclose(fp);
for (i = 0; buf[i]; i++) {
if (buf[i] == '\n' || buf[i] == '\r') {
buf[i] = '\0';
break;
}
}
return true;
}
static int scan_one_pid(pid_t pid,
const struct device_list *devices,
unsigned long *permission_denied_count) {
char fd_dir_path[64];
DIR *dir;
struct dirent *de;
bool *seen;
size_t i;
int hit_count = 0;
if (devices->count == 0) return 0;
snprintf(fd_dir_path, sizeof(fd_dir_path), "/proc/%ld/fd", (long)pid);
dir = opendir(fd_dir_path);
if (!dir) {
if (errno == EACCES || errno == EPERM) {
(*permission_denied_count)++;
}
return 0;
}
seen = calloc(devices->count, sizeof(bool));
if (!seen) {
closedir(dir);
return 0;
}
while ((de = readdir(dir)) != NULL) {
char fd_path[PATH_MAX];
struct stat st;
ssize_t idx;
if (!is_numeric(de->d_name)) continue;
if (snprintf(fd_path, sizeof(fd_path), "%s/%s", fd_dir_path, de->d_name) >=
(int)sizeof(fd_path)) {
continue;
}
if (stat(fd_path, &st) != 0) continue;
if (!S_ISCHR(st.st_mode)) continue;
idx = find_device_by_rdev(devices, st.st_rdev);
if (idx >= 0) {
seen[idx] = true;
}
}
closedir(dir);
for (i = 0; i < devices->count; i++) {
if (seen[i]) {
hit_count++;
}
}
if (hit_count > 0) {
uid_t uid;
char user_buf[64] = "?";
char uid_buf[32] = "?";
char cmd_buf[CMD_BUF_SIZE] = "?";
if (read_proc_uid(pid, &uid)) {
struct passwd *pw = getpwuid(uid);
snprintf(uid_buf, sizeof(uid_buf), "%u", (unsigned int)uid);
if (pw && pw->pw_name) {
snprintf(user_buf, sizeof(user_buf), "%s", pw->pw_name);
} else {
snprintf(user_buf, sizeof(user_buf), "%s", uid_buf);
}
}
read_proc_cmd(pid, cmd_buf, sizeof(cmd_buf));
for (i = 0; i < devices->count; i++) {
if (seen[i]) {
printf("%-16s %-16s %-8s %-8ld %s\n",
devices->items[i].path,
user_buf,
uid_buf,
(long)pid,
cmd_buf);
}
}
}
free(seen);
return hit_count;
}
static void usage(const char *prog) {
fprintf(stderr,
"Usage:\n"
" %s -a|--all Scan all /dev/ttyUSB*\n"
" %s /dev/ttyUSB0 Scan one device\n"
" %s ttyUSB0 ttyUSB1 Scan selected devices\n"
" %s Default: scan all /dev/ttyUSB*\n",
prog, prog, prog, prog);
}
int main(int argc, char **argv) {
struct device_list devices = {0};
DIR *proc_dir;
struct dirent *de;
int total_hits = 0;
unsigned long permission_denied_count = 0;
bool scan_all = false;
int requested_devices = 0;
int i;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
usage(argv[0]);
free_device_list(&devices);
return 0;
} else if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--all")) {
scan_all = true;
} else if (argv[i][0] == '-') {
fprintf(stderr, "Error: unknown option: %s\n", argv[i]);
usage(argv[0]);
free_device_list(&devices);
return 2;
} else {
char path[PATH_MAX];
requested_devices++;
if (!normalize_device_arg(argv[i], path, sizeof(path))) {
fprintf(stderr, "Warning: invalid device argument ignored: %s\n", argv[i]);
continue;
}
if (!add_device(&devices, path)) {
fprintf(stderr, "Warning: not found or not a character device: %s\n", path);
}
}
}
if (scan_all && requested_devices > 0) {
fprintf(stderr, "Error: --all cannot be used together with explicit device names\n");
free_device_list(&devices);
return 2;
}
if (scan_all || requested_devices == 0) {
free_device_list(&devices);
if (!load_all_ttyusb_devices(&devices)) {
fprintf(stderr, "Error: failed to enumerate /dev/ttyUSB*\n");
return 2;
}
}
if (devices.count == 0) {
printf("No /dev/ttyUSB* devices found\n");
free_device_list(&devices);
return 1;
}
proc_dir = opendir("/proc");
if (!proc_dir) {
fprintf(stderr, "Error: cannot open /proc\n");
free_device_list(&devices);
return 2;
}
printf("%-16s %-16s %-8s %-8s %s\n",
"DEVICE", "USER", "UID", "PID", "COMMAND");
while ((de = readdir(proc_dir)) != NULL) {
pid_t pid;
if (!is_numeric(de->d_name)) continue;
pid = (pid_t)strtol(de->d_name, NULL, 10);
total_hits += scan_one_pid(pid, &devices, &permission_denied_count);
}
closedir(proc_dir);
free_device_list(&devices);
if (total_hits == 0) {
printf("No holders found\n");
}
if (permission_denied_count > 0) {
fprintf(stderr,
"Warning: %lu process(es) could not be inspected due to permission restrictions.\n"
" For cross-user detection, run as root or install this binary as setuid-root (mode 4755).\n",
permission_denied_count);
}
return total_hits > 0 ? 0 : 1;
}
编译:
gcc -O2 -Wall -Wextra -o ttyusb_who ttyusb_who.c
如果要支持任意用户查看其他用户占用,需要安装成 setuid root:
sudo install -o root -g root -m 4755 ttyusb_who /usr/local/bin/ttyusb_who
用法:
ttyusb_who -a
ttyusb_who --all
ttyusb_who /dev/ttyUSB1
ttyusb_who ttyUSB0 ttyUSB1
补充一点:
4755 这一步不能省setuid root 的意思是:普通用户执行时,程序临时以 root 有效权限运行4755 是 八进制权限拆开看:
4:setuid7:owner = rwx5:group = r-x5:others = r-x也就是:
4755 = setuid + rwxr-xr-x
一般用户都不会被添加到sudo组里面(可以执行任何命令),没法使用sudo mount挂载nfs,但是mount命令必须要以root身份才能运行, 因此导致mount功能对普通用户的使用极其受限。
解决方案:
只给普通用户开启sudo mount执行权限
sudo visudo
在/etc/sudoers里面添加
Defaults editor=/usr/bin/vim
这样下次打开sudoers文件就是以vim的方法,不是默认的nano,

或则直接在/etc/profile里面添加export EDITOR=/usr/bin/vim这行,
在/etc/sudoers文件里面添加如下两行:
# Allow members of group public_share to execute mount/umount command
%public_share ALL=(ALL) NOPASSWD: /usr/bin/mount, /usr/sbin/mount.nfs, /usr/sbin/umount.nfs, /usr/bin/umount
把普通用户添加到public_share组里面,这样就可以执行mount的操作了, 这个组只授权mount命令的执行权限,因此不会出现权限过大的问题

多用户公共目录方案,可以同时支持 Samba、NFS、Apache 三种访问方式,任何人都能读写 / 下载,且权限不会乱。
sudo apt-get install -y apache2 nfs-kernel-server samba samba-common-bin
1. 创建目录
sudo mkdir -p /srv/public_share
2. 创建公共用户组
777 权限虽然简单,但文件属主会很乱,推荐用统一用户组管理
sudo groupadd public_share
# 把需要访问的用户加入组(比如charleye)
sudo usermod -aG public_share charleye
sudo usermod -aG public_share root
3. 设置目录权限
sudo chown root:public_share /srv/public_share
sudo chmod 775 /srv/public_share
# 设置默认文件权限:新建文件664,目录775
sudo setfacl -d -m g:public_share:rwx /srv/public_share
sudo setfacl -d -m o:r-x /srv/public_share
1. 编辑 exports 文件
sudo vim /etc/exports
添加:
/srv/public_share *(rw,sync,no_subtree_check,no_root_squash,insecure)
rw:读写no_root_squash:客户端 root 能正常读写insecure:允许非特权端口访问(避免 502/permission denied)2. 生效并重启服务
sudo exportfs -ra
sudo systemctl restart nfs-kernel-server
sudo systemctl enable nfs-kernel-server
3. 客户端测试(Linux)
sudo mkdir -p /mnt/public
sudo mount -t nfs ipaddr:/srv/public_share /mnt/public
1. 编辑 smb.conf文件
sudo vim /etc/samba/smb.conf
添加
[public_share]
comment = Public Installer Share
path = /srv/public_share
browsable = yes
writable = yes
create mask = 0664
directory mask = 0775
force group = public_share
这样所有用户写入的文件都属于 public_share 组,大家都能读写。
2. 重启服务
sudo systemctl restart smbd nmbd
sudo systemctl enable smbd nmbd
3. 测试(Windows)
文件资源管理器输入:
\\ipaddr\public_share
直接就能读写文件。
sudo vim /etc/apache2/sites-available/000-default.conf
把里面的内容,完整替换成下面这段(直接复制):
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /srv/public_share
<Directory /srv/public_share>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Options Indexes 就是让浏览器列出目录里的文件,方便别人直接下载安装包。2. 重启 Apache 生效
sudo systemctl restart apache2
3. 配置权限
sudo usermod -aG public_share www-data
4. 浏览器访问测试
http://ipaddr
注意:ipaddr替换成你的实际IP地址