[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$