2017-04-16 21:31:50 qinwunan 阅读数 253

linux管理之系统延迟及定时机制


计划定期任务
 at 命令可以指定某一任务在将来的特定时间运行。该作业可能是一次备份、对您系统的检查或者特定时间发送的通知。那些需要花费很长时间才可完成的作业正适合 at 命 令。仅需使用 at 命令将任务设置为在一分钟或两分钟之后运行。然后您便可以安全注销 , 因为该任务会在与 shell 会话断开连接的情况下运行,at 命令必须指定任务应运行的时间。该指定可以是具体时间和 / 或日期  。也可以是当前时间的相对时间。在键入 at 命令行之后 , 按 Enter 并继续键入作业中包含的其他命令。任务可由多个命令组成。在完成键入要运行的命令时 , 在单独出现的一行中按 Ctrl+d 完成任务


在20:38执行touch命令,并可以用

at   -l    查询at延迟任务

atrm  #  删除at延迟任务,其中#为at的任务号


在相对于现在一分钟之后执行命令touch。

如果是输出命令会将信息发到用户的邮箱里


可以限制用户使用at,分别在/etc/at.allow和/etc/at.deny。


注 :

在at.allow中的用户可以使用at命令,在at.deny中的用户不可以使用at命令。当系统中同时有两个文件时at.deny的文件将不被系统读取。


周期任务

crond是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond进程,crond进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。Linux下的任务调度分为两类,系统任务调度和用户任务调度,系统任务调度:系统周期性所要执行的工作,比如写缓存数据到硬盘、日志清理等。在/etc目录下有一个crontab文件,这个就是系统任务调度的配置文件。

• cron 设备管理必须按计划定期重复运行的程序。后台程序crond 每分钟唤醒一次 , 以运行计划的任何任务用户使用crontab 命令计划个人任务。系统管理员可以在系统范围配置文件中设置任务。

基本格式:

• 1* 2* 3* 4* 5* command
– 1*---------- 分钟
– 2*---------- 小时
– 3*---------- 天
– 4*---------- 月

– 5*---------- 周



• 属于系统的任务通常保存在 /etc/crontab 文件中而不是使用 crontab -e 编辑保存在个人文件中。
• 对于需要每天、每周或每月运行一次的脚本 , 可以直接将其设置为可执行文件 , 并保存在 /etc/cron.{daily,weekly,monthly} 中相应目录下。这些任务由/etc/anacrontab 配置的系统 anacron 服务运行 anacron是 cron 的集成组件 , 用于更好地管理作业 , 并确保在作业因计算机关闭而未运行的情况下 , 在系统引导后运行作业

• 在系统中默认所有用户可以设定 cron
• 用户黑名单
– /etc/cron.deny
• 用户白名单

– /etc/cron.allow



2016-07-27 10:31:51 u010487568 阅读数 4805

Linux内核时钟中断设施

linux的时钟中断需要两个全局变量,分别是xtime与jiffies。

1、xtime

一个timeval结构类型变量,是从cmos电路中取得的时间,一般是从某一历史时刻开始到现在的时间,也就是为了取得我们操作系统上显示的日期。这个就是“实时时钟”,它的精确度是微秒。获取方式是通过sys/time.h头文件里面的gettimeofday函数获取。

2、HZ

Linux核心每隔固定周期会发出timer interrupt (IRQ 0),HZ是用来保存每一秒有几次timer interrupts。如HZ为1000,代表每秒有1000次timer interrupts。 HZ可在编译核心时设定,可设定100、250、300或1000。核心版本预设值为250。
HZ这个值可以理解为操作系统的时钟频率,这个时钟的精度远低于硬件的时钟频率。如HZ设置为250,那么为一秒钟250次,每次为4ms,因此操作系统的时钟精度只能达到4ms。

3、Tick

Tick是HZ的倒数,意即timer interrupt每发生一次中断的时间。如HZ为250时,tick为4毫秒(millisecond)。

4、Jiffies

