CPU亲和性的使用与机制--含Xen中VCPU和PCPU的綁定方法

CPU的亲和性, 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,亲和性是从affinity翻译过来的,应该有点不准确,给人的感觉是亲和性就是有倾向的意思,而实际上是倒向的意思,称为CPU关联性更好,程序员的土话就是绑定CPU,绑核。

在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了,当绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,性能有一定的提高。

另外一种使用绑核考虑就是将重要的业务进程隔离开,对于部分实时进程调度优先级高,可以将其绑定到一个指定核上,既可以保证实时进程的调度,也可以避免其他CPU上进程被该实时进程干扰。

1.CPU亲和性在用户态的使用

linux的CPU亲和性在用户态表现为一个cpu_set_t掩码的形式,用户可以调用两个函数设置和获取掩码:

#define _GNU_SOURCE /* See feature_test_macros(7) */
 #include
int sched_setaffinity(pid_t pid, size_t cpusetsize,
 cpu_set_t *mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize,
 cpu_set_t *mask);
 #include
int sched_setaffinity(pid_t pid, size_t cpusetsize,
 cpu_set_t *mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize,
 cpu_set_t *mask);

sched_setaffinity是设置指定pid亲和性掩码的,mask是传入的参数;sched_getaffinity则是获取指定pid亲和性掩码的,mask是获取的参数。

cpusetsize可以通过sizeof cpu_set_t算出来。

cpu_set_t 是一个掩码数组,一共有1024位,每一位都可以对应一个cpu核心,以下宏,都是对这个掩码进行操作的。如果需要,一个进程是可以绑定多个cpu的。

void CPU_ZERO(cpu_set_t *set);
void CPU_SET(int cpu, cpu_set_t *set);
void CPU_CLR(int cpu, cpu_set_t *set); CPU_ZERO(cpu_set_t *set);
void CPU_SET(int cpu, cpu_set_t *set);
void CPU_CLR(int cpu, cpu_set_t *set);

而mask的表现是如此的:如果是0X23,转换成二进制则为00100011,则表明进程绑定在0核、1核和5核上。

绑核需要注意是,子进程会继承父进程的绑核关系。

代码实例:

