线程的创建---线程标识ID

[1]int pthread_create(pthread_t *restrict thread,
        const pthread_attr_t *restrict attr,
        void *(*start_routine)(void*),
        void *restrict arg);
  thread: 线程创建成功的话,会将分配的线程ID填入该指针指向的地址。线程的后续操作将使用该值作为线程的唯一标识。
  attr:   定制线程的属性, 如果创建线程无特殊的要求,该值也可以是 NULL ,表示采用默认属性。
  start_routine: 子线程执行函数
  arg:    创建线程传给新建线程得我参数, 可以是任意结构的指针
  如果成功,返回0 ;如果不成功,错误码(可能>0)。
  
[2]多线程进程的地址空间
       


    1.调用 pthread_create函数时,glibc首先要为线程分配线程栈,而线程栈的位置就落在mmap区域。

    2.glibc会调用mmap函数为线程分配栈空间。

    3.pthread_create函数分配的线程标识ID, 是分配出来的空间里的一个结构体的指针。
        ==》线程 ID 的本质是内存地址
        pthread_t tid---→ struct pthread {
                             线程局部存储
                             线程栈
                            };
                            
[3]线程标识ID(pid_t)与线程ID(pthread_t)
    1.pthread_t pthread_self(void); //获取到线程自身的标识ID

    2.线程的标识ID与线程ID(pid_t)不同
        1.线程ID,属于进程调度的范畴。用来唯一标识该线程。
        2.线程的标识ID属于NPTL线程库范畴。 线程库的后续操作,将根据线程标识ID来操作线程。

    3.int pthread_equal(pthread_t t1, pthread_t t2); // 判断两个线程的标识符ID是否对应着同一个线程
        返回值是 0 的时候,表示两个线程是同一个线程,非零值则表示不是同一个线程。

    4.不同线程组内的两个线程,哪怕两者的 pthread_t值是一样的,也不是同一个线程,这是显而易见的。

    5.在满足下列条件时,线程ID就有可能会被复用:
        1.线程退出。
        2.程组的其他线程对该线程执行了 pthread_join,或者线程退出前将分离状态设置为已分离
        3.再次调用 pthread_create创建线程。

    6.如果要设计调试日志,用 pthread_t 类型的线程 ID 来标识进程就不太合适了。用pid_t类型的线程ID则是一个比较不错的选择。
        int TID = syscall(SYS_gettid);
        
[4]pid_t类型的线程ID来唯一标识进程有以下优势:

    1.进程之间不会存在重复的线程 ID ,而且不同线程之间也不会重复,在任意时刻都是全局唯一的值。

    2.可以方便地查看 /proc/pid/task/tid 来获取线程对应的信息。

    3.ps 命令提供了查看线程信息的 -L 选项,可以通过输出中的 LWP 和 NLWP ,来查看同一个线程组的线程个数及线程 ID 的信息。

    4.可以给线程起一个有意义的名字,命名以后,既可以从procfs中获取到线程的名字,也可以从ps命令中得到线程的名字,这样就可以更好地辨识不同的线程。
        int prctl(int option, ul arg2,ul arg3 , ul arg4,ul arg5)   //ul: unsigned long
            1.将 option 设为 PR_SET_NAME ,        
            2.将线程的名字作为arg2传递给prctl系统调用
            3.其余参数为NULL
            4.用ps命令来查看线程的名字: ps -L -p pid
            5.在系统中查看线程名字
                cat /proc/pid/task/tid/status
        //这是一个很有用的技巧。给线程命了名,就可以很直观地区分各个线程,尤其是在线程比较多,且其分工不同的情况下。

[5]线程创建的默认属性

    1.默认情况下,线程栈的大小为 8MB (ulimit -s)

    2.调用 pthread_attr_getstack 函数可以返回线程栈的基地址和栈的大小。  

    3.出于可移植性的考虑不建议指定线程栈的基地址。

    4.可以调用接口来调整线程栈的大小:
        int pthread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize);
        int pthread_attr_getstacksize(pthread_attr_t *attr,size_t *stacksize);
[6]示例代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>
#include <sys/syscall.h> 
#include <sys/prctl.h>

typedef struct args {
	int a;
} args_t;

