clone函数探究

我们都知道linux中创建新进程是系统调用fork,但实际上fork是clone功能的一部分,clone和fork的主要区别是传递了几个参数。clone隶属于libc,它的意义就是实现线程。

看一下clone函数:

int clone(int (*fn)(void * arg), void *stack, int flags, void * arg);

fn就是即将创建的线程要执行的函数,stack是线程使用的堆栈。

再来看一下clone和pthread_create的区别:linux中的pthread_create最终调用clone。

我们的目的不是为了介绍clone,而是探究clone中的上下文切换问题。

(1)进程切换:把运行的进程的CPU寄存器中的数据取出存放到内核态堆栈中,同时把要载入的进程的数据放入到寄存器中(硬件上下文),还会把所有一切的状态信息进行切换。

(2)时间片轮转的方式使多个任务在同一颗CPU上执行变成了可能,但同时也带来了保存现场和加载现场的直接消耗(上下文切换会带来直接和间接两种因素影响程序性能的消耗。直接消耗包括:CPU寄存器需要保存和加载,系统调度器的代码需要执行,TLB实例需要重新加载,CPU 的pipeline需要刷掉;间接消耗指的是多核的cache之间得共享数据,间接消耗对于程序的影响要看线程工作区操作数据的大小)。

(3)clone任务[1]:

  • Allocate data structures for thread representation
  • Initialize structures according to clone parameters
  • Set up kernel and user stack as well as argument for the thread function
  • Put the thread on the corresponding CPU core’s run queue
  • Notify target core via an interrupt so that the new thread will be scheduled

(4)我们在clone出线程时指定高的优先级,或许会减少因抢占而造成的上下文切花开销。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>

#define N 4
#define M 30000

#define THREAD_NUM      4
#define POLICY          SCHED_RR

int nwait = 0;
volatile long long sum;
long loops = 6e3;
pthread_mutex_t mutex;

void set_affinity(int core_id) {
	cpu_set_t cpuset;
	CPU_ZERO(&cpuset);
	CPU_SET(core_id, &cpuset);
	assert(pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) == 0);
}

void* thread_func(void *arg) {
	//set_affinity((int)(long)arg);
	for (int j = 0; j < M; j++) {
		pthread_mutex_lock(&mutex);
		nwait++;
		for (long i = 0; i < loops; i++) // This is the key of speedup for parrot: the mutex needs to be a little bit congested.
			sum += i;
		pthread_mutex_unlock(&mutex);
		for (long i = 0; i < loops; i++)
			sum += i*i*i*i*i*i;
		//fprintf(stderr, "compute thread %u %d\n", (unsigned)pthread_self(), sched_getcpu());
  }
}

int main() {
    //set_affinity(23);

    pthread_t             threads[THREAD_NUM], id;
    pthread_attr_t        attrs[THREAD_NUM];
    struct sched_param    scheds[THREAD_NUM], sched;
    int                   idxs[THREAD_NUM];
    int                   policy, i, ret;

    id = pthread_self();
    ret = pthread_getschedparam(id, &policy, &sched);
    assert(!ret && "main pthread_getschedparam failed!");
    sched.sched_priority = sched_get_priority_max(POLICY);
    ret = pthread_setschedparam(id, POLICY, &sched); //set policy and corresponding priority
    assert(!ret && "main pthread_setschedparam failed!");

    for (i = 0; i < THREAD_NUM; i++) {
        idxs[i] = i;

        ret = pthread_attr_init(&attrs[i]);
	assert(!ret && "pthread_attr_init failed!");

        ret = pthread_attr_getschedparam(&attrs[i], &scheds[i]);
	assert(!ret && "pthread_attr_getschedparam failed!");

        ret = pthread_attr_setschedpolicy(&attrs[i], POLICY);
	assert(!ret && "pthread_attr_setschedpolicy failed!");

        scheds[i].sched_priority = sched_get_priority_max(POLICY);

        ret = pthread_attr_setschedparam(&attrs[i], &scheds[i]);
	assert(!ret && "pthread_attr_setschedparam failed!");

        ret = pthread_attr_setinheritsched(&attrs[i], PTHREAD_EXPLICIT_SCHED);
	assert(!ret && "pthread_attr_setinheritsched failed!");
    }

    for (i = 0; i < THREAD_NUM; i++) {
        ret = pthread_create(&threads[i], &attrs[i], thread_func, &idxs[i]);
	assert(!ret && "pthread_create() failed!");
    }

    for (i = 0; i < THREAD_NUM; i++)
        ret = pthread_join(threads[i], NULL);

    return 0;
}

我们让四个子线程和主线程都采取RR调度,并设置最高优先级,我们用VTune观察Preemption Context Switches是否会因此减少。

VTune现象:

现在设置最低优先级:

原来设置最低优先级可以减少Preemption Context Switches,但是增加了Synchronization Context Switches。显然最高优先级运行用时少(4.470s,而最低优先级用时7.280s)。

REFERENCES:

[1] Balazs Gero?, etc, Clone n(): Parallel Thread Creation for Upcoming Many-Core Architectures, 2012, IEEE International Conference on Cluster Computing.