#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#define gettid() syscall(__NR_gettid)
void *test_thread(void *arg)
{
 cpu_set_t mask;
 int loop = 0;
 int cpu_num = 0;
 cpu_num = sysconf(_SC_NPROCESSORS_CONF);
 pthread_detach(pthread_self());
 CPU_ZERO(&mask);
 CPU_SET(1, &mask);
 if(sched_setaffinity(0, sizeof(mask), &mask) == -1)
 {
 printf("set affinity failedn");
 }
 while(1)
 {
 CPU_ZERO(&mask);
 if(sched_getaffinity(0, sizeof(mask), &mask) == -1)
 {
 printf("get failedn");
 }
 for(loop = 0; loop < cpu_num; loop++)
 {
 if(CPU_ISSET(loop, &mask))
 {
 printf("test thread %lu run on processor %dn",
 gettid(), loop);
 }
 }
 sleep(1);
 }
}
void *child_thread(void *arg)
{
 cpu_set_t mask;
 int loop = 0;
 int cpu_num = 0;
 cpu_num = sysconf(_SC_NPROCESSORS_CONF);
 pthread_detach(pthread_self());
 while(1)
 {
 CPU_ZERO(&mask);
 if(sched_getaffinity(0, sizeof(mask), &mask) == -1)
 {
 printf("get failedn");
 }
 for(loop = 0; loop < cpu_num; loop++)
 {
 if(CPU_ISSET(loop, &mask))
 {
 printf("child thread %lu run on processor %dn",
 gettid(), loop);
 }
 }
 sleep(1);
 }
}
int main(int argc, char *argv[])
{
 int cpu_num = 0;
 pthread_t thread;
 int cpuid = 0;
 int ret = 0;
 int loop = 0;
 cpu_set_t mask_set;
 cpu_set_t mask_get;
 if(argc != 2)
 {
 printf("usage:cpu numn");
 return -1;
 }
 cpuid = atoi(argv[1]);
 /* 获取系统CPU的个数 */
 cpu_num = sysconf(_SC_NPROCESSORS_CONF);
 printf("system has %i processor.n", cpu_num);
 /* 初始化mask_set */
 CPU_ZERO(&mask_set);
 CPU_SET(cpuid, &mask_set);
 if(sched_setaffinity(0, sizeof(mask_set), &mask_set) == -1)
 {
 printf("Warning:set cpu %d affinity failedn", cpuid);
 }
 ret = pthread_create(&thread, NULL, child_thread, NULL);
 if(ret)
 {
 printf("Error:pthread_create failedn");
 return -1;
 }
 ret = pthread_create(&thread, NULL, test_thread, NULL);
 if(ret)
 {
 printf("Error:pthread_create failedn");
 return -1;
 }
 while(1)
 {
 CPU_ZERO(&mask_get);
 if(sched_getaffinity(0, sizeof(mask_get), &mask_get) == -1)
 {
 printf("Warning:get cpu %d affinity failedn", cpuid);
 }
 for(loop = 0; loop < cpu_num; loop++)
 {
 if(CPU_ISSET(loop, &mask_get))
 {
 printf("this processor %lu is running on processor:
 %dn", gettid(), loop);
 }
 }
 sleep(1);
 }
 return 0;
}
#include 
#include 
#include 
#include 
#include 
#include 
#define gettid() syscall(__NR_gettid)
void *test_thread(void *arg)
{
 cpu_set_t mask;
 int loop = 0;
 int cpu_num = 0;
 cpu_num = sysconf(_SC_NPROCESSORS_CONF);
 pthread_detach(pthread_self());
 CPU_ZERO(&mask);
 CPU_SET(1, &mask);
 if(sched_setaffinity(0, sizeof(mask), &mask) == -1)
 {
 printf("set affinity failedn");
 }
 while(1)
 {
 CPU_ZERO(&mask);
 if(sched_getaffinity(0, sizeof(mask), &mask) == -1)
 {
 printf("get failedn");
 }
 for(loop = 0; loop < cpu_num; loop++)
 {
 if(CPU_ISSET(loop, &mask))
 {
 printf("test thread %lu run on processor %dn",
 gettid(), loop);
 }
 }
 sleep(1);
 }
}
void *child_thread(void *arg)
{
 cpu_set_t mask;
 int loop = 0;
 int cpu_num = 0;
 cpu_num = sysconf(_SC_NPROCESSORS_CONF);
 pthread_detach(pthread_self());
 while(1)
 {
 CPU_ZERO(&mask);
 if(sched_getaffinity(0, sizeof(mask), &mask) == -1)
 {
 printf("get failedn");
 }
 for(loop = 0; loop < cpu_num; loop++)
 {
 if(CPU_ISSET(loop, &mask))
 {
 printf("child thread %lu run on processor %dn",
 gettid(), loop);
 }
 }
 sleep(1);
 }
}
int main(int argc, char *argv[])
{
 int cpu_num = 0;
 pthread_t thread;
 int cpuid = 0;
 int ret = 0;
 int loop = 0;
 cpu_set_t mask_set;
 cpu_set_t mask_get;
 if(argc != 2)
 {
 printf("usage:cpu numn");
 return -1;
 }
 cpuid = atoi(argv[1]);
 /* 获取系统CPU的个数 */
 cpu_num = sysconf(_SC_NPROCESSORS_CONF);
 printf("system has %i processor.n", cpu_num);
 /* 初始化mask_set */
 CPU_ZERO(&mask_set);
 CPU_SET(cpuid, &mask_set);
 if(sched_setaffinity(0, sizeof(mask_set), &mask_set) == -1)
 {
 printf("Warning:set cpu %d affinity failedn", cpuid);
 }
 ret = pthread_create(&thread, NULL, child_thread, NULL);
 if(ret)
 {
 printf("Error:pthread_create failedn");
 return -1;
 }
 ret = pthread_create(&thread, NULL, test_thread, NULL);
 if(ret)
 {
 printf("Error:pthread_create failedn");
 return -1;
 }
 while(1)
 {
 CPU_ZERO(&mask_get);
 if(sched_getaffinity(0, sizeof(mask_get), &mask_get) == -1)
 {
 printf("Warning:get cpu %d affinity failedn", cpuid);
 }
 for(loop = 0; loop < cpu_num; loop++)
 {
 if(CPU_ISSET(loop, &mask_get))
 {
 printf("this processor %lu is running on processor:
 %dn", gettid(), loop);
 }
 }
 sleep(1);
 }
 return 0;
}

执行之后根据打印和/proc stat的内容可以判断,status有

Cpus_allowed: 08
Cpus_allowed_list: 3

可以更清楚的看到进程绑核状态

但是如果进程已经在运行过程中,用户不能直接改动代码,就用taskset工具更改CPU亲和性关系。

taskset [options] -p [mask] pid

其中mask前面已说了,参看man手册更详细一点。

二、CPU亲和性在内核态机制

在内核进程结构体task_struct里面有一个参数,即为

cpumask_t cpus_allowed;

用来记住CPU的绑核关系。

内核尤其是调度的时候,可以保证让task不会被调度到其他CPU上

static inline
int select_task_rq(struct task_struct *p, int sd_flags, int wake_flags)
{
 int cpu = p->sched_class->select_task_rq(p, sd_flags, wake_flags);
 /*
 * In order not to call set_task_cpu() on a blocking task we need
 * to rely on ttwu() to place the task on a valid ->cpus_allowed
 * cpu.
 *
 * Since this is common to all placement strategies, this lives here.
 *
 * [ this allows ->select_task() to simply return task_cpu(p) and
 * not worry about this generic constraint ]
 */
 if (unlikely(!cpumask_test_cpu(cpu, &p->cpus_allowed) ||
 !cpu_online(cpu)))
 cpu = select_fallback_rq(task_cpu(p), p);
 return cpu;
}int select_task_rq(struct task_struct *p, int sd_flags, int wake_flags)
{
 int cpu = p->sched_class->select_task_rq(p, sd_flags, wake_flags);
 /*
 * In order not to call set_task_cpu() on a blocking task we need
 * to rely on ttwu() to place the task on a valid ->cpus_allowed
 * cpu.
 *
 * Since this is common to all placement strategies, this lives here.
 *
 * [ this allows ->select_task() to simply return task_cpu(p) and
 * not worry about this generic constraint ]
 */
 if (unlikely(!cpumask_test_cpu(cpu, &p->cpus_allowed) ||
 !cpu_online(cpu)))
 cpu = select_fallback_rq(task_cpu(p), p);
 return cpu;
}

进程在选择CPU队列的时候,只选择被允许的CPU队列,使用cpumask_test_cpu进行测试。

 

 

使用xm vcpu-pin命令可以固定vcpu到物理cpu
 xm vcpu-pin domname vcpu cpu
 vcpu--虚拟cpu的号码(号码可以用cat /proc/cpuinfo命令得到,一般从0~N-1[其中N为CPU核数])
 cpu--主机中cpu的号码

代码研究分析:
/xen-4.0.0/tools/python/xen/xm/main.py
96    SUBCOMMAND_HELP = {
97      # common commands
------------------------------------------
160     'vcpu-pin'    : (' ',
161        'Set which CPUs a VCPU can use.'),
------------------------------------------
261     }

-----------------------------------------
376    domain_commands = [
-----------------------------------------
408        "vcpu-pin",
-----------------------------------------
1455    def xm_vcpu_pin(args):
    arg_check(args, "vcpu-pin", 3)

    def cpu_make_map(cpulist):
        cpus = []
        for c in cpulist.split(','):
            if c.find('-') != -1:
                (x,y) = c.split('-')
                for i in range(int(x),int(y)+1):
                    cpus.append(int(i))
            else:
                # remove this element from the list
                if c[0] == '^':
                    cpus = [x for x in cpus if x != int(c[1:])]
                else:
                    cpus.append(int(c))
        cpus.sort()
        return ",".join(map(str, cpus))

    dom  = args[0]
    vcpu = args[1]
    if args[2] == 'all':
        cpumap = cpu_make_map('0-63')
    else:
        cpumap = cpu_make_map(args[2])

    if serverType == SERVER_XEN_API:
        server.xenapi.VM.add_to_VCPUs_params_live(
            get_single_vm(dom), "cpumap%i" % int(vcpu), cpumap)
    else:
        server.xend.domain.pincpu(dom, vcpu, cpumap)


-------------------------------------------------------------------------------
3445  commands = {

3475    # cpu commands
          "vcpu-pin": xm_vcpu_pin,
           "vcpu-list": xm_vcpu_list,
           "vcpu-set": xm_vcpu_set,

-------------------------------------------------第二层分割线---------------------------------------------

/xen-4.0.0/tools/python/xen/xend/XendDomain.py

1564       def domain_pincpu(self, domid, vcpu, cpumap):
        """Set which cpus vcpu can use

        @param domid: Domain ID or Name
        @type domid: int or string.
        @param vcpu: vcpu to pin to
        @type vcpu: int
        @param cpumap:  string repr of usable cpus
        @type cpumap: string
        @rtype: 0
        """
        dominfo = self.domain_lookup_nr(domid)
        if not dominfo:
            raise XendInvalidDomain(str(domid))

        # if vcpu is keyword 'all', apply the cpumap to all vcpus
        if str(vcpu).lower() == "all":
            vcpus = range(0, int(dominfo.getVCpuCount()))
        else:
            vcpus = [ int(vcpu) ]
      
        # set the same cpumask for all vcpus
        rc = 0
        cpus = dominfo.getCpus()
        cpumap = map(int, cpumap.split(","))
        for v in vcpus:
            try:
                if dominfo._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
                    rc = xc.vcpu_setaffinity(dominfo.getDomid(), v, cpumap)
                cpus[v] = cpumap                     
            except Exception, ex:
                log.exception(ex)
                raise XendError("Cannot pin vcpu: %d to cpu: %s - %s" % \
                                (v, cpumap, str(ex)))
        dominfo.setCpus(cpus)
        self.managed_config_save(dominfo)

        return rc


---------------------------------------第三层分割线--------------------------------------------------

/xen-4.0.0/tools/python/xen/lowlevel/xc/xc.c

 #define PKG "xen.lowlevel.xc"
 #define CLS "xc"


static PyObject *pyxc_vcpu_setaffinity(XcObject *self,
                                       PyObject *args,
                                       PyObject *kwds)
{
    uint32_t dom;
    int vcpu = 0, i;
    uint64_t  *cpumap;
    PyObject *cpulist = NULL;
    int nr_cpus, size;
    xc_physinfo_t info = {0};
    uint64_t cpumap_size = sizeof(*cpumap);

    static char *kwd_list[] = { "domid", "vcpu", "cpumap", NULL };

    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|iO", kwd_list,
                                      &dom, &vcpu, &cpulist) )
        return NULL;

    if ( xc_physinfo(self->xc_handle, &info) != 0 )
        return pyxc_error_to_exception();
 
    nr_cpus = info.nr_cpus;

    size = (nr_cpus + cpumap_size * 8 - 1)/ (cpumap_size * 8);
    cpumap = malloc(cpumap_size * size);
    if(cpumap == NULL)
        return pyxc_error_to_exception();

    if ( (cpulist != NULL) && PyList_Check(cpulist) )
    {
        for ( i = 0; i < size; i++)
        {
            cpumap[i] = 0ULL;
        }
        for ( i = 0; i < PyList_Size(cpulist); i++ )
        {
            long cpu = PyInt_AsLong(PyList_GetItem(cpulist, i));
            cpumap[cpu / (cpumap_size * 8)] |= (uint64_t)1 << (cpu % (cpumap_size * 8));
        }
    }
 
    if ( xc_vcpu_setaffinity(self->xc_handle, dom, vcpu, cpumap, size * cpumap_size) != 0 )
    {
        free(cpumap);
        return pyxc_error_to_exception();
    }
    Py_INCREF(zero);
    free(cpumap);
    return zero;
}


---------------------------------------------------------------------------------
1755    static PyMethodDef pyxc_methods[] = {
-------------------------------------------------------
1829    "vcpu_setaffinity",
      (PyCFunction)pyxc_vcpu_setaffinity,
      METH_VARARGS | METH_KEYWORDS, "\n"
      "Pin a VCPU to a specified set CPUs.\n"
      " dom [int]:     Identifier of domain to which VCPU belongs.\n"
      " vcpu [int, 0]: VCPU being pinned.\n"
      " cpumap [list, []]: list of usable CPUs.\n\n"
      "Returns: [int] 0 on success; -1 on error.\n" },
--------------------------------------------------------
2268  };

--------------------------------------------------------------------------------------

/xen-4.0.0/tools/libxc/xc_domain.c

int xc_vcpu_setaffinity(int xc_handle,
                        uint32_t domid,
                        int vcpu,
                        uint64_t cpumap)
{
    DECLARE_DOMCTL;
    int ret = -1;
    uint8_t local[sizeof (cpumap)];

    domctl.cmd = XEN_DOMCTL_setvcpuaffinity;
    domctl.domain = (domid_t)domid;
    domctl.u.vcpuaffinity.vcpu    = vcpu;

    bitmap_64_to_byte(local, &cpumap, sizeof(cpumap) * 8);

    set_xen_guest_handle(domctl.u.vcpuaffinity.cpumap.bitmap, local);

    domctl.u.vcpuaffinity.cpumap.nr_cpus = sizeof(cpumap) * 8;
   
    if ( lock_pages(local, sizeof(local)) != 0 )
    {
        PERROR("Could not lock memory for Xen hypercall");
        goto out;
    }

    ret = do_domctl(xc_handle, &domctl);

    unlock_pages(local, sizeof(local));

 out:
    return ret;
}

 

最近的工作中对性能的要求比较高,下面简单做一下总结:

一、什么是cpu亲和性(affinity)

  CPU的亲和性, 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,也称为CPU关联性;再简单的点的描述就将制定的进程或线程绑定到相应的cpu上;在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了,当绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,性能有一定的提高。

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

  硬亲和性(affinity):简单来说就是利用linux内核提供给用户的API,强行将进程或者线程绑定到某一个指定的cpu核运行。

  解释:在linux内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是 cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。 如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态;(这部分内容在这个博客中有提到一点:http://www.cnblogs.com/wenqiang/p/4802619.html)

   cpus_allowed用于控制进程可以在哪里处理器上运行

  • sched_set_affinity() (用来修改位掩码)
  • sched_get_affinity() (用来查看当前的位掩码)

二、进程与cpu的绑定

   sched_setaffinity可以将某个进程绑定到一个特定的CPU。你比操作系统更了解自己的程序,为了避免调度器愚蠢的调度你的程序,或是为了在多线程程序中避免缓存失效造成的开销,你可能会希望这样做

  在进行进程与cpu的绑定前,我们先了解编写程序需要准备的知识点

复制代码

 1 SCHED_SETAFFINITY(2)                                                                      Linux Programmer's Manual                                                                     SCHED_SETAFFINITY(2)
 2 
 3 NAME
 4        sched_setaffinity, sched_getaffinity - set and get a process's CPU affinity mask
 5 
 6 SYNOPSIS
 7        #define _GNU_SOURCE             /* See feature_test_macros(7) */
 8        #include 
 9 
10        int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
11        /*该函数设置进程为pid的这个进程,让它运行在mask所设定的CPU上.如果pid的值为0,
12         *则表示指定的是当前进程,使当前进程运行在mask所设定的那些CPU上.
13         *第二个参数cpusetsize是mask所指定的数的长度.通常设定为sizeof(cpu_set_t).
14         *如果当前pid所指定的进程此时没有运行在mask所指定的任意一个CPU上,
15         *则该指定的进程会从其它CPU上迁移到mask的指定的一个CPU上运行.*/
16 
17        int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
18        /*该函数获得pid所指示的进程的CPU位掩码,并将该掩码返回到mask所指向的结构中.
19         *即获得指定pid当前可以运行在哪些CPU上.
20         *同样,如果pid的值为0.也表示的是当前进程*/
21 
22 RETURN VALUE
23        On success, sched_setaffinity() and sched_getaffinity() return 0.  On error, -1 is returned, and errno is set appropriately.

复制代码

 设置cpu affinity还需要用到一下宏函数

复制代码

1 void CPU_ZERO (cpu_set_t *set)
2 /*这个宏对 CPU 集 set 进行初始化,将其设置为空集。*/
3 void CPU_SET (int cpu, cpu_set_t *set)
4 /*这个宏将 指定的 cpu 加入 CPU 集 set 中*/
5 void CPU_CLR (int cpu, cpu_set_t *set)
6 /*这个宏将 指定的 cpu 从 CPU 集 set 中删除。*/
7 int CPU_ISSET (int cpu, const cpu_set_t *set)
8 /*如果 cpu 是 CPU 集 set 的一员,这个宏就返回一个非零值(true),否则就返回零(false)。*/

复制代码

下面下一个具体的例子:将当前进程绑定到0、1、2、3号cpu上

复制代码

 1 #define _GNU_SOURCE
 2 #include 
 3 #include 
 4 #include 
 5 #include 
 6 #include 
 7 #include 
 8 
 9 /* sysconf( _SC_NPROCESSORS_CONF ) 查看cpu的个数;打印用%ld长整。
10  * sysconf( _SC_NPROCESSORS_ONLN ) 查看在使用的cpu个数;打印用%ld长整 */
11 int main(int argc, char **argv)
12 {
13     int cpus = 0;
14     int  i = 0;
15     cpu_set_t mask;
16     cpu_set_t get;
17 
18     cpus = sysconf(_SC_NPROCESSORS_CONF);
19     printf("cpus: %d\n", cpus);
20 
21     CPU_ZERO(&mask);    /* 初始化set集,将set置为空*/
22     CPU_SET(0, &mask);  /* 依次将0、1、2、3号cpu加入到集合,前提是你的机器是多核处理器*/
23     CPU_SET(1, &mask);
24     CPU_SET(2, &mask);
25     CPU_SET(3, &mask);
26     
27     /*设置cpu 亲和性(affinity)*/
28     if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
29         printf("Set CPU affinity failue, ERROR:%s\n", strerror(errno));
30         return -1; 
31     }   
32     usleep(1000); /* 让当前的设置有足够时间生效*/
33 
34     /*查看当前进程的cpu 亲和性*/
35     CPU_ZERO(&get);
36     if (sched_getaffinity(0, sizeof(get), &get) == -1) {
37         printf("get CPU affinity failue, ERROR:%s\n", strerror(errno));
38         return -1; 
39     }   
40     
41     /*查看运行在当前进程的cpu*/
42     for(i = 0; i < cpus; i++) {
43 
44         if (CPU_ISSET(i, &get)) { /*查看cpu i 是否在get 集合当中*/
45             printf("this process %d of running processor: %d\n", getpid(), i); 
46         }    
47     }
48     sleep(3); //让程序停在这儿,方便top命令查看
49        
50     return 0;
51 }

复制代码

运行结果如下:

1 [root@localhost test]# ./test    
2 cpus: 24
3 this process 2848 of running processor: 0
4 this process 2848 of running processor: 1
5 this process 2848 of running processor: 2
6 this process 2848 of running processor: 3

上面代码当中用到了syscall这个函数,顺便也在这里做一下说明

  syscall是执行一个系统调用,根据指定的参数number和所有系统调用的接口来确定调用哪个系统调用,用于用户空间跟内核之间的数据交换

  下面是syscall函数原型及一些常用的number 

复制代码

 1 //syscall - indirect system call
 2 SYNOPSIS
 3        #define _GNU_SOURCE         /* See feature_test_macros(7) */
 4        #include 
 5        #include    /* For SYS_xxx definitions */
 6 
 7        int syscall(int number, ...);
 8        
 9 /* sysconf( _SC_PAGESIZE );  此宏查看缓存内存页面的大小;打印用%ld长整型。
10  sysconf( _SC_PHYS_PAGES ) 此宏查看内存的总页数;打印用%ld长整型。
11  sysconf( _SC_AVPHYS_PAGES ) 此宏查看可以利用的总页数;打印用%ld长整型。
12  sysconf( _SC_NPROCESSORS_CONF ) 查看cpu的个数;打印用%ld长整。
13  sysconf( _SC_NPROCESSORS_ONLN ) 查看在使用的cpu个数;打印用%ld长整。
14  (long long)sysconf(_SC_PAGESIZE) * (long long)sysconf(_SC_PHYS_PAGES) 计算内存大小。
15  sysconf( _SC_LOGIN_NAME_MAX ) 查看最大登录名长度;打印用%ld长整。
16  sysconf( _SC_HOST_NAME_MAX ) 查看最大主机长度;打印用%ld长整。
17  sysconf( _SC_OPEN_MAX )  每个进程运行时打开的文件数目;打印用%ld长整。
18  sysconf(_SC_CLK_TCK) 查看每秒中跑过的运算速率;打印用%ld长整。*/

复制代码

三、线程与cpu的绑定

线程于进程的绑定方法大体一致,需要注意的是线程绑定于进程的区别是所用函数不一样

线程绑定用到下面两个函数,跟进程类似就不做详细说明,下面直接贴出函数原型:

复制代码

 1 NAME
 2        pthread_setaffinity_np, pthread_getaffinity_np - set/get CPU affinity of a thread
 3 
 4 SYNOPSIS
 5        #define _GNU_SOURCE             /* See feature_test_macros(7) */
 6        #include 
 7 
 8        int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
 9                                   const cpu_set_t *cpuset);
