LV05-07-Linux系统-04-prctl函数

本文主要是prctl函数的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
开发板 正点原子 i.MX6ULL Linux阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
STM32开发板 正点原子战舰V3(STM32F103ZET6)
点击查看本文参考资料
参考方向 参考原文
------
点击查看相关文件下载
--- ---

一、prctl() 函数

在 linux 下可以使用 man 2 prctl 命令查看该函数的帮助手册,也可以访问这里:prctl(2) - Linux manual page (man7.org)

1
2
3
4
5
/* 需包含的头文件 */
#include <sys/prctl.h>

/* 函数声明 */
int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);

【函数说明】该函数是一个系统调用指令,它是为进程制定而设计的,明确的选择取决于option。

【函数参数】

  • option : int 类型,表示操作类型,具体有哪些类型,我们可以看 linux 内核源码中的 include/uapi/linux/prctl.h 文件。
  • arg2、arg3、arg4和arg5 :unsigned long类型,这些就可以看手册了,不同的选项有不同的含义,

【返回值】 int 类型,这个比较长,就看文档吧,哈哈哈。

【使用格式】none

【注意事项】 none

二、函数应用

为什么要说介绍这个函数?对于我自己来说,我是因为看到它可以设置线程的名称,可以很方便的查看我们的线程是否还在运行。

1. 线程使用实例

1.1 看不到线程信息?

点击查看实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>  /* fopen fputc*/
#include <pthread.h>/* pthread_create pthread_exit */
#include <unistd.h> /* sleep */

void *thread1(void *arg);
void *thread2(void *arg);

int main(int argc, char *argv[])
{
int ret;
pthread_t tid1, tid2;

ret = pthread_create(&tid1, NULL, thread1, NULL);
printf("This is threadMutex1 thread create,ret=%d,tid1=%lu\n", ret, tid1);
ret = pthread_create(&tid2, NULL, thread2, NULL);
printf("This is threadMutex2 thread create,ret=%d,tid2=%lu\n", ret, tid2);
while(1)
{
sleep(1);
}
return 0;
}

void *thread1(void *arg)
{
pthread_detach(pthread_self());
while(1)
{
sleep(1);/* us级休眠 */
}
pthread_exit("thread1 exit!");
}

void *thread2(void *arg)
{
pthread_detach(pthread_self());
while(1)
{
sleep(1);/* us级休眠 */
}
pthread_exit("thread2 exit!");
}

在上边的实例代码中,我们创建两个线程,然后我们在线程中什么也不做,我们编译程序:

1
gcc main.c -Wall -lpthread

然后我们在后台运行这个进程(&就表示在后台运行这个进程):

1
./a.out &

然后我们会看看到屏幕上有两行输出:

1
2
This is threadMutex1 thread create,ret=0,tid1=140490243557120
This is threadMutex2 thread create,ret=0,tid2=140490235164416

随后就什么现象都没有了,我们的进程在运行吗?创建的两条线程在运行吗?

我们使用ps命令来查看一下,在很多实际linux系统的根文件系统,其实并不会像ubuntu桌面版这样支持具有如此多参数的ps命令,一般我们在开发板上使用的根文件系统都是经过裁剪,所以这里我们使用最基础的命令来查看:

1
2
3
4
5
hk@vm:~/6temp/test$ ps
PID TTY TIME CMD
54694 pts/22 00:00:00 bash
58091 pts/22 00:00:00 a.out
58109 pts/22 00:00:00 ps

可以看到我们的进程 a.out 依然在运行,那线程呢,我们可以使用 -T 参数来查看进程以及线程?

1
2
3
4
5
6
7
hk@vm:~/6temp/test$ ps -T
PID SPID TTY TIME CMD
54694 54694 pts/22 00:00:00 bash
58091 58091 pts/22 00:00:00 a.out
58091 58092 pts/22 00:00:00 a.out
58091 58093 pts/22 00:00:00 a.out
58118 58118 pts/22 00:00:00 ps

我们发现,有3个a.out,哪个是thread1,哪个是thread2?一眼看过去我们只能看到他们的PID和SPID,我们其实可以通过SPID来区分不同的线程,但是把,我自己是没有查过这个SPID是与创建线程的时候的tid究竟有没有联系,所以这个时候上边的prctl函数就起到了重要作用。

1.2 线程命名

