Chinaunix首页 | 论坛 | 博客
  • 博客访问: 807298
  • 博文数量: 247
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 501
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-12 21:53
个人简介

系统未建立

文章分类

全部博文(247)

文章存档

2021年(1)

2020年(3)

2019年(5)

2018年(3)

2017年(44)

2016年(75)

2015年(52)

2014年(63)

2013年(1)

我的朋友

分类: LINUX

2017-01-04 14:43:46

同一款产品最近发现在reboot前对文件做的修改没有生效。之前是可以的。
首先可以明确的是,改动没有从缓存同步到磁盘就重启了。
对比新旧版本差异,发现新版本更换了更高的busybox1.17.2,而老版本使用的busybox1.01没有问题。比较reboot代码流程(init.c里面)发现代码改动还是蛮大的。
主要差别,个人认为有2点:
1)run_actions(SHUTDOWN);流程里面的waitpid改为wait,且wait后判断动作发生变化
1.01版本waitfor代码
点击(此处)折叠或打开
  1. static int waitfor(const struct init_action *a)
  2. {
  3.     int pid;
  4.     int status, wpid;

  5.     pid = run(a);
  6.     while (1) {
  7.         wpid = waitpid(pid,&status,0);
  8.         if (wpid == pid)
  9.             break;
  10.         if (wpid == -1 && errno == ECHILD) {
  11.             /* we missed its termination */
  12.             break;
  13.         }
  14.         /* FIXME other errors should maybe trigger an error, but allow
  15.          * the program to continue */
  16.     }
  17.     return wpid;
  18. }
1.17.2版本waitfor代码

点击(此处)折叠或打开

  1. static void waitfor(pid_t pid)
  2. {
  3.     /* waitfor(run(x)): protect against failed fork inside run() */
  4.     if (pid <= 0)
  5.         return;

  6.     /* Wait for any child (prevent zombies from exiting orphaned processes)
  7.      * but exit the loop only when specified one has exited. */
  8.     while (1) {
  9.         pid_t wpid = wait(NULL);
  10.         message(L_LOG | L_CONSOLE, "wait wpid:%d, pid:%d\n",wpid,pid);
  11.         mark_terminated(wpid);
  12.         /* Unsafe. SIGTSTP handler might have wait'ed it already */
  13.         /*if (wpid == pid) break;*/
  14.         /* More reliable: */
  15.         if (kill(pid, 0))
  16.             break;
  17.     }
  18. }
这个变化很早就有了,担心wait的pid不是SHUTDOWN在inittab中指定的程序,加入log诊断。发现这块没有问题。
2)实施系统关闭的流程,调用run_actions(SHUTDOWN);的实现变化
1.01中代码为:

点击(此处)折叠或打开

  1. static void shutdown_system(void)
  2. {
  3.     sigset_t block_signals;

  4.     /* run everything to be run at "shutdown". This is done _prior_
  5.      * to killing everything, in case people wish to use scripts to
  6.      * shut things down gracefully... */
  7.     run_actions(SHUTDOWN);

  8.     /* first disable all our signals */
  9.     sigemptyset(&block_signals);
  10.     sigaddset(&block_signals, SIGHUP);
  11.     sigaddset(&block_signals, SIGQUIT);
  12.     sigaddset(&block_signals, SIGCHLD);
  13.     sigaddset(&block_signals, SIGUSR1);
  14.     sigaddset(&block_signals, SIGUSR2);
  15.     sigaddset(&block_signals, SIGINT);
  16.     sigaddset(&block_signals, SIGTERM);
  17.     sigaddset(&block_signals, SIGCONT);
  18.     sigaddset(&block_signals, SIGSTOP);
  19.     sigaddset(&block_signals, SIGTSTP);
  20.     sigprocmask(SIG_BLOCK, &block_signals, NULL);

  21.     /* Allow Ctrl-Alt-Del to reboot system. */
  22.     init_reboot(RB_ENABLE_CAD);

  23.     message(CONSOLE | LOG, "The system is going down NOW !!");
  24.     sync();

  25.     /* Send signals to every process _except_ pid 1 */
  26.     message(CONSOLE | LOG, "Sending SIGTERM to all processes.");
  27.     kill(-1, SIGTERM);
  28.     sleep(1);
  29.     sync();

  30.     message(CONSOLE | LOG, "Sending SIGKILL to all processes.");
  31.     kill(-1, SIGKILL);
  32.     sleep(1);

  33.     sync();
  34. }
1.17.2代码为:

点击(此处)折叠或打开

  1. static void run_shutdown_and_kill_processes(void)
  2. {
  3.     /* Run everything to be run at "shutdown". This is done _prior_
  4.      * to killing everything, in case people wish to use scripts to
  5.      * shut things down gracefully... */
  6.     message(L_CONSOLE | L_LOG, "SHUTDOWN!");
  7.     run_actions(SHUTDOWN);

  8.     message(L_CONSOLE | L_LOG, "The system is going down NOW!");

  9.     /* Send signals to every process _except_ pid 1 */
  10.     kill(-1, SIGTERM);
  11.     message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");
  12.     sync();
  13.     sleep(1);
  14.     kill(-1, SIGKILL);
  15.     message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
  16.     sync();
  17.     /*sleep(1); - callers take care about making a pause */
  18. }