clone函数探究,布布扣,bubuko.com

时间: 2024-09-29 11:47:07

clone函数探究的相关文章

关于cocos2dx lua中的clone函数的源码解读

cocos2dx的clone函数,是深拷贝,完全的拷贝,这段代码内容不多,不过第一次看还是有点晕,我把解读记下来分享一下. 源码解读: function clone(object)--clone函数 local lookup_table = {}--新建table用于记录 local function _copy(object)--_copy(object)函数用于实现复制 if type(object) ~= "table" then return object ---如果内容不是t

C++ clone()函数的用法

转自:https://blog.csdn.net/xiangxianghehe/article/details/78793300 原型模式 在C++中,要拷贝一个对象,除了自定义一个拷贝构造函数来实现对象复制外,还可以实现一个clone函数.这需要借助编译器实现的一个隐藏拷贝构造函数,这样的做法,更省心. 背后的原理是C++的原型(Prototype)模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. Prototype模式提供了一个通过已存在对象进行新对象创建的接口(Clo

关于JS的clone()函数编写的一些问题

问题讲述:用js 实现一个clone()克隆函数,该函数会把输入进去的不同类型值Number,String,Undefined,Boolean,Function,Null,Object,Array,RegExp,克隆一份出来 一.解题代码 直接贴代码, function clone(obj){ var copy; switch(typeof obj){ case 'undefined':break; case 'number': case 'string': case 'boolean': ca

由“Qt程序运行一段时间后崩溃”引发的“opancv库中Mat::clone()函数”在多线程下的注意事项

问题描述 过程1:从相机中获取图像数据,然后存放到一个cv::Mat对象中(该对象是全局变量,用来交换数据).由相机的回调函数自动调用. 过程2:将上述的全局变量拷贝并转换qimg,放到Qt界面上显示.该过程由定时器调用. 然后程序会在运行一段时间后,出现"程序异常结束.The process was ended forcefully.".运行的时间长短不一. 问题解决与分析 由于QtCreator的编译器选的是MSVC,而调试器选只有GDB(查了下好像需要CDB).所以无法debug

Linux 开关中断系列函数探究

中断与锁 中断是Linux响应异步事件的一种方式,区别于陷阱.异常指令和出错等在指令执行完后产生的异常,它往往发生在处理器的外部,通常由外设触发,可以在指令执行完成的瞬间产生,也可能在指令执行的过程中产生,因而不可预测.从外设的角度看,中断是设备请求CPU响应的方式:而从调度的视角来看,中断是驱动系统心跳和调度的手段.而为了实现同步互斥机制,内核还基于处理器提供的原子指令实现了锁的机制,在进入临界段时需要上锁,而在退出时需要解锁,这样就保证了其他处理器不会也进入临界区.通过关本地中断,保重了当前

oracle 几个时间函数探究

近来经常用到时间函数,在此写一个笔记,记录自己的所得,希望也对您有所帮助. 1.对于一个时间如 sysdate:2015/1/30 14:16:03如何只得到年月日,同时它的数据类型不变化呢? 最容易想到的是to_char然后在to_date,以前我所能想到的就是这个,因为觉得麻烦在网上搜了一下,找到了另外一种方法,trunc(sysdate),即可得到想要的结果 select sysdate from dual; SYSDATE ----------- 2015/1/30 1 select t

嵌入式C语言自我修养 10:内联函数探究

10.1 属性声明:noinline & always_inline 这一节,接着讲 attribute 属性声明,attribute可以说是 GNU C 最大的特色.我们接下来继续讲一下跟内联函数相关的两个属性:noinline 和 always_inline.这两个属性的用途是告诉编译器:编译时,对我们指定的函数内联展开或不展开.它们的使用方法如下. static inline __attribute__((noinline)) int func(); static inline __att

opencv矩阵赋值函数copyTo、clone、重载元算赋=的比较

opencv矩阵赋值函数copyTo.clone.重载元算赋'='之间实现的功能相似均是给不同的矩阵赋值功能.copyTo和clone函数基本相同,被赋值的矩阵和赋值矩阵之间空间独立,不共享同一空间.但是重载元算赋'=',被赋值的矩阵和赋值矩阵之间空间共享,改变任一个矩阵的值,会同时影响到另一个矩阵.当矩阵作为函数的返回值时其功能和重载元算赋'='相同,赋值运算赋会给矩阵空间增加一次计数,所以函数变量返回后函数内部申请的变量空间并不会被撤销,在主函数中仍可以正常使用传递后的参数.具体的过程见下面

JQ中的clone()方法与DOM中的cloneNode()方法

JQ中的clone()方法与DOM中的cloneNode()方法 cloneNode()定义和用法 cloneNode()方法创建节点的拷贝,并返回该副本. 语法: node.cloneNode(deep);  其接收一个可选参数"deep",为布尔类型,默认是false. 当设置为true,克隆当前节点,属性及当前节点的后代.若设置为false,仅仅克隆当前元素节点本身. 扩展:  使用cloneNode()方法,当被克隆的节点绑定了事件处理程序,事件处理程序是否会被一同克隆,这个我