<linux/jiffies.h>,定义了Jiffies(unsigned long),在linux内核中jiffies远比xtime重要。每发生一次timer interrupt,Jiffies变数会被加一。一秒内时钟中断的次数等于HZ,所以jiffies一秒内增加的值也就是HZ。
在 Linux 2.6 中,系统时钟每 1 毫秒中断一次(时钟频率,用 HZ 宏表示,定义为 1000,即每秒中断 1000 次,2.4 中定义为 100,很多应用程序也仍然沿用 100 的时钟频率),这个时间单位称为一个 jiffie。jiffies 与绝对时间之间的转换, 用两个宏来完成两种时间单位的互换:JIFFIES_TO_NS()、NS_TO_JIFFIES()
jiffies是记录着从电脑开机到现在总共的时钟中断次数。在linux内核中jiffies远比xtime重要,那么他取决于系统的频率,单位是Hz。
这是硬件给内核提供一个系统定时器用以计算和管理时间,连续累加一年四个多月后就会溢出(假定HZ=100,1个jiffies等于1/100秒,jiffies可记录的最大秒数为 (2^32 -1)/100=42949672.95秒,约合497天或1.38年),即当取值到达最大值时继续加1,就变为了0。
因此为防止溢出需要使用这些宏函数进行:time_before()、 time_after()、time_after_eq()、time_before_eq()。因为jiffies随时钟滴答变化,不能用编译器优化 它,应取volatile值。
另外,80x86架构定义一个与jiffies相关的变数jiffies_64 ,w为64位,要等到此变数溢位可能要好几百万年。jiffies被对应至jiffies_64最低的32位元。因此,经由jiffies_64可以完全不理会溢位的问题便能取得jiffies。

5、RTC

除了系统定时器外,还有一个与时间有关的时钟:实时时钟(RTC),这是一个硬件时钟,用来持久存放系统时间,系统关闭后靠主板上的微型电池保持计时。系统启动时,内核通过读取RTC来初始化Wall Time,并存放在xtime变量中,这是RTC最主要的作用。

延迟函数问题

1、sleep

Linux下的精度为秒,是精确的,实现原理如下:
1. 注册一个信号signal(SIGALRM,handler)。接收内核给出的一个信号。
2. 调用alarm()函数。
3. pause()挂起进程。

alarm是当前进程的私有定时闹钟,pause函数挂起当前进程,内核切换到其他进程运行。当alarm设置的时间到时,内核通过SIGALRM信号去处理,pause函数返回后进程继续执行。

2、usleep

精度为微妙,但实际并不能精确到微妙。通过前面给出的操作系统时钟基础设置,系统精度由HZ系统时钟频率决定,一般设置为250时精度只能到4ms,因此usleep的微妙延迟只能是至少延迟的时间,时间上操作系统进行内核到用户的切换就会花10-30ms级别的时间,因此短时间的延迟一般都会大于设定值。
另外usleep有以下的问题
1. 在一些平台下不是线程安全,如HP-UX以及Linux usleep()会影响信号;
2. 在很多平台,如HP-UX以及某些Linux下,参数的值必须小于1 * 1000 * 1000也就是1秒,否则该函数会报错,并且立即返回。
3. 大部分平台的帮助文档已经明确说了,该函数是已经被舍弃的函数。

3、高精度延迟nanosleep、select

针对Linux平台下的高精度延迟,Linux2.0.x新增了nanosleep系统调用。精度虽然可以设置到纳秒,但是依然没有精确到纳秒,不过对于毫秒级别的延迟的精度已经足够高了,可以完全满足usleep无法达到的精度。但使用nanosleep应注意判断返回值和错误代码,否则容易造成cpu占用率100%。另外,nanosleep()没有usleep函数的缺点,在Solaris的多线程环境下编译器会自动把usleep()连接成nanosleep()。
select系统调用也可以实现延迟,精度可以达到微妙,而且是精确的,因此是首选。

struct timeval delay;
delay.tv_sec = 0;
delay.tv_usec = 20 * 1000; // 20 ms
select(0, NULL, NULL, NULL, &delay);
2017-12-26 16:44:48 roger_ranger 阅读数 873

1. PLT延迟绑定的提出

动态链接速度损耗主要两方面:
1.对全局和静态的数据访问都要进行复杂的GOT定位,然后再间接跳转寻址;
2.动态链接的很多工作是在程序运行时完成的,动态链接器需寻找并装载目标共享对象、符号查找、地址重定位等,如果不加以优化,会出现程序启动过慢的情况。
故而需要出台一些优化动态链接策略的方法。

动态链接下,模块之间包含大量的函数引用(全局变量往往较少,过多的全局变量会导致模块间的耦合度很大,不符合软件编辑规范,参考丰田车载软件系统功能失效事件)。

