在日常 Java 开发中,调用 Thread.setName(String) 给线程起个有意义的名字,是再平常不过的操作。这些名字在堆栈 dump、日志、调试器中清晰可见,极大提升了问题定位的效率。但你有没有想过:这个 Java 层的方法调用,究竟是如何一路穿透 JVM、glibc、操作系统内核,最终让内核里的 task_struct 也记住这个新名字的?本文将结合 OpenJDK、glibc 和 Linux 内核的源码,完整地走一遍这条"线程改名"之路。
一、起点:Java 层的 setName
一切从一个简单的 Java 调用开始:
java
ini
Thread t = new Thread(() -> { ... });
t.setName("MyWorker");
Thread 类中的 setName 方法实现如下(来自 OpenJDK):
java
arduino
public final synchronized void setName(String name) {
checkAccess(); // 1. 安全权限检查
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name; // 2. 更新 Java 层 name 字段
if (threadStatus != 0) { // 3. 如果线程已经启动
setNativeName(name); // 4. 通知底层修改原生线程名
}
}
关键点已经用注释标出:
checkAccess():确认当前线程有权限修改这个线程(通常就是自己)。threadStatus != 0:这个状态字段在start()后被设为非零,表示 JVM 已创建了对应的原生线程。只有已启动的线程才需要同步修改底层的线程名;未启动的线程只需保存 Java 层的name,等启动时会用这个名初始化底层。setNativeName(name):一个private native方法,真正的底层修改入口。
java
arduino
private native void setNativeName(String name);
到此,我们已经来到了 Java 与 JVM 的边界。下一步,我们要看 JVM 如何实现这个 native 方法。
二、跨过 JNI:JVM 的 native 方法注册
HotSpot JVM 在启动时会通过 JNI 注册一系列的 native 方法。对于 java.lang.Thread 类,相关方法的映射表定义在 Thread.c 中:
c
arduino
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
// ... 省略其他方法 ...
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};
这里将 Java 的 setNativeName 绑定到了 C++ 函数 JVM_SetNativeThreadName(注意 JVM 内部函数命名习惯 JVM_ 前缀)。接下来我们跳进这个函数的实现。
三、JVM 内部:JVM_SetNativeThreadName
该函数位于 OpenJDK 的 jvm.cpp 中(路径可能因版本略有差异),使用 JVM_ENTRY 宏定义:
c
scss
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); // 1. 从 JNI 引用解析出 oop
JavaThread* thr = java_lang_Thread::thread(java_thread); // 2. 获取对应的 JavaThread 对象
// 关键检查:只能修改当前线程的名字,且不能是通过 JNI 附加的线程
if (thread == thr && !thr->has_attached_via_jni()) {
ResourceMark rm(thread);
// 3. 将 Java 字符串转换为 C 字符串
const char *thread_name = java_lang_String::as_utf8_string(
JNIHandles::resolve_non_null(name));
// 4. 调用操作系统相关的设置函数
os::set_native_thread_name(thread_name);
}
JVM_END
这个函数有三个重要的动作:
- 解析 Java 对象 :
JNIHandles::resolve_non_null将 JNI 引用转为 HotSpot 内部的oop,再通过java_lang_Thread::thread拿到与之关联的JavaThread*(JVM 中代表一个 Java 线程的 C++ 对象)。 - 严格限制 :
thread == thr确保你只能修改当前正在执行 的线程的名字。换句话说,你不能通过Thread.setName去改另一个线程的底层名字。为什么?因为 Linux 的pthread_setname_np默认也只允许线程改自己的名字(特权进程可以通过/proc改别的线程,但 JVM 选择保持简单)。另外!thr->has_attached_via_jni()阻止修改那些通过 JNI 附加到 JVM 的现有原生线程,以免干扰外部程序。 - 调用平台相关层 :
os::set_native_thread_name是 HotSpot 对操作系统线程命名功能的抽象。
四、HotSpot 的 POSIX 封装:os::set_native_thread_name
os::set_native_thread_name 的实现位于 os_linux.cpp(以 Linux 为例):
c
c
void os::set_native_thread_name(const char *name) {
if (Linux::_pthread_setname_np) { // 1. 检查函数指针是否可用
char buf [16]; // glibc 规定线程名长度最大 15 字符,加上 '\0' 共 16 字节
snprintf(buf, sizeof(buf), "%s", name);
buf[sizeof(buf) - 1] = '\0';
const int rc = Linux::_pthread_setname_np(pthread_self(), buf); // 2. 调用 glibc 函数
// 3. 出错时断言(ERANGE 表示名字太长)
assert(rc != ERANGE, "pthread_setname_np failed");
}
}
注意到这里使用了一个函数指针 Linux::_pthread_setname_np,它在 JVM 初始化时通过 dlsym 动态获取:
c
c
// 在 os::init() 中
Linux::_pthread_setname_np =
(int(*)(pthread_t, const char*))dlsym(RTLD_DEFAULT, "pthread_setname_np");
这是为了避免静态链接时因 glibc 版本过低缺少该函数而导致 JVM 启动失败。动态获取后,如果函数存在则调用,否则静默忽略。
五、glibc 的两种实现路径
pthread_setname_np 是 glibc 提供的非标准(_np = non‑portable)扩展函数。它的实现代码位于 glibc 的 nptl/pthread_setname.c 中:
c
ini
int
__pthread_setname_np (pthread_t th, const char *name)
{
const struct pthread *pd = (const struct pthread *) th;
// 内核限制线程名长度(含 '\0' 最大 16 字节)
#define TASK_COMM_LEN 16
size_t name_len = strlen (name);
if (name_len >= TASK_COMM_LEN)
return ERANGE;
// 情况 1:修改的是当前线程
if (pd == THREAD_SELF)
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;
}
versioned_symbol (libc, __pthread_setname_np, pthread_setname_np, GLIBC_2_34);
glibc 巧妙地处理了两种情况:
- 改自身 :直接调用
prctl系统调用,传入PR_SET_NAME选项。这是最轻量的路径。 - 改其他线程 :通过
/proc/self/task/<tid>/comm这个特殊的 proc 文件写入新的名字。该文件可被拥有适当权限(通常是 root 或同属主)的进程写入,glibc 利用它实现了跨线程改名。
我们注意到,JVM 在调用 pthread_setname_np 时传入了 pthread_self(),所以走的正是第一种路径(改自身)。接下来,我们分别深入这两条路径的内核实现。
六、深入内核:prctl 系统调用
__prctl 是 glibc 对 prctl 系统调用的封装(同样支持可变参数):
c
arduino
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);
// 这里最终会通过 syscall 指令进入内核
return INLINE_SYSCALL_CALL (prctl, option, arg2, arg3, arg4, arg5);
}
在内核侧,prctl 系统调用的入口在 kernel/sys.c 中,它是一个庞大的 switch 语句处理各种 option。我们重点关注 PR_SET_NAME:
c
c
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)];
// ... 其他 case ...
switch (option) {
// ...
case PR_SET_NAME:
comm[sizeof(me->comm) - 1] = 0;
// 从用户空间复制新名字(最大 15 字节 + '\0')
if (strncpy_from_user(comm, (char __user *)arg2,
sizeof(me->comm) - 1) < 0)
return -EFAULT;
// 关键函数:真正修改 task_struct 的 comm 字段
set_task_comm(me, comm);
// 通知进程连接器(例如 systemd 等可以捕获改名事件)
proc_comm_connector(me);
break;
// ...
}
}
set_task_comm 是一个内联函数,最终调用 __set_task_comm,其实现位于 fs/exec.c:
c
rust
void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
{
// 用户提供的调试打印(可能被内核开发者添加)
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)); // 安全拷贝,填充 '\0'
task_unlock(tsk);
perf_event_comm(tsk, exec); // 性能事件通知
}
到了这一步,内核中代表当前线程的 task_struct 的 comm 字段(即 /proc/self/comm 和 /proc/self/status 中 Name: 行的来源)已经被更新。从此,ps、top、/proc/pid/stat 等看到的就是新名字。
七、备选路径:通过 /proc/pid/comm 改名
如果 pthread_setname_np 尝试修改其他线程,glibc 会走第二种方案------写 /proc/self/task/<tid>/comm。这个文件在内核中对应的操作集如下:
c
ini
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,
};
comm_write 函数负责实际修改:
c
arduino
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); // 同样是调用 set_task_comm
proc_comm_connector(p);
} else {
count = -EINVAL;
}
put_task_struct(p);
return count;
}
注意内核的权限检查:same_thread_group(current, p) 确保写入者必须与目标属于同一个进程(线程组)。这比 prctl 只允许改自身略微宽松,但仍然限制了不能跨进程改名(除非有 CAP_SYS_ADMIN 等特权,但 glibc 未利用)。JVM 由于只改自身,走 prctl 路径,效率更高。
八、完整调用链路回顾
将上面所有的环节串联起来,一条从 Java 到内核的完整路径清晰可见:
text
css
Java 代码: thread.setName("MyWorker")
↓
java.lang.Thread.setName()
↓ (native)
JVM_SetNativeThreadName() [jvm.cpp]
↓
os::set_native_thread_name() [os_linux.cpp]
↓
Linux::_pthread_setname_np() → 动态绑定 glibc 的 pthread_setname_np
↓
__pthread_setname_np() [glibc nptl/pthread_setname.c]
↓ (当前线程分支)
__prctl(PR_SET_NAME, name) → 封装系统调用
↓
sys_prctl(PR_SET_NAME, ...) [kernel/sys.c]
↓
set_task_comm() → __set_task_comm() [fs/exec.c]
↓
更新 current->comm 字段
九、关键限制与注意事项
- 只能改自身 :JVM 为了简单和安全,只允许线程修改它自己的底层名字。虽然 Linux 允许同一进程的其他线程通过
/proc/.../comm帮你改名,但 OpenJDK 没有这样做。这意味着如果你调用threadA.setName()想改threadB的名字,Java 层会成功(Java 字段变了),但底层线程名不会变------除非threadB是自己改的。实验验证:启动两个线程,让线程1调用线程2的setName,之后查看/proc/<tid>/comm,会发现名字没变。 - 长度限制 :Linux 内核限制线程名最多 15 个字符(
TASK_COMM_LEN = 16包含终止符)。Java 允许更长的String,但最终传给setNativeName会被截断到 15 字节。这可能导致一些调试工具中看到的名字不完整。 - 性能:整个调用链涉及多次边界跨越(Java→JNI→C++→glibc→syscall),但改名操作本身频率极低,影响微不足道。
- 安全 :
checkAccess()和 JVM 中的thread == thr双重保证,避免恶意代码乱改线程名造成混淆。
十、总结
一个小小的 setName 背后,是 JVM、glibc、内核三者的精妙协作。从 Java 层的同步方法、JNI 动态绑定、HotSpot 的平台抽象、glibc 的两种策略,直到内核的 prctl 系统调用和 task_struct 字段更新,每一步都体现了软件分层设计的智慧。同时,我们也看到了诸多工程上的权衡:为什么要限制只能改自身?为什么不在 Java 层直接绕开这些检查?答案都是为了稳定性 和可移植性。毕竟,线程名字的最终用途主要是辅助调试,而不是作为一个强同步机制。
当你下次通过 jstack 或者 top -H -p <pid> 看到那些整齐的线程名时,希望你能想起这篇文章,想起从 Java 字符串到内核 comm 字段的那条漫长而有趣的旅程。
#源码
public
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);
}