差异有几个地方:
a)老版本kill前有进行信号屏蔽,新版本没有。担心kill影响,可以查看http://blog.csdn.net/zanget/article/details/6659838,对kill解释如下

点击(此处)折叠或打开

  1. int kill(pid_t pid, int sig);

  2. 1. pid>0时,pid是信号欲送往的进程的标识。

  3. 2. pid=0时,信号送往与调用kill()的进程属同一个使用组的进程

  4. 3. pid=-1时,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)

  5. 4. pid<-1时,信号将送往以-pid为组标识的进程。

b)减少了1个sleep(1),而注释为callers take care about making a pause。调用者要关注进行停顿。
后来,发现放开sleep(1);修改测试成功有所提高。最终测试可靠的修改如下

点击(此处)折叠或打开

  1. static void run_shutdown_and_kill_processes(void)
  2. {
  3.     /* Run everything to be run at "shutdown". This is done _prior_
  4.      * to killing everything, in case people wish to use scripts to
  5.      * shut things down gracefully... */
  6.     run_actions(SHUTDOWN);

  7.     message(L_CONSOLE | L_LOG, "The system is going down NOW!");
  8.     sync();
  9.     sleep(2);
  10.     /* Send signals to every process _except_ pid 1 */
  11.     kill(-1, SIGTERM);
  12.     message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");
  13.     sleep(1);
  14.     sync();
  15.     kill(-1, SIGKILL);
  16.     message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
  17.     sleep(1); /*- callers take care about making a pause */
  18.     message(L_CONSOLE, "Last Sync");
  19.     sync();
  20. }



问题查证过程用了好几个VB测试脚本,最终选择如下:

点击(此处)折叠或打开

  1. #$language = "VBScript"
  2. #$interface = "1.0"

  3. Sub main

  4. Dim Time

  5.   Do while (1)
  6.      crt.Screen.Send "root" & VbCr
  7.      crt.Sleep 500
  8.      crt.Screen.Send "root" & VbCr
  9.      crt.Screen.Send "" & VbCr
  10.      crt.Screen.Send "#"
  11.      crt.Screen.Send Now & VbCr
  12.      crt.Screen.Send "ls /userconfig -l" & VbCr
  13.      crt.Sleep 500
  14.      crt.Screen.Send "echo 1 > /userconfig/calmode" & VbCr
  15.      crt.Sleep 3500
  16.      crt.Screen.Send "rm /userconfig/calmode" & VbCr
  17.      crt.Screen.Send "" & VbCr
  18.      crt.Screen.Send "reboot" & VbCr
  19.      crt.Sleep 60000
  20.      crt.Screen.Send "" & VbCr
  21.   Loop

  22. End Sub

中间停顿3500是3.5s,大于inode脏页时间。对应的一些缓存写入参考:
节选:linux内核有着非常强大的磁盘缓存机制,就是磁盘数据先不往磁盘直接读写而是直接操作缓存,待到一定条件满足的时候才读写磁盘,大致有几个参数:1.dirty_writeback_centisecs,这个参数表示内核刷新缓存的时间间 隔;2.dirty_expire_centisecs,这个参数表示一个inode在dirty状态停留的最长时间;
也尝试过在rm动作后停顿3.5s,测试发现还是存在小概率失败。
中对sync命令有解释,也可以man查看。也有看到讲linux已经实现sync的同步,但测试看不是这样,或许跟linux kernel版本有关。
  • buffer:为了解决写磁盘的效率 
  • cache:为了解决读磁盘的效率

linux dirtypage回写时机
1 定时方式: 定时回写是基于这样的原则:/proc/sys/vm/dirty_writeback_centisecs的值表示多长时间会启动回写线程,由这个定时器启动的回写线程只回写在内存中为dirty时间超过(/proc/sys/vm/didirty_expire_centisecs / 100)秒的页(这个值默认是3000,也就是30秒),一般情况下dirty_writeback_centisecs的值是500,也就是5秒,所以默认情况下系统会5秒钟启动一次回写线程,把dirty时间超过30秒的页回写,要注意的是,这种方式启动的回写线程只回写超时的dirty页,不会回写没超时的dirty页,可以通过修改/proc中的这两个值,细节查看内核函数wb_kupdate。

2 内存不足的时候: 这时并不将所有的dirty页写到磁盘,而是每次写大概1024个页面,直到空闲页面满足需求为止

3 写操作时发现脏页超过一定比例: 当脏页占系统内存的比例超过/proc/sys/vm/dirty_background_ratio 的时候,write系统调用会唤醒pdflush回写dirty page,直到脏页比例低于/proc/sys/vm/dirty_background_ratio,但write系统调用不会被阻塞,立即返回.当脏页占系统内存的比例超/proc/sys/vm/dirty_ratio的时候, write系统调用会被被阻塞,主动回写dirty page,直到脏页比例低于/proc/sys/vm/dirty_ratio


其他参考
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=25597477&id=4778214
http://blog.csdn.net/wavemcu/article/details/8544333这个对reboot流程讲解相对详细,特别有用的是kernel reboot doesn't sync: do that yourself before calling this. 
阅读(2145) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~