linux中线程的设计

来源:互联网 发布:美化声音的软件 编辑:程序博客网 时间:2024/05/21 19:34
struct task_struct  {
      ...
      pid_t pid;//thread id
      pid_t tgid;//如果新创建的线程是线程组中的第一个线程,即主线程,则 TGID 的值就是这个线程 PID 的值,否则 TGID 的值等于进程的 PID (即主线程的 PID )。有了 TGID,内核或相关的 shell 程序就知道某个tast_struct 是代表一个进程还是代表一个线程
      ...
      struct task_struct *group_leader;指向线程组中的第一个线程,创建第一个线程的时候,group_leader指向自己,创建其后的线程时,指向第一个线程的task_struct结构;
      ...
      struct list_head thread_group;//当前进程所有线程的队列的队列头
      ...
  };


接口pthread_create创建线程的步骤如下:


1)调用 mmap() 在堆上分配内存


2)调用 mprotect() 设置个内存页的保护区 (大小为 4K) ,比如页面起始地址为 0x7f70b6c2f000 , 这个页面用于监测栈溢出,如果对这片内存有读写操作,那么将会触发一个 SIGSEGV 信号。


3)调用 clone() 创建线程。在 Linux 中,该接口用来创建进程,实质上, Linux 中线程是用进程来实现


int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
       struct pt_regs regs;//内核处理函数通过该结构取得系统栈内相应参数的值    


       regs.ebx = (unsigned long) fn;          /* ebx指向函数地址 */
       regs.edx = (unsigned long) arg;         /* edx指向参数地址 */


       regs.xds = __USER_DS;
       regs.xes = __USER_DS;
       regs.xfs = __KERNEL_PERCPU;
       regs.orig_eax = -1;
       regs.eip = (unsigned long) kernel_thread_helper;       /*eip指向回调函数*/
       regs.xcs = __KERNEL_CS | get_kernel_rpl();
       regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;




       /* 利用do_fork来产生一个新的线程,共享父进程地址空间,并且不允许调试子进程 */
       return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, 0, NULL, NULL);
}


extern void kernel_thread_helper(void);  /* 定义成全局变量 */
__asm__(".section .text\n"
    ".align 4\n"
    "kernel_thread_helper:\n\t"
    "movl %edx,%eax\n\t"
    "pushl %edx\n\t"   /* edx指向参数,压入堆栈 */
    "call *%ebx\n\t"   /* ebx指向函数地址,执行函数 */
    "pushl %eax\n\t"
    "call do_exit\n"   /* 结束线程 */
    ".previous");






CLONE_FS 表示父进程和子进程共享文件系统信息,包括文件系统根目录、当前工作目录和 umask 。在父进程或子进程中调用 chroot , chdir 和 umask 也会影响其他进程。


CLONE_FILES 表示父进程和子进程共享相同的文件描述符表。在父进程或子进程中打开一个新的文件,或者关闭一个文件,或者用 fcntl 修改相关的文件 flag ,也会影响其他进程。


CLONE_VM 表示父进程和子进程共享内存空间;也就是说,任何一个进程在内存中修改,也会影响其他进程,包括进程中执行 mmap 或 munmap 操作,也会影响其他进程。值得一提的是, fork 也是调用 clone 来创建子进程的,它也不会设置 CLONE_VM 标记。


CLONE_SIGHAND 表示父进程和子进程共享相同的信号处理程序表,即父进程或子进程通过 sigaction 修改信号的处理方式,也会影响其他进程。但是父进程和子进程各种有独立掩码,因此一个进程通过 sigprocmask 来阻塞或不阻塞某个信号,是不会影响其他进程的。


CLONE_THREAD 用来表示子进程与父进程在同一个线程组( thread group )中。简单的说,创建的子进程对于用户空间来说就是创建一个线程。




CLONE_SYSVSEM 用来表示子进程与父进程共享相同的信号量列表。


上面说的“子进程”实质就是我们创建的线程,从这些标识也能看出,进程中各个线程之间共享了那些资源。




long do_fork(unsigned long clone_flags,
          unsigned long stack_start,
          struct pt_regs *regs,
          unsigned long stack_size,
          int __user *parent_tidptr,
          int __user *child_tidptr)
{
    ...
    ...
    p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);//返回子进程的指针
    ...
    ...




