今天看到看到有人介绍说Linux中线程怎么么好,切换代价极低,响应快,能有效利用多处理器机制。然后我就纳闷了:Linux中线程不是完全和进程一样,不进行任何区分么?怎么就有这些独特的好处了?
于是我跑到Linux内核源码里面想找找看pthread_create函数的实现,但是没找到。
后来看到有人说:
在这里下载glibc源代码:http://ftp.gnu.org/pub/gnu/glibc/glibc-2.3.5.tar.gz
tar zxvf glibc-2.3.5.tar.gz解开压缩包
cd glibc-2.3.5/nptl
该目录下的pthread_create.c即为你想看的实现
然后才知道这个函数是在Glibc的库里面实现的!!!我们先看看什么是glibc:
========================================================================================
glibc
glibc是gnu发布的libc库,即c运行库。glibc是linux系统中最底层的api,几乎其它任何运行库都会依赖于glibc。
glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现。由于 glibc 囊括了几乎所有的 UNIX 通行的标准,可以想见其内容包罗万有。
而就像其他的 UNIX 系统一样,其内含的档案群分散于系统的树状目录结构中,像一个支架一般撑起整个作业系统。在 GNU/Linux 系统中,其C函式库发展史点出了GNU/Linux 演进的几个重要里程碑,用glibc 作为系统的C函式库,是GNU/Linux演进的一个重要里程碑。
功能实现主要的如下:
(1)string,字符串处理
(2)signal,信号处理
(3)dlfcn,管理共享库的动态加载
(4)direct,文件目录操作
(5)elf,共享库的动态加载器,也即interpreter
(6)iconv,不同字符集的编码转换
(7)inet,socket接口的实现
(8)intl,国际化,也即gettext的实现
(9)io
(10)linuxthreads
(11)locale,本地化
(12)login,虚拟终端设备的管理,及系统的安全访问
(13)malloc,动态内存的分配与管理
(14)nis
(15)stdlib,其它基本功能
在 GNU/Linux 系统中,其 C 函式库的发展史点出了 GNU/Linux 演进的几个重要里程碑,由此可以突显出 C 函式库在系统中的地位与重要性。早期的 GNU/Linux 系统不支援可分享函式库,因此所有的应用程式都是以静态连结的方式存于系统中。直到 1995-1996 年 libc5 问世以后,系统才开始支援 ELF 可分享函式库,同时该版的 C 函式库也?作了其他 UNIX 上大部分的功能与函式群,可谓 GNU/Linux 发展上的一大进步。
然而 libc5 在程式国际化 (I18N) 与本土化 (L10N) 方面的支援很差,故那个时候若要开发所谓中文化的程式,就非得自行?作一套标准不可。直到一两年后, GNU/Linux 换用了 GNU 所开发的 glibc-2.0 做为其 C 函式库后,其国际化与本土化的支援才开始起步,后经 glibc-2.1,到了现在的 2.2 版,整个多国语文的开发环境才大至成熟。
用 glibc 做为系统的 C 函式库,是 GNU/Linux 演进的一个重要里程碑。
比起过去的 libc5,glibc 系列 (一般又称之为 libc6) 除了有完整的国际化与本土化支援外,同时还符合许多标准与规格,使得在 glibc 下开发的程式可以很容易移植到其他 UNIX 平台去。这些标准包括:
ISO C:
ISO C 是 International Standard for the C programming language 的缩写,此标准明定了 C 语言的语法,标准 C 函式库应具备那些标头档、巨集定义、函式与物件 .... 等等,几乎在任何平台上的 C 语言 (包括非 UNIX 平台) 都支援此标准。
POSIX:
POSIX 是 Portable Operating System Interface for Computer Environments 的缩写,它是 ISO C 的延伸,明定了一个可移植的作业系统所应具备种种条件,其范围不只有系统函式库而已,还同时包括一些标准的工具程式、系统核心应有的特色与?作、以及在 C 函式库中某些与作业系统相关的低阶控制支援 (如系统呼叫窗口) 等等。由于 glibc 是完全按照 POSIX 的标准制作的,同时搭配了符合 POSIX 标准的 Linux 核心,故在此环境下开发的程式可以做到完全符合 POSIX 的规格。
Berkeley Unix:
Berkeley Unix 泛称柏克莱大学所开发的 UNIX 系列作业系统,包括 4.2 BSD、4.3 BSD、4.4 BSD 以及早期的 SunOS。这些系统的 C 函式库中有许多杰出的设计,但却没有在上述两个标准中,包括 select() 函式、sockets .... 等等,这些在 glibc 中都有支援。
等等。。。
====================================================================================
======================================pthread_create.c===============================
下面看看这个函数的源码吧(只保留重要的几行):
int
__pthread_create_2_1 (newthread, attr, start_routine, arg)
pthread_t *newthread;
const pthread_attr_t *attr;
void *(*start_routine) (void *);
void *arg;
{
STACK_VARIABLES;
const struct pthread_attr *iattr;
struct pthread *pd; //。。。
上面省略的代码主要是:分配栈空间等,初始化pthread结构pd和pthread_attr结构iattr,其中内容基本上是从其parent拷贝过来的,然后设置一些必要的标志
/ Start the thread. / err = create_thread (pd, iattr, STACK_VARIABLES_ARGS); }
versioned_symbol (libpthread, __pthread_create_2_1, pthread_create, GLIBC_2_1);
=======================================createthread.c=====================================
在createthread.c中create_thread方法:
static int
create_thread (struct pthread *pd, const struct pthread_attr *attr,
STACK_VARIABLES_PARMS)
{
/ Actually create the thread. / int res = do_clone (pd, attr, clone_flags, start_thread, STACK_VARIABLES_ARGS, stopped);
}
===================================createthread.c============================
static int
do_clone (struct pthread *pd, const struct pthread_attr *attr,
int clone_flags, int (*fct) (void *), STACK_VARIABLES_PARMS,
int stopped)
{
#ifdef PREPARE_CREATE
PREPARE_CREATE;
#endif
if (stopped)
/ We Make sure the thread does not run far by forcing it to get a
lock. We lock it here too so that the new thread cannot continue
until we tell it to. /
lll_lock (pd->lock);
/ One more thread. We cannot have the thread do this itself, since it
might exist but not have been scheduled yet by the time we've returned
and need to check the value to behave correctly. We must do it before
creating the thread, in case it does get scheduled first and then
might mistakenly think it was the only thread. In the failure case,
we momentarily store a false value; this doesn't matter because there
is no kosher thing a signal handler interrupting us right here can do
that cares whether the thread count is correct. / atomic_increment (&__nptl_nthreads);
if (ARCH_CLONE (fct, STACK_VARIABLES_ARGS, clone_flags,
pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
{
atomic_decrement (&__nptl_nthreads); / Oops, we lied for a second. /
/ Failed. If the thread is detached, remove the TCB here since
the caller cannot do this. The caller remembered the thread
as detached and cannot reverify that it is not since it must
not access the thread descriptor again. / if (IS_DETACHED (pd))
__deallocate_stack (pd);
return errno;
}
/ Now we have the possibility to set scheduling parameters etc. / if (__builtin_expect (stopped != 0, 0))
{
INTERNAL_SYSCALL_DECL (err);
int res = 0;
/ Set the affinity mask if necessary. / if (attr->cpuset != NULL)
{
res = INTERNAL_SYSCALL (sched_setaffinity, err, 3, pd->tid,
sizeof (cpu_set_t), attr->cpuset);
if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
{
/ The operation failed. We have to kill the thread. First
send it the cancellation signal. / INTERNAL_SYSCALL_DECL (err2);
err_out:
#if __ASSUME_TGKILL
(void) INTERNAL_SYSCALL (tgkill, err2, 3,
THREAD_GETMEM (THREAD_SELF, pid),
pd->tid, SIGCANCEL);
#else
(void) INTERNAL_SYSCALL (tkill, err2, 2, pd->tid, SIGCANCEL);
#endif
return (INTERNAL_SYSCALL_ERROR_P (res, err)
? INTERNAL_SYSCALL_ERRNO (res, err)
: 0);
}
}
/ Set the scheduling parameters. / if ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)
{
res = INTERNAL_SYSCALL (sched_setscheduler, err, 3, pd->tid,
pd->schedpolicy, &pd->schedparam);
if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
goto err_out;
}
}
/ We now have for sure more than one thread. The main thread might
not yet have the flag set. No need to set the global variable
again if this is what we use. / THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1);
return 0;
=====================================================================================
没看出来glibc中创建线程与Linux源码的fork.c中创建进程的do_fork函数有什么明显的区别。。。
难道Linux仅仅是单纯地保留了Unix中的API,其实内在并没有任何联系?
Unix中的线程和Linux中的进程的确是有很多区别?只是Linux中不把线程和进程进行区分而已。。。。
不明白。哎~
不过Unix线程之间只要是来自同一个父线程是可以共享数据的,从这一点来看我个人认为Unix中的线程和Linux内核中实现的进程/线程不但不一样,而且还没有任何关系!!!