首页 > *nix技术, 应用程序 > Linux线程相关概念

Linux线程相关概念

2013年2月6日 发表评论 阅读评论 4,708 次浏览

POSIX Threads,通常简称为Pthreads,它是指开放系统接口标准―POSIX线程,1995年规范的POSIX.1c,即线程扩展标准(IEEE Std 1003.1c-1995),其中定义了关于创建和操作线程的相关API
如今,在很多类Unix的POSIX兼容操作系统上,如FreeBSD,NetBSD,OpenBSD,GNU/Linux,Mac OS X和Solaris等,都已实现Pthreads并且可用。

但在早期的Linux系统上,它所提供的线程机制LinuxThreads仅只是部分实现了Pthreads,具体来说,LinuxThreads是通过系统调用clone来实现的,并不是Linux内核真正提供支持,直到Linux 2.6时NPTL的加入为止。也就是说,在Linux 2.6以前,操作系统最小的调度单位是进程而不是线程。

LinuxThreads的主要开发者为大牛Xavier Leroy。LinuxThreads有一些缺点,比如信号处理、进程调度和进程间同步原语等方面(参考1),因此出现了

另外两个互相竞争的项目:一个IBM的组的项目叫做NGPT(Next Generation POSIX Threads,下一代POSIX线程),另一个组是由Red Hat程序员组成的。2003年中NGPT被放弃,几乎与此同时NPTL公布了。

(参考2)

关于这部分发展历史不做多说,反正就是在当前的Linux系统中,Pthreads是由NPTL实现的,下面逐一讨论几个主题。

1,NPTL的代码位置以及维护者
NPTL的实现代码位于glibc,从man pthreads可以看到,从glibc-2.3.2开始,NPTL已经可用,而从glibc-2.4开始,旧的LinuxThreads不再可用。

NPTL由glibc开发组持续发展和维护,这其中包括另一位大牛Ulrich Drepper

2,NPTL的线程模型
在讨论这个问题之前需要先了解kernel thread(核心线程)和user thread(用户线程),其

分类标准主要是线程的调度者在核内还是在核外。前者更利于并发使用多处理器的资源,而后者则更多考虑的是上下文切换开销。

(参考1,参考6)

线程模型分为三种:1:1 (Kernel-level threading)、N:1 (User-level threading)和M:N (Hybrid threading)。

对于Linux系统而言,采用的是1:1线程模型,这意味着对于有N个用户线程的程序,就有N个核心线程与此一一对应,这也被认为是最容易用代码实现并且在实践中最高效的模型

3,同一个进程的多个线程之间共享了哪些数据,各自单独拥有哪些数据。
共享数据(参考4):
程序指令(Process instructions)
大部分数据(Most data)
打开文件描述符(open files (descriptors))
信号与信号处理(signals and signal handlers)
当前工作目录(current working directory)
用户id和组id(User and group id)

单独享有(参考4):
线程id(Thread ID)
部分寄存器和栈指针(set of registers, stack pointer)
栈局部变量和返回地址(stack for local variables, return addresses)
信号掩码(signal mask)
调度优先级(priority)
返回值(Return value: errno)

实例:

[root@localhost pthread]# rz
rz waiting to receive.
[root@localhost pthread]# rpm -q glibc
glibc-2.12-1.7.el6.x86_64
glibc-2.12-1.7.el6.i686
[root@localhost pthread]# cat pthread1.c 
/**
 * gcc pthread1.c -o pthread1 -lpthread
 * ref: http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
 * modified by: http://lenky.info/
 */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *print_message_function( void *ptr );

main()
{
     pthread_t thread1, thread2;
     char *message1 = "Thread 1";
     char *message2 = "Thread 2";
     int  iret1, iret2;

    /* Create independent threads each of which will execute function */

     iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1);
     iret2 = pthread_create( &thread2, NULL, print_message_function, (void*) message2);

     /* Wait till threads are complete before main continues. Unless we  */
     /* wait we run the risk of executing an exit which will terminate   */
     /* the process and all threads before the threads have completed.   */

     pthread_join( thread1, NULL);
     pthread_join( thread2, NULL); 

     printf("Thread 1 returns: %d\n",iret1);
     printf("Thread 2 returns: %d\n",iret2);
     exit(0);
}

void *print_message_function( void *ptr )
{
     char *message;
     message = (char *) ptr;
     printf("%s \n", message);
}
[root@localhost pthread]# gcc pthread1.c -o pthread1 -lpthread -g
[root@localhost pthread]# gdb ./pthread1 -q
Reading symbols from /home/work/pthread/pthread1...done.
(gdb) b 40
Breakpoint 1 at 0x4006ca: file pthread1.c, line 40.
(gdb) r
Starting program: /home/work/pthread/pthread1 
[Thread debugging using libthread_db enabled]
[New Thread 0x7ffff7fd6710 (LWP 7307)]
[Switching to Thread 0x7ffff7fd6710 (LWP 7307)]

Breakpoint 1, print_message_function (ptr=0x4007e8) at pthread1.c:40
40	     printf("%s \n", message);
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.7.el6.x86_64
(gdb) info threads 
[New Thread 0x7ffff77d5710 (LWP 7308)]
  3 Thread 0x7ffff77d5710 (LWP 7308)  0x0000003eba0e14f1 in clone () from /lib64/libc.so.6