static struct task_struct *copy_process(unsigned long clone_flags,
                       unsigned long stack_start,
                       struct pt_regs *regs,
                       unsigned long stack_size,
                       int __user *parent_tidptr,
                       int __user *child_tidptr,
                       struct pid *pid)
{
      int retval;
      struct task_struct *p = NULL;
 
    //clone_flags参数的有效性判断
//如果定义CLONE_THREAD,则必须要定义CLONE_SIGHAND
      if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
          return ERR_PTR(-EINVAL);
  //如果定义CLONE_SIGHAND,则必须要定义CLONE_VM
      if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
          return ERR_PTR(-EINVAL);
         ...


//如果用户的进程总数超过了限制
      if (atomic_read(&p->user->processes) >=p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&p->user != current->nsproxy->user_ns->root_user)
              goto bad_fork_free;
      }


//更新进程用户的相关计数
      atomic_inc(&p->user->__count);
      atomic_inc(&p->user->processes);
      get_group_info(p->group_info);
      //当前进程数是否大于系统规定的最大进程数
      if (nr_threads >= max_threads)
          goto bad_fork_cleanup_count;
 
    //加载进程的相关执行模块
      if (!try_module_get(task_thread_info(p)->exec_domain->module))
          goto bad_fork_cleanup_count;
 
      if (p->binfmt && !try_module_get(p->binfmt->module))
          goto bad_fork_cleanup_put_domain;
 
      //子进程还在进行初始化,没有execve
      p->did_exec = 0;
      delayacct_tsk_init(p); /* Must remain after dup_task_struct() */


      p->tgid = p->pid;
    if (clone_flags & CLONE_THREAD) 
        p->tgid = current->tgid;//在创建线程时,从父进程获取tgid,表明他们在同一个线程组中


//copy父进程的其它资源.比例打开的文件,信号,VM等等
      if ((retval = security_task_alloc(p)))
          goto bad_fork_cleanup_policy;
      if ((retval = audit_alloc(p)))
          goto bad_fork_cleanup_security;
      /* copy all the process information */
      if ((retval = copy_semundo(clone_flags, p)))
          goto bad_fork_cleanup_audit;
      if ((retval = copy_files(clone_flags, p)))
          goto bad_fork_cleanup_semundo;
      if ((retval = copy_fs(clone_flags, p)))
          goto bad_fork_cleanup_files;
      if ((retval = copy_sighand(clone_flags, p)))
          goto bad_fork_cleanup_fs;
      if ((retval = copy_signal(clone_flags, p)))
          goto bad_fork_cleanup_sighand;
      if ((retval = copy_mm(clone_flags, p)))
          goto bad_fork_cleanup_signal;
      if ((retval = copy_keys(clone_flags, p)))
          goto bad_fork_cleanup_mm;
      if ((retval = copy_namespaces(clone_flags, p)))
          goto bad_fork_cleanup_keys;

      retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);//创建结束后返回到用户空
      if (retval)
          goto bad_fork_cleanup_namespaces;


        ...


p->group_leader = p;//对于第一个线程,则group_leader就是它自己
        INIT_LIST_HEAD(&p->thread_group);//对group_leader和thread_group初始化


        ...


        if (clone_flags & CLONE_THREAD) {
p->group_leader = current->group_leader;//将新创建的线程的group_leader设置成为父进程的group_leader,
list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);//通过thread_group字段,挂入到第一个线程的thread_group队列中
}


...


if (likely(p->pid)) {
            ...
if (thread_group_leader(p)) {
               ...
list_add_tail_rcu(&p->tasks, &init_task.tasks);//只有线程组中的第一个线程,才会通过tasks字段,挂入到init_task队列中
               ...
}
            ...
}
         ...
}




Linux环境下使用pthread_create()函数创建线程,函数原型如下:
#include<pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg)


Linux环境下使用pthread_exit()函数终止线程,函数原型如下:
#include<pthread.h>
void pthread_exit(void *rval_ptr)


使用pthread_join()函数访问指定线程的结束信息,函数原型如下:
#include<pthread.h>
int pthread_join(pthread_t tid, void **rval_ptr)


Linux环境下使用pthread_cancel()函数取消另一个线程,函数原型如下:
#include<pthread.h>
int pthread_cancel(pthread_t tid)



0 0
原创粉丝点击