pthread与clone()函数解析

实验内容

  1. 编译运行Lecture 14示例代码:alg.14-1~alg.14-7,指出你认为不合适的地方并加以改进

  2. clone()的flags采用不同的配置,设计测试程序讨论其调用结果。

    • 配置包括CLONE_PARENT,CLONE_VFORK,CLONE_FILES,CLONE_SIGHAND,CLONE_THREAD

实验环境

  • 架构:Intel x86_64(双系统)

  • 操作系统:Ubuntu 20.04

  • 汇编器:gas(GNU Assembler) in AT&T mode

  • 编译器:gcc

技术日志

alg.14-1-tls-thread分析

执行命令

gcc -o alg.14-1-tls-thread.o alg.14-1-tls-thread.c -l pthread

p.s:由于pthread不是C库中的内容,而是POSIX提供的线程集成操作集,因此在生成可执行文件时需将其加入编译的路径中。

执行结果

代码及原理分析

代码原理

  1. pthread主要函数功能分析

    Pthread函数
    pthread_create create a new thread
    pthread_exit terminate the calling thread
    pthread_join wait for a specific thread to exit
    pthread_yield release the CPU to let another thread run
    pthread_attr_init create and initialize a thread's attribute structure
    pthread_attr_destroy remove a thread's attribute structure
  2. pthread_create函数分析

    函数原型

    int pthread_create(pthread_t &restict ptid,const pthread_attr_t *restrict_attr,void*(start_rtn)(void*),void*restrict arg)

    第一个参数ptid用来存储该函数申请到的线程号(不唯一);第二个参数pthread_attr_t表示该申请的子线程需要拥有的性质,没有特殊要求则传入(NULL);第三个参数表明生成的子线程在完成创建之后将要执行的函数,因此传入该函数对应的首地址(对应线程代码段的首地址),注意:此处所使用函数的首地址与父线程中该函数的地址相同。最后一个参数,表明需要传入前一个函数的参数列表。

宏定义解析

#define __NR_gettid 186 /* 186 for x86-64, 224 for i386-32 */
#define gettid() syscall(__NR_gettid) /* long int */
​
__thread int tlsvar = 0; 
 /* tlsvar for each thread; interpreted by language compiler */

首先此处定义了一个调用system call查询当前线程线程号的宏定义,此处的__NR_gettid由主机的架构决定x86-64体系使用186,i386-32体系则使用224.在调用该宏定义后则会在终端自动打印出当前线程的线程号。随后,定义一个用于之后实现tls的全局变量tlsvar,值得注意的是该全局变量会由编译器进行特殊的处理,其结果是所有生成的子进程都会拥有一个自己的tlsvar(通过这个变量作为标识符,可以让每个线程在共有的全局变量区内使用某块特定的内存存储自己私有的变量

子线程执行的函数(其拥有的代码段)

void printf_tlsvar(char *param)
{
        /* sharing the TLS tlsvar with thread_worker */
    printf("%s%ld, tlsvar = %d\n", param, gettid(), tlsvar);
​
    return;
}
​
​
static void* thread_worker(void* arg)
{  
    char *param = (char *)arg; 
    int randomcount;
​
    for (int i = 0; i < 5; ++i) {
        randomcount = rand() % 100000;
        for (int k = 0; k < randomcount; k++) ; /* delay for a random time */
        printf_tlsvar(param);
        tlsvar++; /* each thread has its local tlsvar */
    }
    
    pthread_exit(0);
​
} 

printf_tlsvar(char *param)中,会自动打印出传入的参数(此处使用不同长度\t来区分不同线程),以及当前线程号及其对应的tls_var的数值。为了使得不同tls_var的出现拥有随机性,此处使用randomcount = rand() % 100000;for (int k = 0; k < randomcount; k++) ;让任意线程延迟随机长的时间段。当执行完这段函数后,则自动退出成功pthread_exit(0);

生成子进程的main函数分析

int main(void)
{
    pthread_t ptid1, ptid2;  
    char para1[] = "\t\t\t";
    char para2[] = "\t\t\t\t\t\t";
    int randomcount;
​
    pthread_create(&ptid1, NULL, &thread_worker, para1);  
    pthread_create(&ptid2, NULL, &thread_worker, para2);  
    
    printf("parent                  tid1                    tid2\n");
    printf("================        ================        ================\n");
​
    for (int i = 0; i < 5; ++i) {
        randomcount = rand() % 100000;
        for (int k = 0; k < randomcount; k++) ;
        printf("%ld, tlsvar = %d\n", gettid(), tlsvar);
        tlsvar++; /* main- thread has its local tlsvar */
    }
​
    sleep(1);
    pthread_join(ptid1, NULL);  
    pthread_join(ptid2, NULL);  
​
    return 0;  
}

在主线程中,首先使用para1para2用来更明显的区分不同的子线程。随后,调用pthread_create()函数创建两个子进程,并将para1para2作为参数传入函数中(此时,二者的代码段首地址相同,但是堆栈空间不同)。然后主线程与两个子线程同时执行打印线程号以及tlsvar的相似代码段,从执行结果可以看到,三个子线程的tlsvar完全隔离。最后,在完成打印任务休眠一秒后,在等待两个子线程成功退出后(调用join函数),结束自身进程。

alg.14-2-tls-pthread-key-1.c分析

执行命令

gcc -o alg.14-2-tls-pthread-key-1.o alg.14-2tls-pthread-key-1.c -l pthread

执行结果

代码原理与分析

代码原理:

$$
\begin{aligned} impleamentation\_of\_TLS: \_\_thread&:tlsvar\,for\,each\,thread,gcc\,extension\,of\,C,interpreted\,by compiler,a\,language\,level\,solutional\,of\,TLS\\ pthread\,API&:\ \begin{aligned} &pthread\_key\_create\\&pthread\_get\_spcific\\&pthread\_set\_sepcific \end{aligned} \end{aligned}
$$

pthread_key_create创建该线程独有的关键字变量(创建时需传入call back函数,以关闭申请的资源),随后调用pthread_get_specific()并将key

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值