10        int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize,
11                                   cpu_set_t *cpuset);
12 
13        Compile and link with -pthread.
14 
15 DESCRIPTION
16        The  pthread_setaffinity_np()  function  sets the CPU affinity mask of the thread thread to the CPU set pointed to by cpuset.  If the call is successful, and the thread is not
17        currently running on one of the CPUs in cpuset, then it is migrated to one of those CPUs.
18 
19        The pthread_getaffinity_np() function returns the CPU affinity mask of the thread thread in the buffer pointed to by cpuset.
20 
21        For more details on CPU affinity masks, see sched_setaffinity(2).  For a description of a set of macros that can be used to manipulate and inspect CPU sets, see CPU_SET(3).
22 
23        The argument cpusetsize is the length (in bytes) of the buffer pointed to by cpuset.  Typically, this argument would be specified as sizeof(cpu_set_t).  (It may be some  other
24        value, if using the macros described in CPU_SET(3) for dynamically allocating a CPU set.)
25 
26 RETURN VALUE
27        On success, these functions return 0; on error, they return a nonzero error number

复制代码

 

下面同样是个具体的例子:将当前线程绑定到0、1、2、3号cpu上

复制代码

 1 #define _GNU_SOURCE
 2 #include 
 3 #include 
 4 #include 
 5 #include 
 6 #include 
 7 #include 
 8 
 9 void *testfunc(void *arg)
