从 Thread.setName 到内核:Java 线程命名的完整穿越之旅
在日常开发中,我们常常会调用 thread.setName("worker-1") 来为线程命名。这个看似简单的操作,实际上穿越了 Java 层、JVM 层、glibc 层,最终抵达 Linux 内核。本文将结合 OpenJDK、glibc 和 Linux Kernel 的源码,完整还原一个线程名从用户态 String 变为内核 task_struct.comm 的整个旅程。
一、Java 层的入口:Thread.setName
一切始于 java.lang.Thread 中的 setName 方法:
java
public final synchronized void setName(String name) {
checkAccess(); // ① 安全管理器检查(通常为空)
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name; // ② 更新 Java 层的线程名字段
if (threadStatus != 0) { // ③ 仅当线程已启动时才同步到 native 层
setNativeName(name);
}
}
-
synchronized确保多线程环境下修改变量的安全性。 -
checkAccess()留给SecurityManager做权限校验,现代应用几乎忽略。 -
threadStatus是 JVM 内部维护的线程状态字段,0 代表NEW(尚未启动)。
因此,如果线程尚未调用start(),setNativeName根本不会执行——线程名只更新到 Java 层,不会同步到底层操作系统。
这也是许多开发者困惑的地方:为什么setName后top -H看到的仍然是"Thread-0"?原因就是你没有在线程启动之后调用setName。
当线程已经启动(threadStatus != 0)时,会调用 private native void setNativeName(String name)。这个 native 方法通过 JNI 连接到 HotSpot 虚拟机。
二、JNI 方法注册:连接 Java 与 JVM
在 Thread.c 中,定义了一个 JNI 方法表,将 Java 的 native 方法映射到 JVM 内部的 C 函数:
c
static JNINativeMethod methods[] = {
// ... 其他映射 ...
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
// ...
};
当 Java 代码第一次调用 setNativeName 时,JVM 会查找这张表并调用对应的 JVM_SetNativeThreadName 函数。
三、HotSpot 层的实现:JVM_SetNativeThreadName
JVM_SetNativeThreadName 位于 jvm.cpp 中,使用 JVM_ENTRY 宏定义:
c
JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name))
// 注释:此处不使用 ThreadsListHandle,因为当前线程必须存活
oop java_thread = JNIHandles::resolve_non_null(jthread); // ① JNI引用 → oop
JavaThread* thr = java_lang_Thread::thread(java_thread); // ② 获取内部的 JavaThread*
// ③ 关键限制:仅允许为当前线程改名,且不能是通过 JNI 附加的线程
if (thread == thr && !thr->has_attached_via_jni()) {
ResourceMark rm(thread);
// ④ 将 Java String 转为 C 字符串
const char *thread_name = java_lang_String::as_utf8_string(
JNIHandles::resolve_non_null(name));
os::set_native_thread_name(thread_name); // ⑤ 调用 OS 层接口
}
JVM_END
-
JNIHandles::resolve_non_null将 JNI 引用解析为 HotSpot 内部的oop(普通对象指针)。 -
java_lang_Thread::thread从 JavaThread对象中取出对应的JavaThread*(HotSpot 对原生线程的 C++ 封装)。 -
权限控制:要求
thread == thr(当前线程只能改自己的名字),并且!thr->has_attached_via_jni()(排除通过 JNI 附加到 JVM 的外部线程)。后者的设计目的是避免破坏外部程序(如 C++ 主程序)对线程名的管理。 -
最后调用
os::set_native_thread_name,进入操作系统适配层。
四、操作系统适配层:os::set_native_thread_name
os::set_native_thread_name 是平台相关的函数,在 os_linux.cpp 中实现:
c
void os::set_native_thread_name(const char *name) {
if (Linux::_pthread_setname_np) { // ① 检查函数指针是否可用
char buf [16]; // ② glibc/内核限制:最多15字符+'\0'
snprintf(buf, sizeof(buf), "%s", name);
buf[sizeof(buf) - 1] = '\0';
const int rc = Linux::_pthread_setname_np(pthread_self(), buf);
assert(rc != ERANGE, "pthread_setname_np failed");
}
}
这里的 Linux::_pthread_setname_np 是一个函数指针,在 JVM 初始化时通过 dlsym 动态获取:
c
// 在 os::init(void) 中
void os::init(void) {
// ... 其他初始化 ...
Linux::_pthread_setname_np =
(int(*)(pthread_t, const char*))dlsym(RTLD_DEFAULT, "pthread_setname_np");
// ...
}
这种动态加载的方式保证了 JVM 能够兼容不同版本的 glibc:如果系统不支持 pthread_setname_np,则直接跳过,不会导致 JVM 崩溃。
五、glibc 的 pthread_setname_np:分支处理
pthread_setname_np 是 glibc 提供的非标准扩展(_np 表示 non-portable)。它的实现位于 nptl/pthread_setname.c,核心逻辑如下:
c
int __pthread_setname_np(pthread_t th, const char *name) {
const struct pthread *pd = (const struct pthread *) th;
#define TASK_COMM_LEN 16
size_t name_len = strlen(name);
if (name_len >= TASK_COMM_LEN) // 名称过长,返回错误
return ERANGE;
if (pd == THREAD_SELF) { // 情况1:为当前线程改名
return __prctl(PR_SET_NAME, name) ? errno : 0;
}
// 情况2:为其他线程改名 → 通过 /proc 文件系统
#define FMT "/proc/self/task/%u/comm"
char fname[sizeof(FMT) + 8];
sprintf(fname, FMT, (unsigned int) pd->tid);
int fd = __open64_nocancel(fname, O_RDWR);
if (fd == -1)
return errno;
int res = 0;
ssize_t n = TEMP_FAILURE_RETRY(__write_nocancel(fd, name, name_len));
if (n < 0)
res = errno;
else if (n != name_len)
res = EIO;
__close_nocancel_nostatus(fd);
return res;
}
-
长度检查:内核的
TASK_COMM_LEN为 16,因此线程名最长 15 个可见字符。 -
当前线程:直接调用
__prctl(PR_SET_NAME, name),这是最轻量、最高效的方式。 -
其他线程:打开
/proc/self/task/[tid]/comm文件,然后写入新名称。/proc下的每个线程都有一个comm文件,可读可写,这是一个内核提供的统一接口。
__write_nocancel 的内联系统调用
c
ssize_t __write_nocancel(int fd, const void *buf, size_t nbytes) {
return INLINE_SYSCALL_CALL(write, fd, buf, nbytes);
}
经过多层宏展开,最终在 x86-64 上生成内联汇编 syscall 指令,触发系统调用,陷入内核。
六、内核路径一:prctl 系统调用(当前线程)
当 glibc 为当前线程调用 __prctl(PR_SET_NAME, name) 时,最终会进入内核的 sys_prctl 函数(定义在 kernel/sys.c 中)。用户提供的源码中展示了完整的 prctl 处理流程,我们聚焦于 PR_SET_NAME 分支:
c
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, ...) {
struct task_struct *me = current;
unsigned char comm[sizeof(me->comm)];
// ... 其他选项 ...
switch (option) {
// ...
case PR_SET_NAME:
comm[sizeof(me->comm) - 1] = 0;
// 从用户空间拷贝新的线程名(最多15个字符)
if (strncpy_from_user(comm, (char __user *)arg2,
sizeof(me->comm) - 1) < 0)
return -EFAULT;
set_task_comm(me, comm); // ① 核心:设置线程名
proc_comm_connector(me); // ② 通知 proc 文件系统
break;
// ...
}
return error;
}
set_task_comm 是一个内联函数,最终调用 __set_task_comm。
七、内核路径二:write 到 /proc/.../comm(其他线程)
当目标线程不是当前线程时,glibc 会打开 /proc/self/task/[tid]/comm 并写入。这个写操作进入内核的 VFS 层,最终由 procfs 的 comm_write 函数处理。用户提供的源码中包含了关键结构:
c
static const struct file_operations proc_pid_set_comm_operations = {
.open = comm_open,
.read = seq_read,
.write = comm_write, // 写操作被映射到 comm_write
.llseek = seq_lseek,
.release= single_release,
};
comm_write 的实现如下:
c
static ssize_t comm_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset) {
struct inode *inode = file_inode(file);
struct task_struct *p;
char buffer[TASK_COMM_LEN] = {}; // 内核缓冲区
const size_t maxlen = sizeof(buffer) - 1;
// ① 从用户空间拷贝名称
if (copy_from_user(buffer, buf, count > maxlen ? maxlen : count))
return -EFAULT;
p = get_proc_task(inode); // ② 获取目标线程的 task_struct
if (!p)
return -ESRCH;
// ③ 只能修改同一线程组(即同一进程)内的线程名
if (same_thread_group(current, p)) {
set_task_comm(p, buffer); // ④ 更新 comm 字段
proc_comm_connector(p);
} else
count = -EINVAL;
put_task_struct(p);
return count;
}
-
same_thread_group检查确保只能修改同一进程内的线程名(不能跨进程修改)。 -
最终仍然调用
set_task_comm,与prctl路径殊途同归。
VFS 层的 write 路径回顾
用户提供的源码中也包含了 vfs_write 的实现,它负责调用具体文件系统的 write 方法:
c
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) {
// ... 权限检查、长度限制等 ...
if (file->f_op->write)
ret = file->f_op->write(file, buf, count, pos); // 调用 proc_pid_set_comm_operations.write
else if (file->f_op->write_iter)
ret = new_sync_write(file, buf, count, pos);
else
ret = -EINVAL;
// ...
}
因此,无论是 prctl 还是写入 /proc/.../comm,最终都汇聚到 set_task_comm。
八、最终归宿:__set_task_comm
set_task_comm 是一个简单的包装,直接调用 __set_task_comm(位于 kernel/sys.c 或 fs/exec.c 相关位置)。用户提供的源码中包含了自定义调试版本:
c
void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec) {
// 用户添加的调试输出(可通过 dynamic_debug 开启)
pr_debug("yym-gaizao set_task_comm: %s (pid=%d) new comm: %s\n",
tsk->comm, tsk->pid, buf);
task_lock(tsk);
trace_task_rename(tsk, buf); // 跟踪点,供 ftrace 使用
strscpy_pad(tsk->comm, buf, sizeof(tsk->comm)); // 安全拷贝,最多15字符+'\0'
task_unlock(tsk);
perf_event_comm(tsk, exec); // 通知 perf 子系统
}
-
task_lock保护tsk->comm的并发访问。 -
strscpy_pad确保字符串以 null 结尾,剩余空间填充 null,防止信息泄露。 -
perf_event_comm通知性能事件子系统,使得perf record等工具能够记录线程名变化。
至此,新的线程名已经被写入内核的 task_struct 结构体中的 comm 数组。此后,通过 ps -T -p <pid>、top -H、cat /proc/<pid>/task/<tid>/comm 等工具都能看到更新后的名字。
九、完整调用链路图
text
Java: thread.setName("my-worker")
│ (threadStatus != 0)
↓
JNI: setNativeName()
↓
HotSpot: JVM_SetNativeThreadName (检查:当前线程 && 非JNI附加)
↓
os_linux: os::set_native_thread_name()
↓
glibc: pthread_setname_np()
├─ 当前线程 → __prctl(PR_SET_NAME)
│ ↓
│ 内核: sys_prctl → PR_SET_NAME → set_task_comm
└─ 其他线程 → open("/proc/self/task/tid/comm") → write
↓
内核: sys_write → vfs_write → proc_pid_set_comm_operations.write
→ comm_write → set_task_comm
↓
__set_task_comm → strscpy_pad(tsk->comm, ...)
十、重要结论与注意事项
-
线程名长度限制:Linux 内核中
TASK_COMM_LEN为 16,因此线程名最长 15 个有效字符(含结尾'\0')。超出会被截断或返回ERANGE。JVM 中使用char buf[16]正是遵循这一限制。 -
修改时机至关重要:
-
在
start()之前调用setName:只更新 Java 层,不触发setNativeName。 -
在
start()之后调用setName:才会走完整链路,同步到操作系统。
因此,如果需要通过top -H或perf等工具观察到自定义线程名,必须在线程启动后设置名称(或者在启动前设置后,再在run()方法开头再次调用setName)。
-
-
安全性限制:
-
JVM 中
JVM_SetNativeThreadName只允许当前线程修改自己的名字,无法通过一个 Java 线程修改另一个线程的 OS 名称。 -
通过 JNI 附加的外部线程不会被 JVM 改名,避免干扰外部程序。
-
-
跨进程无法修改:内核的
comm_write通过same_thread_group检查,只允许修改同一进程内的线程名。这是合理的,因为不同进程的地址空间隔离,task_struct属于不同进程。 -
调试技巧:用户提供的源码中在
__set_task_comm内添加了pr_debug,可以通过内核动态调试功能(dynamic_debug)实时查看线程改名事件,无需重新编译内核。例如:bash
echo 'file kernel/sys.c +p' > /sys/kernel/debug/dynamic_debug/control
-
/proc伪文件的本质:/proc/self/task/*/comm只存在于内存中,不占用磁盘空间,重启后所有修改都会丢失。它只是内核提供的一种便捷接口,背后读写的是内核数据结构。
十一、总结
从 Thread.setName 到内核 task_struct.comm,这趟旅程跨越了 Java 虚拟机、C 运行时库和操作系统内核。每一层都有各自的设计哲学:Java 层注重安全与易用性,JVM 层做了严格的权限控制,glibc 层提供了兼容不同场景的分支实现,而内核则通过统一的 comm 字段和 proc 接口,简洁地完成了最终的存储。
理解这条完整的调用链,不仅能帮助我们写出更高效的多线程代码,还能在遇到线程名相关问题(如 jstack 与 ps 看到的名称不一致)时,快速定位根因。希望这篇博客能让你对 Java 线程命名的“内幕”有更深入的认识。
##源码
public final synchronized void setName(String name) {
checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
if (threadStatus != 0) {
setNativeName(name);
}
}
private native void setNativeName(String name);
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield", "()V", (void *)&JVM_Yield},
{"sleep", "(J)V", (void *)&JVM_Sleep},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};
JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name))
// We don't use a ThreadsListHandle here because the current thread
// must be alive.
oop java_thread = JNIHandles::resolve_non_null(jthread);
JavaThread* thr = java_lang_Thread::thread(java_thread);
if (thread == thr && !thr->has_attached_via_jni()) {
// Thread naming is only supported for the current thread and
// we don't set the name of an attached thread to avoid stepping
// on other programs.
ResourceMark rm(thread);
const char *thread_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name));
os::set_native_thread_name(thread_name);
}
JVM_END
void os::set_native_thread_name(const char *name) {
if (Linux::_pthread_setname_np) {
char buf [16]; // according to glibc manpage, 16 chars incl. '/0'
snprintf(buf, sizeof(buf), "%s", name);
buf[sizeof(buf) - 1] = '\0';
const int rc = Linux::_pthread_setname_np(pthread_self(), buf);
// ERANGE should not happen; all other errors should just be ignored.
assert(rc != ERANGE, "pthread_setname_np failed");
}
}
// this is called _before_ most of the global arguments have been parsed
void os::init(void) {
char dummy; // used to get a guess on initial stack address
clock_tics_per_sec = sysconf(_SC_CLK_TCK);
Linux::set_page_size(sysconf(_SC_PAGESIZE));
if (Linux::page_size() == -1) {
fatal("os_linux.cpp: os::init: sysconf failed (%s)",
os::strerror(errno));
}
_page_sizes.add(Linux::page_size());
Linux::initialize_system_info();
#ifdef __GLIBC__
Linux::_mallinfo = CAST_TO_FN_PTR(Linux::mallinfo_func_t, dlsym(RTLD_DEFAULT, "mallinfo"));
Linux::_mallinfo2 = CAST_TO_FN_PTR(Linux::mallinfo2_func_t, dlsym(RTLD_DEFAULT, "mallinfo2"));
#endif // __GLIBC__
os::Linux::CPUPerfTicks pticks;
bool res = os::Linux::get_tick_information(&pticks, -1);
if (res && pticks.has_steal_ticks) {
has_initial_tick_info = true;
initial_total_ticks = pticks.total;
initial_steal_ticks = pticks.steal;
}
// _main_thread points to the thread that created/loaded the JVM.
Linux::_main_thread = pthread_self();
// retrieve entry point for pthread_setname_np
Linux::_pthread_setname_np =
(int(*)(pthread_t, const char*))dlsym(RTLD_DEFAULT, "pthread_setname_np");
check_pax();
os::Posix::init();
initial_time_count = javaTimeNanos();
}
int
__pthread_setname_np (pthread_t th, const char *name)
{
const struct pthread *pd = (const struct pthread *) th;
/* Unfortunately the kernel headers do not export the TASK_COMM_LEN
macro. So we have to define it here. */
#define TASK_COMM_LEN 16
size_t name_len = strlen (name);
if (name_len >= TASK_COMM_LEN)
return ERANGE;
if (pd == THREAD_SELF)
return __prctl (PR_SET_NAME, name) ? errno : 0;
#define FMT "/proc/self/task/%u/comm"
char fname[sizeof (FMT) + 8];
sprintf (fname, FMT, (unsigned int) pd->tid);
int fd = __open64_nocancel (fname, O_RDWR);
if (fd == -1)
return errno;
int res = 0;
ssize_t n = TEMP_FAILURE_RETRY (__write_nocancel (fd, name, name_len));
if (n < 0)
res = errno;
else if (n != name_len)
res = EIO;
__close_nocancel_nostatus (fd);
return res;
}
versioned_symbol (libc, __pthread_setname_np, pthread_setname_np,
GLIBC_2_34);
ssize_t
__write_nocancel (int fd, const void *buf, size_t nbytes)
{
return INLINE_SYSCALL_CALL (write, fd, buf, nbytes);
}
hidden_def (__write_nocancel)
#define INLINE_SYSCALL_CALL(...) \
__INLINE_SYSCALL_DISP (__INLINE_SYSCALL, __VA_ARGS__)
#define __INLINE_SYSCALL_DISP(b,...) \
__SYSCALL_CONCAT (b,__INLINE_SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
#define __INLINE_SYSCALL3(name, a1, a2, a3) \
INLINE_SYSCALL (name, 3, a1, a2, a3)
#undef INTERNAL_SYSCALL
#define INTERNAL_SYSCALL(name, nr, args...) \
internal_syscall##nr (SYS_ify (name), args)
#undef INTERNAL_SYSCALL_NCS
#define INTERNAL_SYSCALL_NCS(number, nr, args...) \
internal_syscall##nr (number, args)
#undef internal_syscall3
#define internal_syscall3(number, arg1, arg2, arg3) \
({ \
unsigned long int resultvar; \
TYPEFY (arg3, __arg3) = ARGIFY (arg3); \
TYPEFY (arg2, __arg2) = ARGIFY (arg2); \
TYPEFY (arg1, __arg1) = ARGIFY (arg1); \
register TYPEFY (arg3, _a3) asm ("rdx") = __arg3; \
register TYPEFY (arg2, _a2) asm ("rsi") = __arg2; \
register TYPEFY (arg1, _a1) asm ("rdi") = __arg1; \
asm volatile ( \
"syscall\n\t" \
: "=a" (resultvar) \
: "0" (number), "r" (_a1), "r" (_a2), "r" (_a3) \
: "memory", REGISTERS_CLOBBERED_BY_SYSCALL); \
(long int) resultvar; \
})
##系统调用
1 common write sys_write
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
size_t, count)
{
return ksys_write(fd, buf, count);
}
ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count)
{
struct fd f = fdget_pos(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos, *ppos = file_ppos(f.file);
if (ppos) {
pos = *ppos;
ppos = &pos;
}
ret = vfs_write(f.file, buf, count, ppos);
if (ret >= 0 && ppos)
f.file->f_pos = pos;
fdput_pos(f);
}
return ret;
}
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
ssize_t ret;
if (!(file->f_mode & FMODE_WRITE))
return -EBADF;
if (!(file->f_mode & FMODE_CAN_WRITE))
return -EINVAL;
if (unlikely(!access_ok(buf, count)))
return -EFAULT;
ret = rw_verify_area(WRITE, file, pos, count);
if (ret)
return ret;
if (count > MAX_RW_COUNT)
count = MAX_RW_COUNT;
file_start_write(file);
if (file->f_op->write)
ret = file->f_op->write(file, buf, count, pos);
else if (file->f_op->write_iter)
ret = new_sync_write(file, buf, count, pos);
else
ret = -EINVAL;
if (ret > 0) {
fsnotify_modify(file);
add_wchar(current, ret);
}
inc_syscw(current);
file_end_write(file);
return ret;
}
static const struct file_operations proc_pid_set_comm_operations = {
.open = comm_open,
.read = seq_read,
.write = comm_write,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t comm_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
{
struct inode *inode = file_inode(file);
struct task_struct *p;
char buffer[TASK_COMM_LEN] = {};
const size_t maxlen = sizeof(buffer) - 1;
if (copy_from_user(buffer, buf, count > maxlen ? maxlen : count))
return -EFAULT;
p = get_proc_task(inode);
if (!p)
return -ESRCH;
if (same_thread_group(current, p)) {
set_task_comm(p, buffer);
proc_comm_connector(p);
}
else
count = -EINVAL;
put_task_struct(p);
return count;
}
##当前线程修改名字
/* Unconditionally read all potential arguments. This may pass
garbage values to the kernel, but avoids the need for teaching
glibc the argument counts of individual options (including ones
that are added to the kernel in the future). */
int
__prctl (int option, ...)
{
va_list arg;
va_start (arg, option);
unsigned long int arg2 = va_arg (arg, unsigned long int);
unsigned long int arg3 = va_arg (arg, unsigned long int);
unsigned long int arg4 = va_arg (arg, unsigned long int);
unsigned long int arg5 = va_arg (arg, unsigned long int);
va_end (arg);
return INLINE_SYSCALL_CALL (prctl, option, arg2, arg3, arg4, arg5);
}
libc_hidden_def (__prctl)
weak_alias (__prctl, prctl)
#if __TIMESIZE != 64
weak_alias (__prctl, __prctl_time64)
#endif
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
unsigned long, arg4, unsigned long, arg5)
{
struct task_struct *me = current;
unsigned char comm[sizeof(me->comm)];
long error;
error = security_task_prctl(option, arg2, arg3, arg4, arg5);
if (error != -ENOSYS)
return error;
error = 0;
switch (option) {
case PR_SET_PDEATHSIG:
if (!valid_signal(arg2)) {
error = -EINVAL;
break;
}
me->pdeath_signal = arg2;
break;
case PR_GET_PDEATHSIG:
error = put_user(me->pdeath_signal, (int __user *)arg2);
break;
case PR_GET_DUMPABLE:
error = get_dumpable(me->mm);
break;
case PR_SET_DUMPABLE:
if (arg2 != SUID_DUMP_DISABLE && arg2 != SUID_DUMP_USER) {
error = -EINVAL;
break;
}
set_dumpable(me->mm, arg2);
break;
case PR_SET_UNALIGN:
error = SET_UNALIGN_CTL(me, arg2);
break;
case PR_GET_UNALIGN:
error = GET_UNALIGN_CTL(me, arg2);
break;
case PR_SET_FPEMU:
error = SET_FPEMU_CTL(me, arg2);
break;
case PR_GET_FPEMU:
error = GET_FPEMU_CTL(me, arg2);
break;
case PR_SET_FPEXC:
error = SET_FPEXC_CTL(me, arg2);
break;
case PR_GET_FPEXC:
error = GET_FPEXC_CTL(me, arg2);
break;
case PR_GET_TIMING:
error = PR_TIMING_STATISTICAL;
break;
case PR_SET_TIMING:
if (arg2 != PR_TIMING_STATISTICAL)
error = -EINVAL;
break;
case PR_SET_NAME:
comm[sizeof(me->comm) - 1] = 0;
if (strncpy_from_user(comm, (char __user *)arg2,
sizeof(me->comm) - 1) < 0)
return -EFAULT;
set_task_comm(me, comm);
proc_comm_connector(me);
break;
case PR_GET_NAME:
get_task_comm(comm, me);
if (copy_to_user((char __user *)arg2, comm, sizeof(comm)))
return -EFAULT;
break;
case PR_GET_ENDIAN:
error = GET_ENDIAN(me, arg2);
break;
case PR_SET_ENDIAN:
error = SET_ENDIAN(me, arg2);
break;
case PR_GET_SECCOMP:
error = prctl_get_seccomp();
break;
case PR_SET_SECCOMP:
error = prctl_set_seccomp(arg2, (char __user *)arg3);
break;
case PR_GET_TSC:
error = GET_TSC_CTL(arg2);
break;
case PR_SET_TSC:
error = SET_TSC_CTL(arg2);
break;
case PR_TASK_PERF_EVENTS_DISABLE:
error = perf_event_task_disable();
break;
case PR_TASK_PERF_EVENTS_ENABLE:
error = perf_event_task_enable();
break;
case PR_GET_TIMERSLACK:
if (current->timer_slack_ns > ULONG_MAX)
error = ULONG_MAX;
else
error = current->timer_slack_ns;
break;
case PR_SET_TIMERSLACK:
if (arg2 <= 0)
current->timer_slack_ns =
current->default_timer_slack_ns;
else
current->timer_slack_ns = arg2;
break;
case PR_MCE_KILL:
if (arg4 | arg5)
return -EINVAL;
switch (arg2) {
case PR_MCE_KILL_CLEAR:
if (arg3 != 0)
return -EINVAL;
current->flags &= ~PF_MCE_PROCESS;
break;
case PR_MCE_KILL_SET:
current->flags |= PF_MCE_PROCESS;
if (arg3 == PR_MCE_KILL_EARLY)
current->flags |= PF_MCE_EARLY;
else if (arg3 == PR_MCE_KILL_LATE)
current->flags &= ~PF_MCE_EARLY;
else if (arg3 == PR_MCE_KILL_DEFAULT)
current->flags &=
~(PF_MCE_EARLY|PF_MCE_PROCESS);
else
return -EINVAL;
break;
default:
return -EINVAL;
}
break;
case PR_MCE_KILL_GET:
if (arg2 | arg3 | arg4 | arg5)
return -EINVAL;
if (current->flags & PF_MCE_PROCESS)
error = (current->flags & PF_MCE_EARLY) ?
PR_MCE_KILL_EARLY : PR_MCE_KILL_LATE;
else
error = PR_MCE_KILL_DEFAULT;
break;
case PR_SET_MM:
error = prctl_set_mm(arg2, arg3, arg4, arg5);
break;
case PR_GET_TID_ADDRESS:
error = prctl_get_tid_address(me, (int __user * __user *)arg2);
break;
case PR_SET_CHILD_SUBREAPER:
me->signal->is_child_subreaper = !!arg2;
if (!arg2)
break;
walk_process_tree(me, propagate_has_child_subreaper, NULL);
break;
case PR_GET_CHILD_SUBREAPER:
error = put_user(me->signal->is_child_subreaper,
(int __user *)arg2);
break;
case PR_SET_NO_NEW_PRIVS:
if (arg2 != 1 || arg3 || arg4 || arg5)
return -EINVAL;
task_set_no_new_privs(current);
break;
case PR_GET_NO_NEW_PRIVS:
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;
return task_no_new_privs(current) ? 1 : 0;
case PR_GET_THP_DISABLE:
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;
error = !!test_bit(MMF_DISABLE_THP, &me->mm->flags);
break;
case PR_SET_THP_DISABLE:
if (arg3 || arg4 || arg5)
return -EINVAL;
if (mmap_write_lock_killable(me->mm))
return -EINTR;
if (arg2)
set_bit(MMF_DISABLE_THP, &me->mm->flags);
else
clear_bit(MMF_DISABLE_THP, &me->mm->flags);
mmap_write_unlock(me->mm);
break;
case PR_MPX_ENABLE_MANAGEMENT:
case PR_MPX_DISABLE_MANAGEMENT:
/* No longer implemented: */
return -EINVAL;
case PR_SET_FP_MODE:
error = SET_FP_MODE(me, arg2);
break;
case PR_GET_FP_MODE:
error = GET_FP_MODE(me);
break;
case PR_SVE_SET_VL:
error = SVE_SET_VL(arg2);
break;
case PR_SVE_GET_VL:
error = SVE_GET_VL();
break;
case PR_SME_SET_VL:
error = SME_SET_VL(arg2);
break;
case PR_SME_GET_VL:
error = SME_GET_VL();
break;
case PR_GET_SPECULATION_CTRL:
if (arg3 || arg4 || arg5)
return -EINVAL;
error = arch_prctl_spec_ctrl_get(me, arg2);
break;
case PR_SET_SPECULATION_CTRL:
if (arg4 || arg5)
return -EINVAL;
error = arch_prctl_spec_ctrl_set(me, arg2, arg3);
break;
case PR_PAC_RESET_KEYS:
if (arg3 || arg4 || arg5)
return -EINVAL;
error = PAC_RESET_KEYS(me, arg2);
break;
case PR_PAC_SET_ENABLED_KEYS:
if (arg4 || arg5)
return -EINVAL;
error = PAC_SET_ENABLED_KEYS(me, arg2, arg3);
break;
case PR_PAC_GET_ENABLED_KEYS:
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;
error = PAC_GET_ENABLED_KEYS(me);
break;
case PR_SET_TAGGED_ADDR_CTRL:
if (arg3 || arg4 || arg5)
return -EINVAL;
error = SET_TAGGED_ADDR_CTRL(arg2);
break;
case PR_GET_TAGGED_ADDR_CTRL:
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;
error = GET_TAGGED_ADDR_CTRL();
break;
case PR_SET_IO_FLUSHER:
if (!capable(CAP_SYS_RESOURCE))
return -EPERM;
if (arg3 || arg4 || arg5)
return -EINVAL;
if (arg2 == 1)
current->flags |= PR_IO_FLUSHER;
else if (!arg2)
current->flags &= ~PR_IO_FLUSHER;
else
return -EINVAL;
break;
case PR_GET_IO_FLUSHER:
if (!capable(CAP_SYS_RESOURCE))
return -EPERM;
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;
error = (current->flags & PR_IO_FLUSHER) == PR_IO_FLUSHER;
break;
case PR_SET_SYSCALL_USER_DISPATCH:
error = set_syscall_user_dispatch(arg2, arg3, arg4,
(char __user *) arg5);
break;
#ifdef CONFIG_SCHED_CORE
case PR_SCHED_CORE:
error = sched_core_share_pid(arg2, arg3, arg4, arg5);
break;
#endif
case PR_SET_MDWE:
error = prctl_set_mdwe(arg2, arg3, arg4, arg5);
break;
case PR_GET_MDWE:
error = prctl_get_mdwe(arg2, arg3, arg4, arg5);
break;
case PR_SET_VMA:
error = prctl_set_vma(arg2, arg3, arg4, arg5);
break;
case PR_GET_AUXV:
if (arg4 || arg5)
return -EINVAL;
error = prctl_get_auxv((void __user *)arg2, arg3);
break;
#ifdef CONFIG_KSM
case PR_SET_MEMORY_MERGE:
if (arg3 || arg4 || arg5)
return -EINVAL;
if (mmap_write_lock_killable(me->mm))
return -EINTR;
if (arg2)
error = ksm_enable_merge_any(me->mm);
else
error = ksm_disable_merge_any(me->mm);
mmap_write_unlock(me->mm);
break;
case PR_GET_MEMORY_MERGE:
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;
error = !!test_bit(MMF_VM_MERGE_ANY, &me->mm->flags);
break;
#endif
case PR_RISCV_V_SET_CONTROL:
error = RISCV_V_SET_CONTROL(arg2);
break;
case PR_RISCV_V_GET_CONTROL:
error = RISCV_V_GET_CONTROL();
break;
default:
error = -EINVAL;
break;
}
return error;
}
extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec);
static inline void set_task_comm(struct task_struct *tsk, const char *from)
{
__set_task_comm(tsk, from, false);
}
/*
* These functions flushes out all traces of the currently running executable
* so that a new one can be started
*/
void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
{
// yym-gaizao
pr_debug("yym-gaizao set_task_comm: %s (pid=%d) new comm: %s\n", tsk->comm, tsk->pid, buf);
task_lock(tsk);
trace_task_rename(tsk, buf);
strscpy_pad(tsk->comm, buf, sizeof(tsk->comm));
task_unlock(tsk);
perf_event_comm(tsk, exec);
}
更多推荐
所有评论(0)