一、 可重入函数
概念:一个函数被多个执行流进入,不会出错,这就叫可重入函数;否则,就叫不可重入函数。
1.如果一个函数只访问自己的局部变量或参数,当有多个执行流执行时,就不会互相影响。
2.首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括 static),这样的函数就是可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。
3.如果确实需要访问全局变量(包括 static),一定要注意实施互斥手段。可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。
1.如何保证函数的可重入性?
1)在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量);
2)对于要使用的全局变量要加以保护(如采取关中断、信号量等互斥方法),这样构成的函数就一定是一个可重入的函数。
2.满足下列条件的函数多数是不可重入(不安全)的:
1)函数体内使用了静态的数据结构;
2)函数体内调用了malloc() 或者 free() 函数;
3)函数体内调用了标准 I/O 函数。
二、volatile限定符
编译器的优化:
硬件级别的优化:
1.由于内存的访问速度远不及CPU的处理速度,所以引入硬件高速缓存Cache,加速对内存的访问;
2.现代CPU的指令并不一定严格按照顺序执行,没有相关性的指令可以乱序执行,以充分利用CPU的指令流水线,提高执行速度。
软件级别的优化:
1.将内存变量缓存到寄存器,调整指令顺序
作用:告诉编译器不要优化它所修饰的变量,会改变编译结果
未被volatile修饰的变量每次操作时:
从内存中读取----->放入寄存器----->CPU计算操作1---->CPU计算操作2…------>写回内存
被volatile修饰的变量每次操作时:
从内存中读取----->放入寄存器----->CPU计算操作------>写回内存---->第二次从内存中读取
也就是说,被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,从而避免出现数据脏读的现象。
volatile可以修饰不返回的函数,这样就不用把它的返回值载压入堆栈了,起到一定的优化效果
三、SIGCHLD信号
1.子进程在退出的时候,会给父进程发送 “SIGCHLD” 信号,该信号的默认处理动作是忽略。
2.虽然系统默认处理动作是忽略,但是如果父进程不 wait 的话,依然会使子进程变为僵尸进程,所以需要我们捕捉该信号,或者显式忽略它。为啥要显式的忽略它呢?(这是一个特例)
3.所以父进程可以自定义“SIGCHLD”信号的处理动作,当子进程退出时,可以调用该默认处理动作;这样父进程就不必阻塞等待了,提高了运行效率。
代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
using namespace std;
void handler(int signo)
{
if(pid_t ret=(wait(NULL)) != -1)
{
cout << "child exit process" << ret <<endl;
}
}
int main()
{
struct sigaction act,oact;
act.sa_handler=handler;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGCHLD,&act,&oact);
pid_t id = fork();
if(id == 0)
{
cout << "i am child run..." << endl;
sleep(3);
_exit(1);
}
else{
while(1)
{
cout << "i am a parent:"<<getpid()<<endl;
sleep(1);
}
}
}