10 {
11     int i, cpus = 0;
12     cpu_set_t mask;
13     cpu_set_t get;
14 
15     cpus = sysconf(_SC_NPROCESSORS_CONF);
16     printf("this system has %d processor(s)\n", cpus);
17     
18     CPU_ZERO(&mask);
19     for (i = 0; i < 4; i++) { /*将0、1、2、3添加到集合中*/
20         CPU_SET(i, &mask);
21     }   
22 
23     /* 设置cpu 亲和性(affinity)*/
24     if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {
25         fprintf(stderr, "set thread affinity failed\n");
26     }   
27     
28     /* 查看cpu 亲和性(affinity)*/
29     CPU_ZERO(&get);
30     if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) {
31         fprintf(stderr, "get thread affinity failed\n");
32     }   
33 
34     /* 查看当前线程所运行的所有cpu*/
35     for (i = 0; i < cpus; i++) {
36         if (CPU_ISSET(i, &get)) {
37             printf("this thread %d is running in processor %d\n", (int)pthread_self(), i); 
38         }   
39     }   
40     sleep(3); //查看
41     
42     pthread_exit(NULL);
43 }
44  
45 int main(int argc, char *argv[])
46 {
47     pthread_t tid;
48     if (pthread_create(&tid, NULL, (void *)testfunc, NULL) != 0) {
49         fprintf(stderr, "thread create failed\n");
50         return -1; 
51     }   
52 
53     pthread_join(tid, NULL);
54     return 0;
55 }

复制代码

运行结果如下:

1 [root@localhost thread]# ./test                      
2 this system has 24 processor(s)
3 this thread 2812323584 is running in processor 0
4 this thread 2812323584 is running in processor 1
5 this thread 2812323584 is running in processor 2
6 this thread 2812323584 is running in processor 3

你可能感兴趣的:(linux编程基础)