在linux内核源码的 include/uapi/linux/prctl.h 文件中,定义的选项有这么一个:

1
#define PR_SET_NAME    15		/* Set process name */

我们查阅文档可以知道这个参数可以用于设定线程的名称:

PR_SET_NAME (since Linux 2.6.9)
Set the name of the calling thread, using the value in the location pointed to by (char *) arg2. The name can be up to 16 bytes long, including the terminating null byte. (If the length of the string, including the terminating null byte, exceeds 16 bytes, the string is silently truncated.) This is the same attribute that can be set via pthread_setname_np(3) and retrieved using pthread_getname_np(3). The attribute is likewise accessible via /proc/self/task/[tid]/comm, where tid is the name of the calling thread.

翻译过来大概的意思就是,使用(char *) arg2指向的位置中的值可以设置调用线程的名称。该名称最多可以有16个字节长,包括终止空字节。(如果字符串的长度,包括结束空字节,超过16字节,字符串将被截断。)这个属性可以通过pthread_setname_np(3)设置,也可以使用pthread_getname_np(3)检索。该属性同样可以通过/proc/self/task/[tid]/comm访问,其中tid是调用线程的名称。

于是我们修改例子如下:

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <stdio.h>  /* fopen fputc */
#include <pthread.h>/* pthread_create pthread_exit */
#include <unistd.h> /* sleep */
#include <sys/prctl.h>

void *thread1(void *arg);
void *thread2(void *arg);

int main(int argc, char *argv[])
{
int ret;
pthread_t tid1, tid2;
ret = pthread_create(&tid1, NULL, thread1, NULL);
printf("This is threadMutex1 thread create,ret=%d,tid1=%lu\n", ret, tid1);
ret = pthread_create(&tid2, NULL, thread2, NULL);
printf("This is threadMutex2 thread create,ret=%d,tid2=%lu\n", ret, tid2);
while(1)
{
sleep(1);
}
return 0;
}

void *thread1(void *arg)
{
pthread_detach(pthread_self());
char * thread1_name = "thread1";
prctl(PR_SET_NAME, (unsigned long)thread1_name);
while(1)
{
sleep(1);/* us级休眠 */
}
pthread_exit("thread1 exit!");
}

void *thread2(void *arg)
{
pthread_detach(pthread_self());
char * thread2_name = "thread2";
prctl(PR_SET_NAME, (unsigned long)thread2_name);
while(1)
{
sleep(1);/* us级休眠 */
}
pthread_exit("thread2 exit!");
}

像上边一样,我们同样编译吗,并在后台运行:

1
2
gcc main.c -Wall -lpthread
./a.out &

然后我们看一下现在的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
hk@vm:~/6temp/test$ ps
PID TTY TIME CMD
57544 pts/23 00:00:00 bash
58948 pts/23 00:00:00 main_process
58970 pts/23 00:00:00 ps
hk@vm:~$ ps -T
PID SPID TTY TIME CMD
2401 2401 pts/19 00:00:00 bash
8950 8950 pts/19 00:00:00 gedit
8950 8951 pts/19 00:00:00 gmain
8950 8952 pts/19 00:00:00 gdbus
8950 8953 pts/19 00:00:00 dconf worker
9001 9001 pts/19 00:00:00 a.out
9001 9002 pts/19 00:00:00 thread1
9001 9003 pts/19 00:00:00 thread2
9004 9004 pts/19 00:00:00 ps

我们发现,我们的进程为a.out,下边有两个名为thread1和thread2的东西,它们前边的PID与进程a.out是一致的,这就是我们创建的两个线程。

2. 设置进程名字?

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>  /* fopen fputc */
#include <unistd.h> /* sleep */
#include <sys/prctl.h>

int main(int argc, char *argv[])
{
int ret;

char * main_name = "main_process";
prctl(PR_SET_NAME, (unsigned long)main_name);

while(1)
{
sleep(1);
}
return 0;
}

像上边一样,我们同样编译吗,并在后台运行:

1
2
gcc main.c -Wall -lpthread
./a.out &

然后我们看一下现在的情况:

1
2
3
4
5
hk@vm:~$ ps -T
PID SPID TTY TIME CMD
9077 9077 pts/1 00:00:00 bash
9106 9106 pts/1 00:00:00 main_process
9107 9107 pts/1 00:00:00 ps

可以看到我们的进程名字不再是a.out了,变成了我们设置的值。