• Linux 系统编程 学习:10-线程:线程的属性


    背景

    上一讲我们介绍了线程的创建,回收与销毁;简单地提到了线程属性。这一讲我们就来具体看看,线程的属性。

    概述

    c
    #include<pthread.h>
    
    typedef struct __pthread_attr_s
    {
        int                       __detachstate;   // 线程的分离状态
        int                       __schedpolicy;   // 线程调度策略
        structsched_param         __schedparam;    // 线程的调度参数
        int                       __inheritsched;  // 线程的继承性
        int                       __scope;         // 线程的作用域
        size_t                    __guardsize;     // 线程栈末尾的警戒缓冲区大小
        int                       __stackaddr_set; // 线程的栈设置
        void*                     __stackaddr;     // 线程栈的位置
        size_t                    __stacksize;     // 线程栈的大小
    } pthread_attr_t;
    
    intpthread_attr_init(pthread_attr_t *attr);
    intpthread_attr_destroy(pthread_attr_t *attr);
    

    线程具有属性,用pthread_attr_t表示,在对该结构进行处理之前必须进行初始化(pthread_attr_init),在使用后需要对其去除初始化(pthread_attr_destroy)。

    初始化为默认属性

    c
    intpthread_attr_init(pthread_attr_t *attr);
    

    描述:初始化一个线程属性对象,重置为当前系统支持线程的所有属性的默认值。

    属性
    __scope PTHREAD_SCOPE_PROCESS
    __tetachstate PTHREAD_CREATE_JOINABL
    __stackaddr NULL
    __stacksize 1M
    __sched_param.priority 0
    __inheritsched PTHREAD_INHERIT_SCHED
    __schedpolicy SCHED_OTHER

    反初始化

    c
    intpthread_attr_destroy(pthread_attr_t *attr);
    

    描述:销毁一个线程属性对象,使它在重新初始化之前不能重新使用。

    原理:用无效的值设置了属性对象。

    因此:如果经pthread_attr_destroy去除初始化之后的pthread_attr_t结构被pthread_create函数调用,将会导致其返回错误。

    detachstate 分离状态

    我们来分析结构体中的有关成员。

    c
    intpthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate);
    
    // 有set就有get 。
    intpthread_attr_getdetachstate(constpthread_attr_t* attr, int *detachstate)

    描述:设置线程是否和其他线程分离(能否调用pthread_join()回收), 运行时可以调用pthread_detach()完成。

    c
    intpthread_detach(pthread_t thread);
    

    参数解析:

    attr:设置的属性对象

    detachstate :分离状态

    • PTHREAD_CREATE_JOINABLE(默认):线程的资源在退出后自行释放。
    • PTHREAD_CREATE_DETACHED:

    设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置) 则不能再恢复到PTHREAD_CREATE_JOINABLE状态。

    返回值:成功返回0,失败返回错误号。

    schedpolicy 调度策略与优先级

    调度策略

    如果主线程是唯一的线程,那么基本上不会被调度出去。另一方面,如果可运行的线程数大于CPU的数量,那么操作系统最终会将某个正在运行的线程调度出去,从而使其他线程能够使用CPU。这将导致一次上下文切换。在这个过程中将保存当前运行线程的执行上下文,并将新调度进来的线程的执行上下文设置为当前上下文。

    Linux内核的三种调度策略:

    • SCHED_OTHER 分时调度策略,默认的调度策略
    • SCHED_FIFO 实时调度策略,先到先服务。一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃
    • SCHED_RR 实时调度策略,时间片轮转。当进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平

    继承创建者的调度策略

    只有在不继承时,下面的操作才是有效的。

    c
    intpthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
    intpthread_attr_getinheritsched(constpthread_attr_t *attr, int *inheritsched);
    

    描述:设置线程是否继承创建者优先级属性

    参数解析:

    inheritsched : 是否继承

    • PTHREAD_INHERIT_SCHED 继承
    • PTHREAD_EXPLICIT_SCHED 不继承

    获取可设置的优先级

    c
    intsched_get_priority_max(int policy);
    intsched_get_priority_min(int policy);
    

    描述:获取本线程的最大/小优先级。

    返回值:成功时返回最大/小值,失败返回-1。

    设置线程的调度策略与优先级

    c
    intpthread_setschedparam(pthread_t thread, int policy,
                              const struct sched_param *param);
    intpthread_getschedparam(pthread_t thread, int *policy,
                              struct sched_param *param);
    /* 用到的结构体 */
    structsched_param {
        int sched_priority;     /* Scheduling priority */
    };
    

    描述:设置线程的调度策略, 运行时可以调用pthread_setschedparam()来改变。

    参数解析:

    policy:

    • (默认)SCHED_OTHER(正常、非实时)
    • SCHED_RR(实时、轮转法)
    • SCHED_FIFO(实时、先入先出)

    param:优先级(越大越高)

    优先级与调度的例程

    c
    #include<pthread.h>
    #include<stdio.h>
    #include<unistd.h>
    
    void *task0(void *arg){
        while(1)
        {
            //sleep(1);
            printf("task0.
    ");
        }
    }
    
    void *task1(void *arg){
        while(1)
        {
            sleep(1);
            printf("task1.
    ");
        }
    }
    
    intmain(void){
        pthread_attr_t attr;		
        structsched_paramparm;
        pthread_t tid0, tid1;
        void* retval;
    
        pthread_attr_init(&attr);
    
        pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); //不继承创建者的调度策略,而是设置以下的调度
        pthread_attr_setschedpolicy(&attr, SCHED_FIFO); //为线程属性设置调度策略
    
        parm.sched_priority = 1;					// 设置线程优先级
        pthread_attr_setschedparam(&attr,&parm);	// 设置线程优先级
    
        pthread_create(&tid0, &attr, task0, NULL);  // 为线程task0设置优先级
        pthread_create(&tid1, NULL , task1, NULL);  // 让线程task1使用默认优先级
    
    
        while(1);
    
        // 等待线程的结束(实际上由于线程一直在循环中,所以main函数不会结束)
        pthread_join(tid0, &retval); // 等待线程的结束,并取返回值
        pthread_attr_destroy(&attr);
        pthread_join(tid1, &retval); // 等待线程的结束,并取返回值
    
    }
    

    设置线程的竞争作用域

    线程的竞争作用域:表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。

    c
    int pthread_attr_setscope(pthread_attr_t*attr,int scope);
    int pthread_attr_getscope(const pthread_attr_t*attr,int*scope);
    

    描述:设置线程的竞争范围。

    使用前,需要将pthread_attr_setinheritsched设置为PTHREAD_EXPLICIT_SCHED

    参数解析:

    scope:

    • PTHREAD_SCOPE_SYSTEM :与系统中所有线程一起竞争CPU时间
    • PTHREAD_SCOPE_PROCESS:仅与同进程中的线程竞争CPU。

    根据man pthread_attr_setscope的结果来看目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。

    Linux supports PTHREAD_SCOPE_SYSTEM, but not PTHREAD_SCOPE_PROCESS

    设置堆栈

    线程可以设置堆栈地址(stackaddr)与大小(stacksize)。对于大部分程序是应该避免使用的。

    如果使用attr创建多个线程,则调用方必须在对pthread_create()的调用之间更改堆栈地址属性;否则,线程将尝试为其堆栈使用相同的内存区域,随后将出现混乱。

    c
    intpthread_attr_setstack(pthread_attr_t *attr,
                              void *stackaddr, size_t stacksize);
    // 或者由这2个函数分别设置
    intpthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
    intpthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
    
    
    
    intpthread_attr_getstack(constpthread_attr_t *attr,
                              void **stackaddr, size_t *stacksize);
    intpthread_attr_getstackaddr(constpthread_attr_t *attr, void **stackaddr);
    intpthread_attr_getstacksize(constpthread_attr_t *attr, size_t *stacksize);
    
    intpthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
    intpthread_attr_getguardsize(constpthread_attr_t *attr, size_t *guardsize);
    

    当应用程序使用pthread_attr_setstack()时,它将接管分配堆栈的责任;此时,pthread_attr_setguardsize()设置的任何保护大小值都将被忽略。如果认为有必要,应用程序有责任分配一个保护区(防止读写的一个或多个页面)来处理堆栈溢出的可能性。

    stackaddr中指定的地址应该适当对齐:为了完全可移植,请在页面边界(sysconf(_SC_PAGESIZE))上对齐它。posix_memalign()可用于分配。

    stacksize也应该是系统页面大小的倍数。

    关于大小

    默认情况下线程保留1M的,而且会在堆栈的顶增加一个空闲的内存页,当访问该内存页的时候就会触发SIGSEGV信号,如果开发者设置了stack size那么就需要用户制定这个多余的内存页并且通过mprotect函数设置保护标志,而且它必须设置调用pthread_attr_setdetachstate 且设置PTHREAD_CREATE_JOINABLE模式,因为只有其他线程调用pthread_join后分配的资源才会被释放, 线程的堆栈的分配必须大于一个最小值PTHREAD_STACK_MIN() 。当分配内的时候会设置MAP_NORESERVE标志(mmap),这个标志表示不预留交换空间,当对该内存进行写的时候,如果系统不能分配到交换空间,那么就会触发SIGSEGV信号,如果可以分配到交换空间,那么就会把private page复制到交换空间。如果mmap没有指定MAP_NORESERVE,在分配空间的时候就会保留和映射区域相同大小的交换空间(这个其实就是资源的滞后分配原则)

    关于地址

    如果线程地址为NULL,那么pthread分配指定的内存(1M)或者是指定的堆栈大小,如果设定了堆栈的地址那么内存的分配必须由开发者设定,例如:

    c
    stackbase = (void *) malloc(size);
    ret = pthread_attr_setstacksize(&tattr, size);
    ret = pthread_attr_setstackaddr(&tattr, stackbase);
    ret = pthread_create(&tid, &tattr, func, arg);
    

    affinity 设置线程亲和性

    线程的亲和性

    CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。

    亲和性分为软亲和性与硬亲和性2种:

    • 软亲和性 :进程并不会在处理器之间频繁迁移
    • 硬亲和性:进程需要在您指定的处理器上运行

    CPU的数量与表示

    在有n个CPU的Linux上,CPU是用0...n-1来进行一一标识的。

    CPU的数量可以通过proc文件系统下的CPU相关文件得到,如cpuinfo和stat:

    bash
    cat /proc/stat | grep "^cpu[0-9]+" | wc -l
    

    在系统编程中,可以直接调用库调用sysconf获得:sysconf(_SC_NPROCESSORS_ONLN);

    c
    #define  _GNU_SOURCE
    
    intpthread_attr_setaffinity_np(pthread_attr_t *attr,
                                    size_t cpusetsize, constcpu_set_t *cpuset);
    intpthread_attr_getaffinity_np(constpthread_attr_t *attr,
                                    size_t cpusetsize, cpu_set_t *cpuset);
    
    /* 运行时设置 */
    intpthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
                               constcpu_set_t *cpuset);
    intpthread_getaffinity_np(pthread_t thread, size_t cpusetsize,
                               cpu_set_t *cpuset);
    
    /*
    	cpu_set_t:可以理解为一个CPU的集合,通过约定好的宏进行清除,设置以及判断
    */
    
    voidCPU_ZERO(cpu_set_t  *set); //(初始化操作)
    voidCPU_SET(int cpu,cpu_set_t *set)//(将某个cpu加进cpu集里)voidCPU_CLR(int cpu,cpu_set_t *set);//(将某个cpu清除出cpu集里)
    voidCPU_ISSET(int cpu,constcpu_set_t *set);// (判断某个cpu是不是在cpu集里)
    

    在 Linux 内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是 cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。

    如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态。

    例程

    c
     
    #define _GNU_SOURCE
    #include<stdio.h>
    #include<math.h>
    #include<pthread.h>
    cpu_set_t cpuset,cpuget;
    doublewaste_time(long n){
        double res = 0;
        long i = 0;
        while (i <n * 200000000) {
            i++;
            res += sqrt(i);
        }
        return res;
    }
    
    void *thread_func(void *param){   
        CPU_ZERO(&cpuset);
        CPU_SET(0, &cpuset); /* cpu 0 is in cpuset now */
        /* bind process to processor 0 */
        if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) !=0) {
            perror("pthread_setaffinity_np");
        }  
        printf("Core 0 is running!
    ");
        /* waste some time so the work is visible with "top" */
        printf("result: %f
    ", waste_time(5));
        pthread_exit(NULL);
    }
    
    
    intmain(int argc, char *argv[]){ 
        pthread_t my_thread;
        time_t startwtime, endwtime;
        startwtime = time (NULL); 
        if (pthread_create(&my_thread, NULL, thread_func,NULL) != 0) {
            perror("pthread_create");
        }
        pthread_join(my_thread,NULL);
        endwtime = time (NULL);
        printf ("wall clock time = %d
    ", (endwtime - startwtime));
        return 0;
    }

    背景

    上一讲我们介绍了线程的创建,回收与销毁;简单地提到了线程属性。这一讲我们就来具体看看,线程的属性。

    概述

    c
    #include<pthread.h>
    
    typedef struct __pthread_attr_s
    {
        int                       __detachstate;   // 线程的分离状态
        int                       __schedpolicy;   // 线程调度策略
        structsched_param         __schedparam;    // 线程的调度参数
        int                       __inheritsched;  // 线程的继承性
        int                       __scope;         // 线程的作用域
        size_t                    __guardsize;     // 线程栈末尾的警戒缓冲区大小
        int                       __stackaddr_set; // 线程的栈设置
        void*                     __stackaddr;     // 线程栈的位置
        size_t                    __stacksize;     // 线程栈的大小
    } pthread_attr_t;
    
    intpthread_attr_init(pthread_attr_t *attr);
    intpthread_attr_destroy(pthread_attr_t *attr);
    

    线程具有属性,用pthread_attr_t表示,在对该结构进行处理之前必须进行初始化(pthread_attr_init),在使用后需要对其去除初始化(pthread_attr_destroy)。

    初始化为默认属性

    c
    intpthread_attr_init(pthread_attr_t *attr);
    

    描述:初始化一个线程属性对象,重置为当前系统支持线程的所有属性的默认值。

    属性
    __scope PTHREAD_SCOPE_PROCESS
    __tetachstate PTHREAD_CREATE_JOINABL
    __stackaddr NULL
    __stacksize 1M
    __sched_param.priority 0
    __inheritsched PTHREAD_INHERIT_SCHED
    __schedpolicy SCHED_OTHER

    反初始化

    c
    intpthread_attr_destroy(pthread_attr_t *attr);
    

    描述:销毁一个线程属性对象,使它在重新初始化之前不能重新使用。

    原理:用无效的值设置了属性对象。

    因此:如果经pthread_attr_destroy去除初始化之后的pthread_attr_t结构被pthread_create函数调用,将会导致其返回错误。

    detachstate 分离状态

    我们来分析结构体中的有关成员。

    c
    intpthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate);
    
    // 有set就有get 。
    intpthread_attr_getdetachstate(constpthread_attr_t* attr, int *detachstate)

    描述:设置线程是否和其他线程分离(能否调用pthread_join()回收), 运行时可以调用pthread_detach()完成。

    c
    intpthread_detach(pthread_t thread);
    

    参数解析:

    attr:设置的属性对象

    detachstate :分离状态

    • PTHREAD_CREATE_JOINABLE(默认):线程的资源在退出后自行释放。
    • PTHREAD_CREATE_DETACHED:

    设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置) 则不能再恢复到PTHREAD_CREATE_JOINABLE状态。

    返回值:成功返回0,失败返回错误号。

    schedpolicy 调度策略与优先级

    调度策略

    如果主线程是唯一的线程,那么基本上不会被调度出去。另一方面,如果可运行的线程数大于CPU的数量,那么操作系统最终会将某个正在运行的线程调度出去,从而使其他线程能够使用CPU。这将导致一次上下文切换。在这个过程中将保存当前运行线程的执行上下文,并将新调度进来的线程的执行上下文设置为当前上下文。

    Linux内核的三种调度策略:

    • SCHED_OTHER 分时调度策略,默认的调度策略
    • SCHED_FIFO 实时调度策略,先到先服务。一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃
    • SCHED_RR 实时调度策略,时间片轮转。当进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平

    继承创建者的调度策略

    只有在不继承时,下面的操作才是有效的。

    c
    intpthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
    intpthread_attr_getinheritsched(constpthread_attr_t *attr, int *inheritsched);
    

    描述:设置线程是否继承创建者优先级属性

    参数解析:

    inheritsched : 是否继承

    • PTHREAD_INHERIT_SCHED 继承
    • PTHREAD_EXPLICIT_SCHED 不继承

    获取可设置的优先级

    c
    intsched_get_priority_max(int policy);
    intsched_get_priority_min(int policy);
    

    描述:获取本线程的最大/小优先级。

    返回值:成功时返回最大/小值,失败返回-1。

    设置线程的调度策略与优先级

    c
    intpthread_setschedparam(pthread_t thread, int policy,
                              const struct sched_param *param);
    intpthread_getschedparam(pthread_t thread, int *policy,
                              struct sched_param *param);
    /* 用到的结构体 */
    structsched_param {
        int sched_priority;     /* Scheduling priority */
    };
    

    描述:设置线程的调度策略, 运行时可以调用pthread_setschedparam()来改变。

    参数解析:

    policy:

    • (默认)SCHED_OTHER(正常、非实时)
    • SCHED_RR(实时、轮转法)
    • SCHED_FIFO(实时、先入先出)

    param:优先级(越大越高)

    优先级与调度的例程

    c
    #include<pthread.h>
    #include<stdio.h>
    #include<unistd.h>
    
    void *task0(void *arg){
        while(1)
        {
            //sleep(1);
            printf("task0.
    ");
        }
    }
    
    void *task1(void *arg){
        while(1)
        {
            sleep(1);
            printf("task1.
    ");
        }
    }
    
    intmain(void){
        pthread_attr_t attr;		
        structsched_paramparm;
        pthread_t tid0, tid1;
        void* retval;
    
        pthread_attr_init(&attr);
    
        pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); //不继承创建者的调度策略,而是设置以下的调度
        pthread_attr_setschedpolicy(&attr, SCHED_FIFO); //为线程属性设置调度策略
    
        parm.sched_priority = 1;					// 设置线程优先级
        pthread_attr_setschedparam(&attr,&parm);	// 设置线程优先级
    
        pthread_create(&tid0, &attr, task0, NULL);  // 为线程task0设置优先级
        pthread_create(&tid1, NULL , task1, NULL);  // 让线程task1使用默认优先级
    
    
        while(1);
    
        // 等待线程的结束(实际上由于线程一直在循环中,所以main函数不会结束)
        pthread_join(tid0, &retval); // 等待线程的结束,并取返回值
        pthread_attr_destroy(&attr);
        pthread_join(tid1, &retval); // 等待线程的结束,并取返回值
    
    }
    

    设置线程的竞争作用域

    线程的竞争作用域:表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。

    c
    int pthread_attr_setscope(pthread_attr_t*attr,int scope);
    int pthread_attr_getscope(const pthread_attr_t*attr,int*scope);
    

    描述:设置线程的竞争范围。

    使用前,需要将pthread_attr_setinheritsched设置为PTHREAD_EXPLICIT_SCHED

    参数解析:

    scope:

    • PTHREAD_SCOPE_SYSTEM :与系统中所有线程一起竞争CPU时间
    • PTHREAD_SCOPE_PROCESS:仅与同进程中的线程竞争CPU。

    根据man pthread_attr_setscope的结果来看目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。

    Linux supports PTHREAD_SCOPE_SYSTEM, but not PTHREAD_SCOPE_PROCESS

    设置堆栈

    线程可以设置堆栈地址(stackaddr)与大小(stacksize)。对于大部分程序是应该避免使用的。

    如果使用attr创建多个线程,则调用方必须在对pthread_create()的调用之间更改堆栈地址属性;否则,线程将尝试为其堆栈使用相同的内存区域,随后将出现混乱。

    c
    intpthread_attr_setstack(pthread_attr_t *attr,
                              void *stackaddr, size_t stacksize);
    // 或者由这2个函数分别设置
    intpthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
    intpthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
    
    
    
    intpthread_attr_getstack(constpthread_attr_t *attr,
                              void **stackaddr, size_t *stacksize);
    intpthread_attr_getstackaddr(constpthread_attr_t *attr, void **stackaddr);
    intpthread_attr_getstacksize(constpthread_attr_t *attr, size_t *stacksize);
    
    intpthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
    intpthread_attr_getguardsize(constpthread_attr_t *attr, size_t *guardsize);
    

    当应用程序使用pthread_attr_setstack()时,它将接管分配堆栈的责任;此时,pthread_attr_setguardsize()设置的任何保护大小值都将被忽略。如果认为有必要,应用程序有责任分配一个保护区(防止读写的一个或多个页面)来处理堆栈溢出的可能性。

    stackaddr中指定的地址应该适当对齐:为了完全可移植,请在页面边界(sysconf(_SC_PAGESIZE))上对齐它。posix_memalign()可用于分配。

    stacksize也应该是系统页面大小的倍数。

    关于大小

    默认情况下线程保留1M的,而且会在堆栈的顶增加一个空闲的内存页,当访问该内存页的时候就会触发SIGSEGV信号,如果开发者设置了stack size那么就需要用户制定这个多余的内存页并且通过mprotect函数设置保护标志,而且它必须设置调用pthread_attr_setdetachstate 且设置PTHREAD_CREATE_JOINABLE模式,因为只有其他线程调用pthread_join后分配的资源才会被释放, 线程的堆栈的分配必须大于一个最小值PTHREAD_STACK_MIN() 。当分配内的时候会设置MAP_NORESERVE标志(mmap),这个标志表示不预留交换空间,当对该内存进行写的时候,如果系统不能分配到交换空间,那么就会触发SIGSEGV信号,如果可以分配到交换空间,那么就会把private page复制到交换空间。如果mmap没有指定MAP_NORESERVE,在分配空间的时候就会保留和映射区域相同大小的交换空间(这个其实就是资源的滞后分配原则)

    关于地址

    如果线程地址为NULL,那么pthread分配指定的内存(1M)或者是指定的堆栈大小,如果设定了堆栈的地址那么内存的分配必须由开发者设定,例如:

    c
    stackbase = (void *) malloc(size);
    ret = pthread_attr_setstacksize(&tattr, size);
    ret = pthread_attr_setstackaddr(&tattr, stackbase);
    ret = pthread_create(&tid, &tattr, func, arg);
    

    affinity 设置线程亲和性

    线程的亲和性

    CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。

    亲和性分为软亲和性与硬亲和性2种:

    • 软亲和性 :进程并不会在处理器之间频繁迁移
    • 硬亲和性:进程需要在您指定的处理器上运行

    CPU的数量与表示

    在有n个CPU的Linux上,CPU是用0...n-1来进行一一标识的。

    CPU的数量可以通过proc文件系统下的CPU相关文件得到,如cpuinfo和stat:

    bash
    cat /proc/stat | grep "^cpu[0-9]+" | wc -l
    

    在系统编程中,可以直接调用库调用sysconf获得:sysconf(_SC_NPROCESSORS_ONLN);

    c
    #define  _GNU_SOURCE
    
    intpthread_attr_setaffinity_np(pthread_attr_t *attr,
                                    size_t cpusetsize, constcpu_set_t *cpuset);
    intpthread_attr_getaffinity_np(constpthread_attr_t *attr,
                                    size_t cpusetsize, cpu_set_t *cpuset);
    
    /* 运行时设置 */
    intpthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
                               constcpu_set_t *cpuset);
    intpthread_getaffinity_np(pthread_t thread, size_t cpusetsize,
                               cpu_set_t *cpuset);
    
    /*
    	cpu_set_t:可以理解为一个CPU的集合,通过约定好的宏进行清除,设置以及判断
    */
    
    voidCPU_ZERO(cpu_set_t  *set); //(初始化操作)
    voidCPU_SET(int cpu,cpu_set_t *set)//(将某个cpu加进cpu集里)voidCPU_CLR(int cpu,cpu_set_t *set);//(将某个cpu清除出cpu集里)
    voidCPU_ISSET(int cpu,constcpu_set_t *set);// (判断某个cpu是不是在cpu集里)
    

    在 Linux 内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是 cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。

    如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态。

    例程

    c
     
    #define _GNU_SOURCE
    #include<stdio.h>
    #include<math.h>
    #include<pthread.h>
    cpu_set_t cpuset,cpuget;
    doublewaste_time(long n){
        double res = 0;
        long i = 0;
        while (i <n * 200000000) {
            i++;
            res += sqrt(i);
        }
        return res;
    }
    
    void *thread_func(void *param){   
        CPU_ZERO(&cpuset);
        CPU_SET(0, &cpuset); /* cpu 0 is in cpuset now */
        /* bind process to processor 0 */
        if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) !=0) {
            perror("pthread_setaffinity_np");
        }  
        printf("Core 0 is running!
    ");
        /* waste some time so the work is visible with "top" */
        printf("result: %f
    ", waste_time(5));
        pthread_exit(NULL);
    }
    
    
    intmain(int argc, char *argv[]){ 
        pthread_t my_thread;
        time_t startwtime, endwtime;
        startwtime = time (NULL); 
        if (pthread_create(&my_thread, NULL, thread_func,NULL) != 0) {
            perror("pthread_create");
        }
        pthread_join(my_thread,NULL);
        endwtime = time (NULL);
        printf ("wall clock time = %d
    ", (endwtime - startwtime));
        return 0;
    }
  • 相关阅读:
    this指向问题
    原生js实现的金山打字小游戏(实例代码详解)
    js实现点赞效果
    .net core部署到linux可能碰到的问题
    Linux curl命令详解 Web程序
    用十年来学编程
    JAVA的字符串拼接与性能
    PHP学习的技巧和学习的要素总结
    php实现验证邮箱格式的代码实例
    PHP页面中文乱码处理办法
  • 原文地址:https://www.cnblogs.com/wt88/p/15100310.html
Copyright © 2020-2023  润新知