Press "Enter" to skip to content

深入理解PHP原理之Session Gc的一个小概率Notice

如果在ubuntu/Debian下, 采用apt安装的PHP, 那么在使用Session的时候, 就可能会有小概率遇到这个提示.

PHP Notice: session_start(): ps_files_cleanup_dir:
   opendir(/var/lib/php5) failed: Permission denied (13)
   in /home/laruence/www/htdocs/index.php on line 22

这是因为, 在PHP中, 如果使用file_handler作为Session的save handler, 那么就有概率在每次session_start的时候运行Session的Gc过程.

//有省略
        int nrdels = -1;
        nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg(TSRMLS_C));
        if (nrand < PS(gc_probability)) {
            PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &nrdels TSRMLS_CC);
        }
//有省略

这个警告的原因是因为在apt的PHP中, session的默认目录/var/lib/php5的权限是733 with sticky bit, 也就是

drwx-wx-wt  root  root

而一般PHP的worker都运行在非root身份下, 所以是没有权限来打开这个文件夹的(但是因为可以write, 所以不影响正常的Session文件读取). 于是在s_gc中的如下代码, 就会触发开头所说的Notice:

//对于file handler来说, s_gc间接调用ps_files_cleanup_dir:
   dir = opendir(dirname);
    if (!dir) {
        php_error_docref(NULL TSRMLS_CC, E_NOTICE,
           "ps_files_cleanup_dir: opendir(%s) failed: %s (%d)",
           dirname, strerror(errno), errno);
        return (0);
    }

当然, 在ubuntu/Debian下, 还是有gc回收的, 只不过是外部的cron进程来完成的, 默认的在/etc/cron.d/php5:,

09,39 *     * * *     root   [ -x /usr/lib/php5/maxlifetime ]
&& [ -d /var/lib/php5 ] && find /var/lib/php5/
 -type f -cmin +$(/usr/lib/php5/maxlifetime) -print0
| xargs -n 200 -r -0 rm

另外, 可以看到, 在判别s_gc是否运行的时候, 有俩个关键变量: PS(gc_divisor)和PS(gc_probability), 这俩个变量分别对应着session的运行时配置项的俩个同名配置项:

session.gc_probability和session.gc_divisor, 他们分别默认为1和100.

而php_combined_lcg是一个随机数发生器, 生成0到1范围的随机数, 所以上面的判别相当于:

 rand < probability / gc_divisor

也就是说, 默认情况下, 差不多是100次能调用一次gc过程. 所以也就是小概率的可以看到这个Notice.

要关闭这个Notice, 只需要设置:

session.gc_probability = 0, 让s_gc完全没有运行的可能即可.

当然, 你也可以改变这个文件夹的权限…

最后, 感谢CFC4N提供这个问题.

29 Comments

  1. Marylou
    Marylou 2015-02-06

    Hello very cool site!! Guy .. Beautiful ..
    Superb .. I’ll bookmark your site and take the
    feeds also? I am glad to search out a lot of helpful info right here within the put up,
    we’d like work out more techniques in this regard, thanks
    for sharing. . . . . .

    web page; Marylou,

  2. Dying
    Dying 2014-10-13

    有个疑问,session.gc_probability为0的时候,是怎么清理session的,因为我看我的配置里面已经是0了,但是过期的session也是被清理了。

  3. info-expresspcb.com
    info-expresspcb.com 2014-08-22

    The web has truly changed the way we communicate and
    made it far easier to stay informed about the lives of our loved ones.

    Today, lots of websites and blogs bring in unheard of income for their owners merely by
    promoting another’s company on their web pages. To report stolen email addresses at Yahoo, click Yahoo.

  4. larro
    larro 2012-10-08

    鸟哥,如果关闭gc的话,session不是不能够失效过期了么,也不能够删除掉一直在增加的session文件了吧?

  5. […] s_gc是函数指针,指向PS_GC_FUNC, 而gc_maxlifetime是设置session过期时间。随机事件可以看下鸟哥的《深入理解PHP原理之Session Gc的一个小概率Notice》 […]

  6. fy
    fy 2011-03-29

    @CFC4N 每个client对应一个session文件,过期的必须手动清理。而php的运行机制决定这些过期文件的清理只能通过这样的概率处理他们,当然这个概率落到谁头上,响应请求的响应时间要稍长一点。
    @雪候鸟 当运行在fastcgi 方式下是否能fork出一个进程专门来干这事,这个概率问题不就解决了?

  7. horseluke
    horseluke 2011-03-29

    在我的印象中,deploy环境的session.gc_divisor是1000……
    (check了php.ini,确实是1000:
    ; Default Value: 100
    ; Development Value: 1000
    ; Production Value: 1000

    遇到这种问题,相对安全的一般提议改动session.save_path,如果实在改动不了,只好弄个可以用于session_set_save_handler的类了…

  8. CFC4N
    CFC4N 2011-03-29

    感谢鸟哥。
    session_start的话,一定概率的GC,是为了效率考虑的吗?还是有其他的?
    文中的其他GC方式,是OS去执行的吗?但是我没看到ubuntu上的cron中有任何任务。

    • 雪候鸟
      雪候鸟 2011-03-29

      @CFC4N 恩, 刚才确认了下, 补充到文章中了.

Leave a Reply

Your email address will not be published. Required fields are marked *