1 概述
进程与其对应的线程之间使用相同的内存空间、文件描述符和一些其他的东西。
2 分析
在内核中,线程与进程都是用结构体task_struct来表示的,在内核调度上并没有什么区别。
2.1 相同点
我们已经在上文中指出主线程与线程之间使用相同的内存空间、文件描述符和一些其他的东西。
#include <stdio.h>
#include <pthread.h>
void *thread1(void *data)
{
while (1){
printf("thread 1\n");
sleep(1);
}
pthread_exit(0);
return;
}
void *thread2(void *data)
{
while (1){
printf("thread 2\n");
sleep(2);
}
pthread_exit(0);
return;
}
int main()
{
pthread_t th1;
pthread_t th2;
if (pthread_create(&th1, NULL,thread1, NULL)){
return 1;
}
if (pthread_create(&th2, NULL,thread2, NULL)){
return 1;
}
pthread_join(th1, NULL);
pthread_join(th2, NULL);
return 0;
}
为了验证这些观点,我以上面的用户态代码示例来说明(代码不一定规范,仅为说明问题)。如上代码,在主线程成创建了两个线程thread1和thread2,然后就等待这两个线程执行完成,但这两个线程都一直在死循环中,也就说,整个都不为结束,除非我手动杀掉这个进程。
在内核中编写(test)模块来查看这三个(主)线程之间的关系,代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <asm/string.h>
static void print_task(struct task_struct *p)
{
printk(KERN_ALERT "pid: %d,tgid: %d, mm = %p, files = %p\n",
p->pid, p->tgid, p->mm, p->files);
return;
}
static int hello_init(void)
{
struct task_struct *g =NULL;
struct task_struct *t =NULL;
printk(KERN_ALERT "\ntestmodule init\n");
do_each_thread(g, t){
if (!strcmp(g->comm,"a.out")) {
print_task(t);
}
}while_each_thread(g,t);
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "test moduleexit\n");
}
MODULE_LICENSE("DualBSD/GPL");
module_init(hello_init);
module_exit(hello_exit);
如上代码,轮训当前所有的线程,找到名称为“a.out”的线程(即以上用户态代码生成的线程)。打印对应线程的pid、tgid、mm和files。其中,pid表示线程的一个标识;tgid即threadgroup id,表示该线程组的id,其实也就是主线程的id;mm表示该task使用的内存空间;file表示该task所有打开的文件描述符。运行结果如下:
从上述结果中可以看出,主线程和两个辅线程都拥有各自的pid,但其tgid都等于主线程的pid,而且他们的mm和files也指向同样的一块内存,也就是说:主线程和辅线程之间的确共享着内存空间和文件描述符信息。
在内核中,调度主线程和辅线程是没有区别的。
版权声明:本文为博主原创文章,未经博主允许不得转载。