动态链接会耗费不少时间解决模块之间的函数引用的符号查找及重定位(GOT表填充和更新),但是其实程序运行过程中,可能很多函数时用不到的(比如C++编程#include <stdlib.h>,但是其实只用了一个printf()),比如一些错误处理函数或者一些用户很少用到的功能模块等,延迟绑定的核心思想是函数第一次被用到时才进行绑定,这种做法可以大大加速程序的启动速度,特别有利于一些有大量函数引用和大量模块的软件。

ELF针对延迟绑定,采用的是PLT(procedure linkage table)机制来实现,这种方法使用了很多精巧的指令序列来完成。在Linux中,动态链接在扫描可执行文件时,一旦扫描到外部函数的引用,则glibc会启用绑定函数_dl_runtime_resolve(module, function)来确定模块module中的function函数的在GOT的填充工作。(举个例子,liba.so需要调用libc.so中的bar()函数,那么当liba.so第一次调用bar()时,需要调用_dl_runtime_resolve来完成地址绑定工作,该函数显然需要知道绑定发生在哪个模块,哪个函数。

2. PLT的实现

延迟绑定PLT(Procedure Linkage Table)在GOT表基础又做了一次间接跳转。即模块内关于外部函数的地址引用,这下并不直接通过GOT跳转,而是通过一个叫做PLT项的结构来进行,每个外部函数引用都对应PLT表中的一个表项,比如bar()函数在PLT表中的表项称为bar@plt,实现如下:

bar@plt:
jmp    *(bar@GOT)
push   n
push   moduleID
jump   _dl_runtime_resolve

可以看到bar@plt表项采用了类似于桩代码的格式。bar@plt第一指令中bar@GOT指向GOT表中函数bar()对应项地址,如果GOT表中关于bar()的地址已经被填充非空,则显然将会直接跳转到GOT表给出的bar()函数地址,实现正常的函数调用。

但所谓延迟绑定,即是指在初始未遇到bar()函数之前,GOT表中并无函数bar()的地址信息,而是将后续push n指令的地址填充到GOT中bar()表项中,这时jmp指令将会直接跳转继续执行后续的push n...等指令,该步操作很简单也不需要遍历寻址目标符号,故而代价很低,只需要在生成桩代码时将push n指令位置填入即可。而后的操作便是正常参数压栈工作,其中push n中的参数n对应的是bar()函数符号在重定位表.rel.plt中的下标,push moduleID中的moduleID则是模块ID,调用_dl_runtime_resolve函数完成具体的符号解析和重定位工作,将外部模块中bar()函数的真正地址填入GOT对应的bar@GOT表项。这样当我们下次再次回到PLT表bar@plt表项中转时,便会进入正常的函数调用过程,而不会继续执行push n及之后的代码,那段代码只会在符号未被解析时执行only一次。

上述是PLT的原理,而PLT机制的具体实现则复杂得多。ELF将GOT表分成全局变量专用表”.got”和函数引用专用子表”.got.plt”,所有外部函数的引用中转全部被集中到”.got.plt”子表中(前面提到过每个共享模块都有自己的一份GOT表,通过推出GOT表来实现代码段的地址无关性,而将地址相关性转移到GOT表上,这也是为啥GOT表示放在.data段中的原因)。关于”.got.plt”子表需要注意的是它的前三项:第一项为”.dynamic”段地址,第二项为本模块的ID,第三项保存的是_dl_runtime_resolve()地址。


其中第二项和第三项由动态链接器在装载共享模块的时候负责初始化。 PLT每项规定16个字节,刚好可以放3条指令,如下:

bar@plt:
jmp     *(bar@GOT)
push    n
jmp     PLT0

其中函数PLT0是通用的的,形式如下:

PLT0:
push   *(GOT+4)
jmp   *(GOT+8)

对照图片可以看到,moduleID是方便_dl_runtime_resolve()找到目标模块的”.got.plt”函数地址子表,而n则对应要修正函数的表项下标。这样便实现了一个利用桩代码完成的PLT延迟绑定操作。

2017-04-10 23:48:52 weixin_38227455 阅读数 222

1 at 命令

at命令可以指定某一任务在将来的特定时间运行,at命令必须指定任务运行的时间,设置完成后按ctrl+d开始



1.2 查看任务命令是at –l

1.3 取消任务是atrm+编号

1.4 如果是输出则输出的内容在mail里面

1.5 我们现在切换用户看是否可以添加延时任务

可以看出student普通用户是可以添加延时任务的这样做会影响超级用户的使用,因此我们应该将它禁用


在at.deny配置文件中添加student这样就可以禁止它的定时了

我们还可以设置白名单允许普通用户来设定延时任务

2 管理定期任务

cron设备管理必须按计划定期重复运行的程序

crond每分钟唤醒一次,以运行计划的任务

crontab  -l 列出文件

crontab  -r 删除文件

crontab  -e 编辑文件

crontab –e使用的默认编辑器为vi

首先确认crond.service服务是开启的


用crontable –e –u –root对系统进行定时任务的是设定

也可以用命令直接进行设置/var/spool/cron/root

Cron权限的设定

在系统中默认所有用户可以设定 这样做是很不安全的

用户黑名单的设置/etc/cron.deny

用户白名单的设置/etc/cron.allow

 

系统中的临时文件

系统运行产生的临时文件在/usr/lib/tmpfile.d/下

我们现在增加一个临时任务看看

清除临时文件systemd-tmpfiles –create /usr/lib/tmpfiles/han.conf

启用服务产生临时文件systemd-tmpfiles –create/usr/lib/tmpfiles/han.conf

2019-04-24 10:06:12 qq_16812035 阅读数 61

以前看书的时候,模模糊糊知道linux延迟重定位的意思,没对知识点进行总结和记录,面试的时候,问到了ELF文件的时候,才知道自己并没有搞懂。

在面试中,ELF文件中的plt和got是必问的问题,plt和got是用来实现linux中的延迟重定位,意思是等到调用到这个外部函数的时候再进行重定位,优点很明显,加快了程序的启动速度,在传统的运行时重定位中,在程序启动的时候,直接将对每个动态函数解析,将动态函数的地址填入到got表中,在程序比较大的时候,无疑会影响程序的启动速度,延迟重定位技术将重定位放到了动态函数第一次被调用的时候,这种技术的实现就是利用了ELF格式文件中的plt和got,首先plt表和got表类似于数组,两者的不同点是plt表中放的是可执行代码,所以plt表中的数据是不能改变的,接着是got表,got表中存放的是数据,所以got表中的数据是可以改动的,这些数据其实就是一些跳转地址,提供给plt中的代码使用。下面写个代码调试下。

#include<stdio.h>

void HelloWorld(){
        printf("Hello World!\n");
        return;
}

int main(){
        HelloWorld();
        return 0;
}

这段代码中,printf为外部模块函数,需要进行动态重定位,gcc -g test.c -o test,生成可执行文件test,gdb test 动态调试test。

可以看到HelloWorld函数在 0x40052d中,接着查看HelloWorld函数。

在HelloWorld中 callq 400410 <puts@plt> 代表了调用了puts函数,其中的@plt是关键,代表了0x400410这个地址并不是puts函数的真实地址,而是plt表的地址,说明函数会先跳到plt表中,接着看下0x400410中有什么内容。

可以看到0x40010 中是plt表中的内容,这里存放着三行代码。

jmpq *0x200c02(%rip)

pushq $0x0

jmpq 0x400400

这里看到plt中又直接跳转到了 *0x200c02(%rip)中,跳转到(0x200c02 + rip)中存放的数据,接着看一下(0x200c02 + rip)中存放了什么,在x86汇编中rip是指向下一条指令的地址,所以是0x601018,上面这条指令右边的注释已经帮我们标明了。

可以看到值为0x400416,正是当前指令的下一条指令pushq $0x0。pushq $0x0这条指令将要链接的函数的索引入栈,在rel.plt中使用该索引,可以用readelf -r test查看。可以看到puts再索引为0的地方,跟上面的push一致。

接着是jmpq 0x400400指令,不知道为啥disas指令不起作用,用了x /5i 0x400400指令。

到这里的时候,第一个pushq 0x200c02(%rip) 将link_map的地址入栈,jmpq *0x200c04(%rip)跳转到dl_runtime_reslove中解析函数,解析完毕,再将解析到的函数地址,填到对应的got表项中。关于dl_runtime_reslove怎么知道填到哪个got表项中,可以看rle.plt表项目中的offset,offset字段中的值为0x601018,跟前面GOT表项的地址对上了。每个外部函数第一次调用都要进行这样一次函数的查找,并将地址填到got表项中,这样下次调用的时候,就无需查找了,直接跳转到外部函数。

 

linux中的延迟函数

阅读数 607

linux下的延迟函数

博文 来自: hao507

Linux延迟函数

阅读数 511

没有更多推荐了,返回首页