CPU性能课程笔记一:进程和线程的关系

让我们回顾最古朴的进程和线程的区别:其中进程是资源封装的单位,其中具体封装的内容有CPU、内存、信号、文件系统;而线程则是最小的CPU资源的执行单元

在我之前学习Linux的时候,总是对于进程和线程的区别不是太理解,在这个课程中,张师傅从几个方面来说明了其区别。首先二者实际上共用一个数据结构task_struct,这个数据结构定义在include/linux/sched.h中,我们首先关注其中的pid的部分:


c

复制代码

struct task_struct { pid_t pid; // 这个是 process 的 id pid_t tgid; // 这个是 thread 的 id }

为了验证我们的猜想,于是编写了一个函数,该文件名为 a0_tid_tgid.c


c

复制代码

#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> void *foo(void * args) { sleep(1000); } int main() { printf("start ... \n"); pthread_t t[4]; int i = 0; for (i=0; i<4; i++) { pthread_create(&t[i], NULL, foo, NULL); } for (i=0; i<4; i++) { pthread_join(t[i], NULL); } printf("end ... \n"); return 0; }

然后开始编译以及运行程序:


shell

复制代码

gcc -pthread a0_tid_tgid.c -o a0_tid_tgid

然后进一步使用pstree | grep a0观察程序运行结果:


shell

复制代码

64264 64264 S ./a0_tid_tgid 64264 64265 S ./a0_tid_tgid 64264 64266 S ./a0_tid_tgid 64264 64267 S ./a0_tid_tgid 64264 64268 S ./a0_tid_tgid 64708 64708 S grep --color=auto a0

然后下面这个程序则是张师傅用来进一步说明在Linux中,进程和线程的区别的程序。首先张师傅说明了无论是fork() 还是 pthread_create()最后底层都是使用了sys_clone()以及_do_fork()函数。

image.png

因此我们其实最需要关注的就是clone()这个函数的作用:


shell

复制代码

int clone(int (*fn)(void *), void *child_stack, int flags, void *args, ..);

其中:

  1. fn:表示 clone 生成的时候会调用的 fn 参数
  2. child_stack 表示生成的子进程的 stack
  3. flag:这个参数区分了子进程和父进程如何共享资源

让我们来仔细了解一下 threadprocess的区别:


c

复制代码

#define _GNU_SOURCE #include <sys/wait.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <sched.h> #define STACK_SIZE (256 * 1024) int child_fun(void *arg) { char *buf = (char *)arg; strcpy(buf, "hello from child"); return 0; } int main(int argc, char **argv) { int clone_flags = SIGCHLD; // 如果有第二个参数,就修改在调用clone的时候共享内存, // 实际上我们可以认为 clone_flags |= CLONE_VM // 这个参数就是在 pthread_create 也用了 if (argc >1 && !strcmp(argv[1], "clone_vm")) { clone_flags |= CLONE_VM; } char buf[] = "msg from parent"; char *stack = malloc(STACK_SIZE); int child_pid = clone(child_fun, stack+STACK_SIZE, clone_flags, buf); if(child_pid < 0) { exit(1); } if(waitpid(child_pid, NULL, 0) < 0) { exit(1); } printf("buf: %s \n", buf); return 0; }

然后编译查看两个程序的运行情况:


shell

复制代码

./a3_clone buf: msg from parent ./a3_clone clone_vm buf: hello from child

最后还有一个我个人认为在这个课程中学到的知识,那就是thread才是CPU运行资源的单位,但是process才是CPU的资源封装的单位。为了让实验结果明显,首先查看一下CPU的属性:


shell

复制代码

lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 40 bits physical, 48 bits virtual CPU(s): 4 On-line CPU(s) list: 0-3

也就是我当前的vm中有四个vCPU,因此,我选择让线程数目超过四。

首先编写两个程序,首先是使用了四个线程的test_a


c

复制代码

// test_a.c #include <stdio.h> #include <pthread.h> #define NUM_THREADS 4 void *do_work(void *args) { int i = 0; while (1) { i += 1; } return NULL; } int main() { pthread_t threads[NUM_THREADS]; int i; // 创建四个线程 for (i = 0; i < NUM_THREADS; ++i) { pthread_create(&threads[i], NULL, do_work, NULL); } // 等待所有线程结束 for (i = 0; i < NUM_THREADS; ++i) { pthread_join(threads[i], NULL); } return 0; }

以及使用了六个线程的test_b.c程序:


c

复制代码

#include <stdio.h> #include <pthread.h> #define NUM_THREADS 6 void *do_work(void *args) { int i = 0; while (1) { i += 1; } return NULL; } int main() { pthread_t threads[NUM_THREADS]; int i; // 创建四个线程 for (i = 0; i < NUM_THREADS; ++i) { pthread_create(&threads[i], NULL, do_work, NULL); } // 等待所有线程结束 for (i = 0; i < NUM_THREADS; ++i) { pthread_join(threads[i], NULL); } return 0; }

然后编译:


shell

复制代码

gcc -pthread test_a.c -o test_a gcc -pthread test_b.c -o test_b

同时运行,然后在top中查看CPU使用情况,可以看到二者使用的资源差不持平:

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值