* 2 Thread 0x7ffff7fd6710 (LWP 7307)  print_message_function (ptr=0x4007e8) at pthread1.c:40
  1 Thread 0x7ffff7fd8700 (LWP 7304)  0x0000003ebac05bf0 in __nptl_create_event ()
   from /lib64/libpthread.so.0
(gdb) thread 3
[Switching to thread 3 (Thread 0x7ffff77d5710 (LWP 7308))]#0  0x0000003eba0e14f1 in clone ()
   from /lib64/libc.so.6
(gdb) info reg rsp
rsp            0x7ffff77d4ff0	0x7ffff77d4ff0
(gdb) thread 2 
[Switching to thread 2 (Thread 0x7ffff7fd6710 (LWP 7307))]#0  print_message_function (
    ptr=0x4007e8) at pthread1.c:40
40	     printf("%s \n", message);
(gdb) info reg rsp
rsp            0x7ffff7fd5e90	0x7ffff7fd5e90
(gdb) thread 1
[Switching to thread 1 (Thread 0x7ffff7fd8700 (LWP 7304))]#0  0x0000003ebac05bf0 in __nptl_create_event () from /lib64/libpthread.so.0
(gdb) info reg rsp
rsp            0x7fffffffe3c8	0x7fffffffe3c8
(gdb) 

可以看到三个线程的栈指针是不一样的。另外,我在上面的代码基础上做了一下改动,创建5个相同的线程,查看它们的rsp分别为:

rsp            0x7fffffffe450	0x7fffffffe450
rsp            0x7ffff7fd5ca0	0x7ffff7fd5ca0
rsp            0x7ffff77d4ca0	0x7ffff77d4ca0
rsp            0x7ffff6fd3ca0	0x7ffff6fd3ca0
rsp            0x7ffff67d2ca0	0x7ffff67d2ca0
rsp            0x7ffff5fd1ca0	0x7ffff5fd1ca0

上面第一行为线程1(即主线程)的rsp,然后依次为线程2、3、4、5、6的rsp,可以看到,它们各自的可用栈空间是不重叠的,每个栈大小为0x801000,即8196KB,这刚好匹配我的系统设置(各个栈之间应该是留了一页内存作为分割屏障):

[root@localhost pthread]# ulimit -s
8192

设置栈大小为1024后,测试各个线程之间的rsp相差:

[root@localhost pthread]# ulimit -s 1024
[root@localhost pthread]# ulimit -s
1024

0x7ffff7fd5ca0 – 0x7ffff7ed4ca0 = 0x101000
即1028KB。

[root@localhost pthread]# cat t.c 
#include <pthread.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
   size_t stacksize;
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_getstacksize (&attr, &stacksize);
   printf("Default stack size = %d\n", stacksize);
   
}
[root@localhost pthread]# gcc t.c -lpthread
[root@localhost pthread]# ./a.out 
Default stack size = 1048576

新开终端用ps命令查看:

[root@localhost linux-2.6.38.8]# ps -eL -f -F | grep PSR | grep -v grep
UID        PID  PPID   LWP  C NLWP    SZ   RSS PSR STIME TTY          TIME CMD
[root@localhost linux-2.6.38.8]# ps -eL -f -F | grep pthread
root      7302 27742  7302  0    1 58006 15672   0 04:43 pts/2    00:00:00 gdb ./pthread1
root      7304  7302  7304  0    3  5641   308   0 04:43 pts/2    00:00:00 /home/work/pthread/pthread1
root      7304  7302  7307  0    3  5641   308   1 04:43 pts/2    00:00:00 /home/work/pthread/pthread1
root      7304  7302  7308  0    3  5641   308   1 04:43 pts/2    00:00:00 /home/work/pthread/pthread1
root      7487 28643  7487  0    1 25800   804   0 04:51 pts/3    00:00:00 grep pthread

PID和PPID相同,而LWP为thread ID,是各不相同的,NLWP表示number of threads,都为3,PSR列表示线程当前所在的cpu号。

4,NPTL的辅助调试工具
GDB:http://www.sourceware.org/gdb/onlinedocs/gdb/Threads.html
另外两个专门工具貌似很久没更新了,不知道现在是否还有用,如下:
POSIX Thread Trace Tool (PTT):http://nptltracetool.sourceforge.net/
Open POSIX Test Suite (OPTS):http://posixtest.sourceforge.net/

参考:
1,http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/
2,http://zh.wikipedia.org/wiki/Native_POSIX_Thread_Library
3,http://xanpeng.github.com/linux/2012/03/28/linux-pthread.html
4,http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
5,http://stackoverflow.com/questions/807506/threads-vs-processes-in-linux
6,http://www.thegeekstuff.com/2012/03/linux-threads-intro/
7,http://www.linuxquestions.org/questions/linux-newbie-8/default-stack-size-on-linux-glibc-pthreads-358438/
8,http://stackoverflow.com/questions/807506/threads-vs-processes-in-linux
9,http://stackoverflow.com/questions/2340093/how-is-stack-size-of-process-on-linux-related-to-pthread-fork-and-exec
10,http://xanpeng.github.com/linux/2012/05/15/more-linux-pthreads.html

转载请保留地址:http://www.lenky.info/archives/2013/02/2206http://lenky.info/?p=2206


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

分类: *nix技术, 应用程序 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.