Linux中pthread_create函数的实现

    今天看到看到有人介绍说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内核中实现的进程/线程不但不一样,而且还没有任何关系!!!

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计实现,以及它们在MATLAB环境的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值