title: 從一次事故談?wù)?pid 文件的作用
tags:
- pid
categories:
- Tech
comments: true
date: 2017-05-26 20:00:00
很多程序在啟動(dòng)后會(huì)在 /var/run 目錄下創(chuàng)建一個(gè)文件 xxx.pid 文件欢搜,用以保存這個(gè)進(jìn)程的進(jìn)程號(hào)辨绊。之前一直以為這個(gè)文件僅僅是用來控制進(jìn)程的啟動(dòng)和關(guān)閉,直到最近遇到的一個(gè)慘痛的教訓(xùn)...
先講個(gè)故事(也是個(gè)事故...)
最近手頭的一個(gè)工作是分析全站的鏡像流量祈秕,流程大概是抓取網(wǎng)卡的所有幀逐層解析,最終是在應(yīng)用層實(shí)現(xiàn)重組 http 會(huì)話翅溺,將重組后的數(shù)據(jù)發(fā)送到 kafka 供后端分析奢啥。(程序的代碼在 http-capture)
程序的細(xì)節(jié)這里不談,直接進(jìn)入事故...
一開始這個(gè)程序是直接跑在后臺(tái)的满钟,為了保證程序的可靠性胜榔,準(zhǔn)備托管給 supervisor。于是巴拉巴拉把 supervisor 的配置文件寫好湃番,然后 supervisorctl update夭织,把程序跑起來了。
嗯吠撮,supervisorctl status 看一下尊惰,http-capture 狀態(tài)變成 running,沒毛病,非常穩(wěn)弄屡!再 ps 查看一下進(jìn)程的大概情況题禀,我了個(gè)去有兩個(gè) http-capture 進(jìn)程在工作,原來之前運(yùn)行在后臺(tái)的進(jìn)程忘記關(guān)掉了琢岩!
由于是使用 ansible 批量操作的投剥,所以全部的12臺(tái)設(shè)備都是啟動(dòng)了兩個(gè)進(jìn)程,也就是說每臺(tái)設(shè)備同時(shí)輸出了兩份相同的數(shù)據(jù)担孔!再一看kafka 那邊的入隊(duì)情況江锨,果然 double 了。oh糕篇,my god啄育!
事故整個(gè)復(fù)盤就是這么簡(jiǎn)答,后續(xù)的處理拌消、恢復(fù)工作就先不談了挑豌。
事后分析
整個(gè)事故直接原因總結(jié)起來很簡(jiǎn)單,就是操作人員大意墩崩,誤操作導(dǎo)致的氓英。但是深究背后的程序是否存在問題呢,當(dāng)然存在很多問題的鹦筹。
首先铝阐,我在操作過程中測(cè)試成功后直接使用 ansible 全量上線。更合適的方式應(yīng)該是先ansible 操作1~2臺(tái)設(shè)備上線铐拐,然后待觀察穩(wěn)定后全量上線徘键。
其次,就是程序本身存在問題遍蟋,邏輯不夠嚴(yán)謹(jǐn)吹害,這種要保證一臺(tái)服務(wù)器上只能唯一啟動(dòng)的進(jìn)程,在程序啟動(dòng)邏輯中就應(yīng)該驗(yàn)證這個(gè)條件虚青。
另外它呀,就是問題的發(fā)現(xiàn)過程,是偶然的通過 ps 命令查看進(jìn)程此發(fā)現(xiàn)的此問題挟憔,缺少統(tǒng)一的監(jiān)控钟些、告警工具。
最后绊谭,發(fā)現(xiàn)問題后政恍,沒有快速的回滾機(jī)制,只能通過命令依次全部 kill 掉后达传,但是此時(shí)有大量的數(shù)據(jù)走入后端了篙耗,容錯(cuò)能力不足迫筑。
總體說在,就是在程序啟動(dòng)宗弯、運(yùn)行脯燃、關(guān)閉的過程中缺少必要的檢測(cè)、容錯(cuò)和恢復(fù)手段蒙保。其他的不談辕棚,這里重點(diǎn)說說第二點(diǎn),程序自身的問題邓厕,如何實(shí)現(xiàn)程序自身的啟動(dòng)檢測(cè)逝嚎。
Pid 文件的作用
pid 文件是什么呢?打開系統(tǒng)(Linux) 的 "/var/run/" 目錄可以看到有很多已 ".pid" 為結(jié)尾的文件详恼,如下:
這些文件只有一行补君,它記錄的是相應(yīng)進(jìn)程的 pid,即進(jìn)程號(hào)昧互。所以通過 pid 文件可以很方便的得到一個(gè)進(jìn)程的 pid挽铁,然后做相應(yīng)的操作,比如檢測(cè)敞掘、關(guān)閉叽掘。
那 pid 文件是不是只是存儲(chǔ)呢?當(dāng)然不是玖雁!它還有另一個(gè)更重要的作用够掠,那就是防止進(jìn)程啟動(dòng)多個(gè)副本。通過文件鎖茄菊,可以保證一時(shí)間內(nèi)只有一個(gè)進(jìn)程能持有這個(gè)文件的寫權(quán)限,所以在程序啟動(dòng)的檢測(cè)邏輯中加入獲取pid 文件鎖并寫pid文件的邏輯就可以防止重復(fù)啟動(dòng)進(jìn)程的多個(gè)副本了赊堪。
下面是實(shí)現(xiàn)這個(gè)邏輯的一段 c 代碼面殖,在程序的啟動(dòng)檢測(cè)邏輯中調(diào)用這個(gè)函數(shù)即可保證程序唯一啟動(dòng)。
void writePidFile(const char *szPidFile)
{
/* 獲取文件描述符 */
char str[32];
int pidfile = open(szPidFile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
if (pidfile < 0) {
printf("pidfile is %d", pidfile);
exit(1);
}
/* 鎖定文件哭廉,如果失敗則說明文件已被鎖脊僚,存在一個(gè)正在運(yùn)行的進(jìn)程,程序直接退出 */
if (lockf(pidfile, F_TLOCK, 0) < 0) {
fprintf(stderr, "File locked ! Can not Open Pid File: %s", szPidFile);
exit(0);
}
/* 鎖定文件成功后遵绰,會(huì)一直持有這把鎖辽幌,知道進(jìn)程退出,或者手動(dòng) close 文件
然后將進(jìn)程的進(jìn)程號(hào)寫入到 pid 文件*/
sprintf(str, "%d\n", getpid()); // \n is a symbol.
ssize_t len = strlen(str);
ssize_t ret = write(pidfile, str, len);
if (ret != len ) {
fprintf(stderr, "Can't Write Pid File: %s", szPidFile);
exit(0);
}
}
參考文檔
https://unix.stackexchange.com/questions/12815/what-are-pid-and-lock-files-for
https://www.zhihu.com/question/20289583
https://stackoverflow.com/a/13651761/5126723
http://www.man7.org/tlpi/code/online/dist/filelock/create_pid_file.c.html