pid_t   pid;	// 进程pid
pid_t	tid;	// 线程pid
pthread_t main_id;	// 主线程标识ID
pthread_t sub_id;	// 子线程标识ID
pthread_attr_t attr; // 线程属性
void *stack;		 // 线程栈地址
size_t size;	 // 线程栈大小

void *start_routim(void *arg)
{
	sleep(1);
	printf("\n------------2-----------------\n");
	pid =  getpid();	
	tid = (pid_t)syscall(SYS_gettid);	
	sub_id = pthread_self();			// 子线程PID
	printf("pid = %d\n", pid);
	printf("tid = %d\n", tid);
	printf("sub_id = %lu\n", sub_id);
	printf("arg->a = %d\n", ((args_t *)arg)->a);
	// 给子线程设置名字
	prctl(PR_SET_NAME, "sub_pthread", NULL, NULL, NULL);

	// 获取线程栈的基地址和栈大小
	if(pthread_attr_getstacksize(&attr, &size))
		perror("pthread_attr_setstacksize err\n");
	printf("pthread stack size = %lu M\n", size/1024/1024);

	// 设置线程栈的基地址和栈大小
	size += 10 * 1024 *1024;		// 10M
	if(pthread_attr_setstacksize(&attr, size))		// 设置
		perror("pthread_attr_getstack err\n");
	if(pthread_attr_getstacksize(&attr, &size))		// 获取
		perror("pthread_attr_setstacksize err\n");
	printf("pthread stack size = %lu M\n", size/1024/1024);		// 18M
	while(1)
		sleep(1);	// 不让子线程退出
}
int main(int argc, char **argv)
{
	args_t arg;		// 传递给子线程的参数
	char buf[256];
	
	arg.a = 123;
	pid =  getpid();	// 进程ID
	//tid =  gettid();	// glibc库没有提供这个接口
	tid = (pid_t)syscall(SYS_gettid);	// 主线程tid
	
	if(pthread_create(&sub_id, NULL, start_routim, (void *)&arg))
		perror("pthread_create err");

	main_id = pthread_self();	// 主线程标识ID
	
	printf("\n-----------1------------------\n");
	printf("pid = %d\n", pid);
	printf("tid = %d\n", tid);
	printf("main_id = %lu\n", main_id);
	printf("sub_id = %lu\n", sub_id);
		
	sleep(2);	// 等待子线程创建完成
	// 给主线程设置名字
	prctl(PR_SET_NAME, "main_pthread", NULL, NULL, NULL);
	
	bzero(buf, 256);
	sprintf(buf, "ps -L -p %d", pid);
	printf("buf: %s\n", buf);
	system(buf);

	// 获取线程栈的基地址和栈大小
	printf("\n-----------3------------------\n");
	if(pthread_attr_getstacksize(&attr, &size))
		perror("pthread_attr_setstacksize err\n");
	printf("pthread stack size = %lu M\n", size/1024/1024);

	// 设置线程栈的基地址和栈大小
	size += 5 * 1024 *1024;		// 5M
	if(pthread_attr_setstacksize(&attr, size))		// 设置
		perror("pthread_attr_getstack err\n");
	if(pthread_attr_getstacksize(&attr, &size))		// 获取
		perror("pthread_attr_setstacksize err\n");
	printf("pthread stack size = %lu M\n", size/1024/1024);		// 13M

	
	while(1);
}

[7]执行结果

book@gui_hua_shu:/work/nfs_root/qt_fs_new/2system_pro$ ./a.out

-----------1------------------
pid = 15200
tid = 15200
main_id = 140307526604544
sub_id = 140307518293760

------------2-----------------
pid = 15200
tid = 15201
sub_id = 140307518293760
arg->a = 123
pthread stack size = 8 M
pthread stack size = 18 M
buf: ps -L -p 15200
  PID   LWP TTY          TIME CMD
15200 15200 pts/1    00:00:00 main_pthread
15200 15201 pts/1    00:00:00 sub_pthread

-----------3------------------
pthread stack size = 18 M
pthread stack size = 23 M
^C
book@gui_hua_shu:/work/nfs_root/qt_fs_new/2system_pro$

猜你喜欢

转载自blog.csdn.net/qq_38813056/article/details/85268866