文章目录
- 线程的概念
- Linux内核线程实现原理
- Linux内核线程共享资源
- Linux内核线程非共享资源
- 线程控制原语
- 线程使用注意事项
线程的概念
LWP: light weight process,轻量级进程,本质上仍然是进程(Linux环境下)
进程:独立地址空间,拥有PCB
线程:也有PCB,但没有独立的地址空间
在Linux下,进程是最小的资源分配单元,线程是最小的执行单元
Linux内核线程实现原理
- 创建线程使用的底层函数和进程一样,都是clone();
- 从内核里看进程和线程一样,都有各自的PCB,但是PCB中指向内存资源的三级页表(页目录->页表->物理页面)是相同的;
- 进程可蜕变成线程;
- 线程可看做寄存器和栈的集合
- CPU按照线程划分时间片,划分的依据是LWP号,查看LWP号的命令:
ps -Lf PID可查看指定PID下的线程号
Linux内核线程共享资源
- 文件描述符表
- 每种信号的处理方式
- 当前的工作目录
- 用户ID和组ID
- 内存地址空间(.text/.data/.bss/heap/共享库)
Linux内核线程非共享资源
- 线程ID
- 处理器现场和栈指针(内核栈)
- 独立的栈空间
- errno变量
- 信号屏蔽字
- 调度优先级
线程控制原语
pthread_self
再次强调,线程ID和LWP号是两个概念。
#include <pthread.h>
pthread_t pthread_self(void);
Compile and link with -pthread.
DESCRIPTION
The pthread_self() function returns the ID of the calling thread. This is the same value
that is returned in *thread in the pthread_create(3) call that created this thread.
RETURN VALUE
This function always succeeds, returning the calling thread's ID.
pthread_create
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
Compile and link with -pthread.
DESCRIPTION
The pthread_create() function starts a new thread in the calling process. The new thread starts execution by
invoking start_routine(); arg is passed as the sole argument of start_routine().
The new thread terminates in one of the following ways:
* It calls pthread_exit(3), specifying an exit status value that is available to another thread in the same
process that calls pthread_join(3).
* It returns from start_routine(). This is equivalent to calling pthread_exit(3) with the value supplied in
the return statement.
* It is canceled (see pthread_cancel(3)).
* Any of the threads in the process calls exit(3), or the main thread performs a return from main(). This
causes the termination of all threads in the process.
The attr argument points to a pthread_attr_t structure whose contents are used at thread creation time to determine
attributes for the new thread; this structure is initialized using pthread_attr_init(3) and related functions. If
attr is NULL, then the thread is created with default attributes.
Before returning, a successful call to pthread_create() stores the ID of the new thread in the buffer pointed to by
thread; this identifier is used to refer to the thread in subsequent calls to other pthreads functions.
******************尽管继承了信号屏蔽字,两线程间信号屏蔽字彼此独立
The new thread inherits a copy of the creating thread's signal mask(pthread_sigmask(3)). The set of pending signals
for the new thread is empty (sigpending(2)). The new thread does not inherit the creating thread's alternate signal
stack (sigaltstack(2)).
******************
The new thread inherits the calling thread's floating-point environment(fenv(3)).
The initial value of the new thread's CPU-time clock is 0 (see pthread_getcpuclockid(3)).
循环创建N个线程
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<errno.h>
void *threadFunc(void* arg)
{
int i = (int) arg;
sleep(i);
printf("This is slave thread: %d, thread ID: %lu, PID: %u\n",i, pthread_self(),getpid());
}
int main(void)
{
int i,ret;
pthread_t tid;
for(i=0;i<5;i++)
{
ret = pthread_create(&tid,NULL,threadFunc,(void*)i);
if(ret != 0)
{
perror("create failed:");
exit(1);
}
}
printf("This is main thread , thread ID: %lu on PID %u exit\n",pthread_self(),getpid());
sleep(i);
return 0;
}
// 输出结果:
This is slave thread: 0, thread ID: 140436385527552, PID: 5670
This is slave thread: 1, thread ID: 140436377134848, PID: 5670
This is slave thread: 2, thread ID: 140436368742144, PID: 5670
This is slave thread: 3, thread ID: 140436360349440, PID: 5670
This is slave thread: 4, thread ID: 140436351956736, PID: 5670
This is main thread , thread ID: 140436393862912 on PID 5670 exit
pthread_exit
#include <pthread.h>
void pthread_exit(void *retval);
DESCRIPTION
The pthread_exit() function terminates the calling thread and returns a value via retval
that (if the thread is joinable) is available to another thread in the same process that
calls pthread_join(3).
Any clean-up handlers established by pthread_cleanup_push(3) that have not yet been popped,
are popped (in the reverse of the order in which they were pushed) and executed. If the
thread has any thread-specific data, then, after the clean-up handlers have been executed,
the corresponding destructor functions are called, in an unspecified order.
//*********************pay attetion*******************
When a thread terminates, process-shared resources (e.g., mutexes, condition variables,
semaphores, and file descriptors) are not released, and functions registered using atexit(3)
are not called.
After the last thread in a process terminates, the process terminates as by calling exit(3)
with an exit status of zero; thus, process-shared resources are released and functions
registered using atexit(3) are called.
RETURN VALUE
This function does not return to the caller.
因此,可以使用pthread_exit()来改进上一小节中的循环创建N个线程
循环创建N个线程
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<errno.h>
void *threadFunc(void* arg)
{
int i = (int) arg;
sleep(i);
printf("This is slave thread: %d, thread ID: %lu, PID: %u\n",i, pthread_self(),getpid());
}
int main(void)
{
int i,ret;
pthread_t tid;
for(i=0;i<5;i++)
{
ret = pthread_create(&tid,NULL,threadFunc,(void*)i);
if(ret != 0)
{
perror("create failed:");
exit(1);
}
}
printf("This is main thread , thread ID: %lu on PID %u exit\n",pthread_self(),getpid());
pthread_exit(NULL);
}
pthread_join
阻塞等待线程退出,获取线程退出状态
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
DESCRIPTION
The pthread_join() function waits for the thread specified by thread to terminate. If that
thread has already terminated, then pthread_join() returns immediately. The thread specified
by thread must be joinable.
If retval is not NULL, then pthread_join() copies the exit status of the target thread (i.e.,
the value that the target thread supplied to pthread_exit(3)) into the location pointed to
by *retval. If the target thread was canceled, then PTHREAD_CANCELED is placed in *retval.
If multiple threads simultaneously try to join with the same thread, the results are undefined.
If the thread calling pthread_join() is canceled, then the target thread will remain joinable
(i.e., it will not be detached).
RETURN VALUE
On success, pthread_join() returns 0; on error, it returns an error number.
示例:利用pthread_join()接收pthread_exit()的返回值
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
struct exit_t
{
char Arr[20];
char Stat[50];
};
void *STfunc (void* arg)
{
struct exit_t * returnValue = (struct exit_t *)malloc(sizeof(struct exit_t));
strcpy(returnValue->Arr,"maybe");
strcpy(returnValue->Stat,"love and own");
pthread_exit((void*)returnValue);
}
int main(void)
{
pthread_t tid;
struct exit_t *retval;
pthread_create(&tid,NULL,STfunc,NULL);
pthread_join(tid,(void**)&retval);
printf("%s,%s\n",retval->Arr,retval->Stat);
free(retval);
return 0;
}
pthread_detach
pthread_detach()实现线程分离。
线程分离状态:指定该状态后,线程主动与主控线程断开关系,线程结束后,自动释放资源,此外其他线程不能使用pthread_join()获取其退出状态,网络及多线程服务器中常用。
进程若有该机制,将不会产生僵尸进程,僵尸进程产生的原因是进程死亡后大部分资源被释放,一点残留的资源仍存在于系统中,导致内核认为该进程仍存在。
#include <pthread.h>
int pthread_detach(pthread_t thread);
DESCRIPTION
The pthread_detach() function marks the thread identified by thread as detached. When a
detached thread terminates, its resources are automatically released back to the system
without the need for another thread to join with the terminated thread.
Attempting to detach an already detached thread results in unspecified behavior.
RETURN VALUE
On success, pthread_detach() returns 0; on error, it returns an error number.
pthread_cancle
#include <pthread.h>
int pthread_cancel(pthread_t thread);
DESCRIPTION
The pthread_cancel() function sends a cancellation request to the thread thread. Whether
and when the target thread reacts to the cancellation request depends on two attributes
that are under the control of that thread: its cancelability state and type.
A thread's cancelability state, determined by pthread_setcancel‐state(3), can be enabled
(the default for new threads) or disabled. If a thread has disabled cancellation, then a
cancellation request remains queued until the thread enables cancellation. If a thread has
enabled cancellation, then its cancelability type determines when cancellation occurs.
A thread's cancellation type, determined by pthread_setcanceltype(3),may be either asynch-
ronous or deferred (the default for new threads). Asynchronous cancelability means that
the thread can be canceled at any time (usually immediately, but the system does not
guarantee this). Deferred cancelability means that cancellation will be delayed until the
thread next calls a function that is a cancellation point. A list of functions that are or
may be cancellation points is provided in pthreads(7).
/*
由此可以看出,线程的取消并不是实时的,其具有一定的延时性,需要等待线程达到某个取消点。
取消点:是线程检查是否被取消,并按请求进行动作的一个位置,通常是一些系统调用,可用man 7 pthreads
查看具备这些取消点的系统调用列表。如果当前线程中没有取消点,可调用pthread_testcancel()
自行设置一个取消点。
被取消的线程,退出值定义在Linux的pthread库中,常数PTHREAD_CANCELED的值为-1。可在头文件pthread.h
中找到它的定义,#define PTHREAD_CANCELED((void *) -1)。因此,对一个已经被取消了的线程使用
pthread_join()进行回收时,得到的返回值是-1。
*/
RETURN VALUE
On success, pthread_cancel() returns 0; on error, it returns a nonzero error number.
NOTES
On Linux, cancellation is implemented using signals. Under the NPTL threading implementation,
the first real-time signal (i.e., signal 32) is used for this purpose. On LinuxThreads, the
second real-time signal is used, if real-time signals are available, otherwise SIGUSR2 is
used.
pthread_equal
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
DESCRIPTION
The pthread_equal() function compares two thread identifiers.
RETURN VALUE
If the two thread IDs are equal, pthread_equal() returns a nonzero value; otherwise, it
returns 0.
pthread_attr_init/pthread_attr_destroy
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
DESCRIPTION
The pthread_attr_init() function initializes the thread attributes object pointed to by
attr with default attribute values. After this call, individual attributes of the object
can be set using various related functions (listed under SEE ALSO), and then the object
can be used in one or more pthread_create(3) calls that create threads.
Calling pthread_attr_init() on a thread attributes object that has already been initialized
results in undefined behavior.
When a thread attributes object is no longer required, it should be destroyed using the
pthread_attr_destroy() function. Destroying a thread attributes object has no effect on
threads that were created using that object.
Once a thread attributes object has been destroyed, it can be reinitialized using the
pthread_attr_init(). Any other use of a destroyed thread attributes object has undefined
results.
RETURN VALUE
On success, these functions return 0; on error, they return a nonzero error number.
ADDITION
typedef struct
{
int etachstate;
/*线程分离状态,pthread_attr_getdetachstate()/pthread_attr_setdetachstate() */
int schedpolicy;
/*线程调度策略,pthread_attr_getschedpolicy()/pthread_attr_setschedpolicy()*/
strcut sched_param schedparam;
/*线程调度参数,pthread_attr_getschedparam()/pthread_attr_setschedparam()*/
int inheritsced;
/*线程的继承性,pthread_attr_getinheritsched()/pthread_attr_setinheritsched()*/
int scope;
/*线程的作用域,pthread_attr_getscope()/pthread_attr_setscope()*/
size_t guardsize;
/*线程栈末尾的警戒缓冲区大小,pthread_attr_getguardsize()/pthread_attr_setguardsize()*/
int stackaddr_set;
/*线程的栈设置,pthread_attr_getstack()/pthread_attr_setstack()*/
void* stackaddr;
/*线程栈的位置,pthread_attr_getstackaddr()/pthread_attr_setstackaddr()*/
size_t stacksize;
/*线程栈的大小,pthread_attr_getstacksize()/pthread_attr_setstacksize()*/
}pthread_attr_t;
pthread_attr_setdetachstate()/pthread_attr_getdetachstate()
设置一个线程为分离线程,而这个线程运行又很快,那么可能出现pthread_create()返回之前,子线程就终止运行,那么可能会将线程号和系统资源移交给其他线程使用,这样pthread_create()就得到了错误的线程号,要避免这种情况,最简单的方式之一是可以在被创建的线程里调用pthread_cond_timewait(),该函数可以使线程留出足够的时间,让pthread_create()返回,设置等待时间也是多线程编程中常用的手段。
#include <pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
DESCRIPTION
The pthread_attr_setdetachstate() function sets the detach state attribute of the thread
attributes object referred to by attr to the value specified in detachstate. The detach
state attribute determines whether a thread created using the thread attributes object
attr will be created in a joinable or a detached state.
The following values may be specified in detachstate:
PTHREAD_CREATE_DETACHED
Threads that are created using attr will be created in a detached state.
PTHREAD_CREATE_JOINABLE
Threads that are created using attr will be created in a joinable state.
The default setting of the detach state attribute in a newly initialized thread attributes
object is PTHREAD_CREATE_JOINABLE.
The pthread_attr_getdetachstate() returns the detach state attribute of the thread attributes
object attr in the buffer pointed to by detachstate.
RETURN VALUE
On success, these functions return 0; on error, they return a nonzero error number.
使用pthread_attr_setdetachstate()创建分离线程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void *slaveThreadFunc(void *arg)
{
printf("---------slave thread %d has executed------------\n",pthread_self());
pthread_cancel(pthread_self());
pthread_testcancel();
}
int main(void)
{
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_create(&tid,&attr,slaveThreadFunc,NULL);
sleep(1);
if(0 != pthread_join(tid,NULL))
{
printf("should print this information due to the fact that pthread_join() can't join a detached thread\n");
}
pthread_attr_destroy(&attr);
return 0;
}
pthread_attr_setstack()/pthread_attr_getstack()
当系统中有很多线程时,可能需要减小每个线程栈的默认大小,防止进程地址空间不足,当线程调用的函数会分配很大的局部变量或者函数调用层次很深时,可能需要增加线程栈的默认大小。
#include <pthread.h>
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
DESCRIPTION
The pthread_attr_setstack() function sets the stack address and stack size attributes of
the thread attributes object referred to by attr to the values specified in stackaddr and
stacksize, respectively. These attributes specify the location and size of the stack that
should be used by a thread that is created using the thread attributes object attr.
stackaddr should point to the lowest addressable byte of a buffer of stacksize bytes that
was allocated by the caller. The pages of the allocated buffer should be both readable and
writable.
The pthread_attr_getstack() function returns the stack address and stack size attributes
of the thread attributes object referred to by attr in the buffers pointed to by stackaddr
and stacksize, respectively.
RETURN VALUE
On success, these functions return 0; on error, they return a nonzero error number.
借助线程属性修改栈空间
如下程序,更改SIZE查看不同栈空间下最后创建了多少线程。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#define SIZE 0x10000
void *slaveThreadFunc(void *arg)
{
while(1)
sleep(20);
}
int main(void)
{
pthread_t tid;
size_t stacksize; //typedef size_t unsigned int
void * stackaddr;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
for(int i = 0;;i++)
{
stackaddr = malloc(SIZE);
stacksize = SIZE;
pthread_attr_setstack(&attr,stackaddr,stacksize);
pthread_create(&tid,&attr,slaveThreadFunc,NULL);
printf("%d\n",i);
}
pthread_attr_destroy(&attr);
return 0;
}
NPTL
- 使用
getconf GNU_LIBPTHREAD_VERSION在命令行中查看当前pthread库版本。 - NPTL实现机制{POSIX},Native POSIX Thread Library
- 使用线程库时,一定要为gcc指定
-lpthread
线程使用注意事项
- 主线程退出而其他线程不退出,主线程应调用
pthread_exit() - 避免僵尸线程
pthread_detach()
pthread_join()
被join的线程可能在pthread_join()返回之前就释放了所有内存资源,所以不应返回被回收线程中的值 malloc()和mmap()申请的内存可以被其他线程释放- 尽量避免在多线程模型中使用
fork()除非马上exec(),原因是fork()之后的子进程中仅仅存在调用fork()的线程,其他线程均通过pthread_exit()退出,因而子进程继承下来的资源也只有调用fork()的线程中的资源。 - 应避免在线程中使用信号。
169万+

被折叠的 条评论
为什么被折叠?



