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)
...
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
- linux中线程的设计
- Linux线程池的设计
- Linux线程池的设计
- linux中线程的切换
- 【转】线程池的设计linux下
- Linux下线程池的设计
- linux线程设计
- 在Linux中设置线程的优先级
- 在Linux中设置线程的优先级
- linux线程中pthread_detach函数的作用
- Linux中突破线程数的限制
- Linux内核中线程的实现方式
- Linux中线程和进程的区别
- linux中线程使用的错误
- Linux中线程和进程的区别
- linux 中线程的查看方式
- linux中线程的基本相关知识
- Linux中线程的函数解析
- 捕鱼玩家断线时,经验错误和无金币记录(得到的金币为0,GSP_GR_WriteGameScoreLog存储过程不执行)
- Sublime Text3 + Golang搭建开发环境
- 一次Java垃圾收集调优实战
- 自动化运维工具Ansible详细部署
- Android调用百度地图可能导致出错的问题
- linux中线程的设计
- 分享我的开源项目-springmore
- 选项卡切换
- 征服 Redis + Jedis + Spring (一)—— 配置&常规操作(GET SET DEL)
- MySQL server has gone away 问题的解决方法
- cvBoostNextWeakClassifier(翻译)
- word中插入MathType,遇到问题“内存不足”“不能打开toolbar.eql”
- openoffice+swftools+flexPaper 转换文件失败解决办法
- SCP 命令