• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            elva

            關(guān)于SIGPIPE導(dǎo)致的程序退出

            收集一些網(wǎng)上的資料,以便參考:

            http://blog.chinaunix.net/u2/69143/showart_1087349.html

                 當(dāng)服務(wù)器close一個連接時,若client端接著發(fā)數(shù)據(jù)。根據(jù)TCP協(xié)議的規(guī)定,會收到一個RST響應(yīng),client再往這個服務(wù)器發(fā)送數(shù)據(jù)時,系統(tǒng)會發(fā)出一個SIGPIPE信號給進(jìn)程,告訴進(jìn)程這個連接已經(jīng)斷開了,不要再寫了。
                根據(jù)信號的默認(rèn)處理規(guī)則SIGPIPE信號的默認(rèn)執(zhí)行動作是terminate(終止、退出),所以client會退出。若不想客戶端退出可以把SIGPIPE設(shè)為SIG_IGN

                如:    signal(SIGPIPE,SIG_IGN);
                這時SIGPIPE交給了系統(tǒng)處理。

              服務(wù)器采用了fork的話,要收集垃圾進(jìn)程,防止僵尸進(jìn)程的產(chǎn)生,可以這樣處理:
              signal(SIGCHLD,SIG_IGN); 交給系統(tǒng)init去回收。
               這里子進(jìn)程就不會產(chǎn)生僵尸進(jìn)程了。


            http://www.cublog.cn/u/31357/showart_242605.html


            好久沒做過C開發(fā)了,最近重操舊業(yè)。
            聽說另外一個項目組socket開發(fā)遇到問題,發(fā)送端和接受端數(shù)據(jù)大小不一致。建議他們采用writen的重發(fā)機制,以避免信號中斷錯誤。采用后還是有問題。PM讓我?guī)兔ρ芯肯隆?/div>
            UNP n年以前看過,很久沒做過底層開發(fā),手邊也沒有UNP vol1這本書,所以做了個測試程序,研究下實際可能發(fā)生的情況了。
             
            測試環(huán)境:AS3和redhat 9(缺省沒有nc)
             
            先下載unp源碼:
            wget http://www.unpbook.com/unpv13e.tar.gz
            tar xzvf *.tar.gz;
            configure;make lib.
            然后參考str_cli.c和tcpcli01.c,寫了測試代碼client.c
             
             

            #include    "unp.h"

            #define MAXBUF 40960
            void processSignal(int signo)
            {
                printf("Signal is %d\n", signo);
                signal(signo, processSignal);
            }
            void
            str_cli(FILE *fp, int sockfd)
            {
                char    sendline[MAXBUF], recvline[MAXBUF];

                while (1) {

                    memset(sendline, 'a', sizeof(sendline));
                    printf("Begin send %d data\n", MAXBUF);
                    Writen(sockfd, sendline, sizeof(sendline));
                    sleep(5);

                }
            }

            int
            main(int argc, char **argv)
            {
                int                    sockfd;
                struct sockaddr_in    servaddr;

                signal(SIGPIPE, SIG_IGN);
                //signal(SIGPIPE, processSignal);

                if (argc != 2)
                    err_quit("usage: tcpcli [port]");

                sockfd = Socket(AF_INET, SOCK_STREAM, 0);

                bzero(&servaddr, sizeof(servaddr));
                servaddr.sin_family = AF_INET;
                servaddr.sin_port = htons(atoi(argv[1]));
                Inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);

                Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

                str_cli(stdin, sockfd);        /* do it all */

                exit(0);
            }

             

            為了方便觀察錯誤輸出,lib/writen.c也做了修改,加了些日志:

             

             

            /* include writen */
            #include    "unp.h"

            ssize_t                        /* Write "n" bytes to a descriptor. */
            writen(int fd, const void *vptr, size_t n)
            {
                size_t        nleft;
                ssize_t        nwritten;
                const char    *ptr;

                ptr = vptr;
                nleft = n;
                while (nleft > 0) {
                    printf("Begin Writen %d\n", nleft);
                    if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
                        if (nwritten < 0 && errno == EINTR) {
                            printf("intterupt\n");
                            nwritten = 0;        /* and call write() again */
                        }
                        else
                            return(-1);            /* error */
                    }

                    nleft -= nwritten;
                    ptr += nwritten;
                    printf("Already write %d, left %d, errno=%d\n", nwritten, nleft, errno);
                }
                return(n);
            }
            /* end writen */

            void
            Writen(int fd, void *ptr, size_t nbytes)
            {
                if (writen(fd, ptr, nbytes) != nbytes)
                    err_sys("writen error");
            }

             

            client.c放在tcpclieserv目錄下,修改了Makefile,增加了client.c的編譯目標(biāo)

             


            client: client.c
                            ${CC} ${CFLAGS} -o $@ $< ${LIBS}

             

            接著就可以開始測試了。

            測試1 忽略SIGPIPE信號,writen之前,對方關(guān)閉接受進(jìn)程

            本機服務(wù)端:

            nc -l -p 30000
             
            本機客戶端:
            ./client 30000
            Begin send 40960 data
            Begin Writen 40960
            Already write 40960, left 0, errno=0
            Begin send 40960 data
            Begin Writen 40960
            Already write 40960, left 0, errno=0
            執(zhí)行到上步停止服務(wù)端,client會繼續(xù)顯示:
            Begin send 40960 data
            Begin Writen 40960
            writen error: Broken pipe(32)
            結(jié)論:可見write之前,對方socket中斷,發(fā)送端write會返回-1,errno號為EPIPE(32)

            測試2 catch SIGPIPE信號,writen之前,對方關(guān)閉接受進(jìn)程

            修改客戶端代碼,catch sigpipe信號

                    //signal(SIGPIPE, SIG_IGN);

                    signal(SIGPIPE, processSignal);

            本機服務(wù)端:

            nc -l -p 30000
             
            本機客戶端:
            make client
            ./client 30000
            Begin send 40960 data
            Begin Writen 40960
            Already write 40960, left 0, errno=0
            Begin send 40960 data
            Begin Writen 40960
            Already write 40960, left 0, errno=0
            執(zhí)行到上步停止服務(wù)端,client會繼續(xù)顯示:
            Begin send 40960 data
            Begin Writen 40960
            Signal is 13
            writen error: Broken pipe(32)
            結(jié)論:可見write之前,對方socket中斷,發(fā)送端write時,會先調(diào)用SIGPIPE響應(yīng)函數(shù),然后write返回-1,errno號為EPIPE(32)
             

            測試3 writen過程中,對方關(guān)閉接受進(jìn)程

            為了方便操作,加大1次write的數(shù)據(jù)量,修改MAXBUF為4096000

            本機服務(wù)端:

            nc -l -p 30000
             
            本機客戶端:
            make client
            ./client 30000
            Begin send 4096000 data
            Begin Writen 4096000
            執(zhí)行到上步停止服務(wù)端,client會繼續(xù)顯示:
            Already write 589821, left 3506179, errno=0
            Begin Writen 3506179
            writen error: Connection reset by peer(104)

            結(jié)論:可見socket write中,對方socket中斷,發(fā)送端write會先返回已經(jīng)發(fā)送的字節(jié)數(shù),再次write時返回-1,errno號為ECONNRESET(104)

            為什么以上測試,都是對方已經(jīng)中斷socket后,發(fā)送端再次write,結(jié)果會有所不同呢。從后來找到的UNP5.12,5.13能找到答案

            The client's call to readline may happen before the server's RST is received by the client, or it may happen after. If the readline happens before the RST is received, as we've shown in our example, the result is an unexpected EOF in the client. But if the RST arrives first, the result is an ECONNRESET ("Connection reset by peer") error return from readline.

            以上解釋了測試3的現(xiàn)象,write時,收到RST.

            What happens if the client ignores the error return from readline and writes more data to the server? This can happen, for example, if the client needs to perform two writes to the server before reading anything back, with the first write eliciting the RST.

            The rule that applies is: When a process writes to a socket that has received an RST, the SIGPIPE signal is sent to the process. The default action of this signal is to terminate the process, so the process must catch the signal to avoid being involuntarily terminated.

            If the process either catches the signal and returns from the signal handler, or ignores the signal, the write operation returns EPIPE.

            以上解釋了測試1,2的現(xiàn)象,write一個已經(jīng)接受到RST的socket,系統(tǒng)內(nèi)核會發(fā)送SIGPIPE給發(fā)送進(jìn)程,如果進(jìn)程catch/ignore這個信號,write都返回EPIPE錯誤.

            因此,UNP建議應(yīng)用根據(jù)需要處理SIGPIPE信號,至少不要用系統(tǒng)缺省的處理方式處理這個信號,系統(tǒng)缺省的處理方式是退出進(jìn)程,這樣你的應(yīng)用就很難查處處理進(jìn)程為什么退出。


            http://blog.csdn.net/shcyd/archive/2006/10/28/1354577.aspx

            在Unix系統(tǒng)下,如果send在等待協(xié)議傳送數(shù)據(jù)時網(wǎng)絡(luò)斷開的話,調(diào)用send的進(jìn)程會接收到一個SIGPIPE信號,進(jìn)程對該信號的默認(rèn)處理是進(jìn)程終止。
            在Unix系統(tǒng)下,如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時網(wǎng)絡(luò)斷開了,那么調(diào)用recv的進(jìn)程會接收到一個SIGPIPE信號,進(jìn)程對該信號的默認(rèn)處理是進(jìn)程終止。

            處理方法:
            在初始化時調(diào)用signal(SIGPIPE,SIG_IGN)忽略該信號(只需一次)
            其時send或recv函數(shù)將返回-1,errno為EPIPE,可視情況關(guān)閉socket或其他處理

            gdb:
            gdb默認(rèn)收到sigpipe時中斷程序,可調(diào)用handle SIGPIPE nostop print

            相關(guān)

            (1)SIG_DFL信號專用的默認(rèn)動作:
              (a)如果默認(rèn)動作是暫停線程,則該線程的執(zhí)行被暫時掛起。當(dāng)線程暫停期間,發(fā)送給線程的任何附加信號都不交付,直到該線程開始執(zhí)行,但是SIGKILL除外。
              (b)把掛起信號的信號動作設(shè)置成SIG_DFL,且其默認(rèn)動作是忽略信號 (SIGCHLD)。

            (2)SIG_IGN忽略信號
              (a)該信號的交付對線程沒有影響
              (b)系統(tǒng)不允許把SIGKILL或SIGTOP信號的動作設(shè)置為SIG_DFL

            (3)指向函數(shù)的指針--捕獲信號
              (a)信號一經(jīng)交付,接收線程就在指定地址上執(zhí)行信號捕獲程序。在信號捕 獲函數(shù)返回后,接受線程必須在被中斷點恢復(fù)執(zhí)行。
              (b)用C語言函數(shù)調(diào)用的方法進(jìn)入信號捕捉程序:
                void func (signo)
                int signo;
                func( )是指定的信號捕捉函數(shù),signo是正被交付信號的編碼
              (c)如果SIGFPE,SIGILL或SIGSEGV信號不是由C標(biāo)準(zhǔn)定義的kill( )或raise( )函數(shù)所生成,則從信號SIGFPE,SIGILL,SIGSEGV的信號捕獲函數(shù)正常返回后線程的行為是未定義的。
              (d)系統(tǒng)不允許線程捕獲SIGKILL和SIGSTOP信號。
              (e)如果線程為SIGCHLD信號建立信號捕獲函數(shù),而該線程有未被等待的以終止的子線程時,沒有規(guī)定是否要生成SIGCHLD信號來指明那個子線程。

            每一種信號都被OSKit給予了一個符號名,對于32位的i386平臺而言,一個字32位,因而信號有32種。下面的表給出了常用的符號名、描述和它們的信號值。

            符號名  信號值 描述                是否符合POSIX
            SIGHUP  1   在控制終端上檢測到掛斷或控制線程死亡  是
            SIGINT  2   交互注意信號              是
            SIGQUIT  3   交互中止信號              是
            SIGILL  4   檢測到非法硬件的指令          是
            SIGTRAP  5   從陷阱中回朔              否
            SIGABRT  6   異常終止信號              是
            SIGEMT  7   EMT 指令                否
            SIGFPE  8   不正確的算術(shù)操作信號          是
            SIGKILL  9   終止信號                是
            SIGBUS  10   總線錯誤                否
            SIGSEGV  11   檢測到非法的內(nèi)存調(diào)用          是
            SIGSYS  12   系統(tǒng)call的錯誤參數(shù)           否
            SIGPIPE  13   在無讀者的管道上寫           是
            SIGALRM  14   報時信號                是
            SIGTERM  15   終止信號                是
            SIGURG  16   IO信道緊急信號             否
            SIGSTOP  17   暫停信號                是
            SIGTSTP  18   交互暫停信號              是
            SIGCONT  19   如果暫停則繼續(xù)             是
            SIGCHLD  20   子線程終止或暫停            是
            SIGTTIN  21   后臺線程組一成員試圖從控制終端上讀出  是
            SIGTTOU  22   后臺線程組的成員試圖寫到控制終端上   是
            SIGIO   23   允許I/O信號               否
            SIGXCPU  24   超出CPU時限               否
            SIGXFSZ  25   超出文件大小限制            否
            SIGVTALRM 26   虛時間警報器              否
            SIGPROF  27   側(cè)面時間警報器             否
            SIGWINCH 28   窗口大小的更改             否
            SIGINFO  29   消息請求                否
            SIGUSR1  30   保留作為用戶自定義的信號1        是
            SIGUSR2  31   保留作為用戶自定義的信號        是 

            注意:Linux信號機制基本上是從Unix系統(tǒng)中繼承過來的。早期Unix系統(tǒng)中的信號機制比較簡單和原始,后來在實踐中暴露出一些問題,因此,把那些建立在早期機制上的信號叫做"不可靠信號",信號值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信號都是不可靠信號。這就是"不可靠信號"的來源。它的主要問題是:進(jìn)程每次處理信號后,就將對信號的響應(yīng)設(shè)置為默認(rèn)動作。在某些情況下,將導(dǎo)致對信號的錯誤處理;因此,用戶如果不希望這樣的操作,那么就要在信號處理函數(shù)結(jié)尾再一次調(diào)用signal(),重新安裝該信號。



            另外,我再做一些補充,產(chǎn)生RST響應(yīng)以至于系統(tǒng)發(fā)出SIGPIPE信號,應(yīng)該分為兩種情況:

            1. 客戶端到服務(wù)端之間網(wǎng)絡(luò)斷掉,或者服務(wù)端斷電等,物理連接斷掉了,這種情況下客戶端不會退出,send函數(shù)正常執(zhí)行,不會感覺到自己出錯。因為由于物理網(wǎng)絡(luò)斷開,服務(wù)端不會給客戶端回應(yīng)錯誤消息,沒有RST響應(yīng),自然也不會產(chǎn)生SIGPIPE信號。但是當(dāng)服務(wù)端再恢復(fù)正常的時候,對客戶端send來的消息會產(chǎn)生RST響應(yīng),客戶端就收到SIGPIPE信號了,程序退出,但是這時send函數(shù)是能夠返回 -1的。可以進(jìn)行異常處理。

            2.客戶端到服務(wù)端的網(wǎng)絡(luò)能通,服務(wù)程序掛掉,客戶端程序會馬上退出,因為服務(wù)端能正常返回錯誤消息,客戶端收到,SIGPIPE信號就產(chǎn)生了。不過我不確定此時服務(wù)端返回是的RST響應(yīng),抓包來看沒有RST標(biāo)志。水平有限,只寫到這了。

            posted on 2008-09-10 22:49 葉子 閱讀(36876) 評論(0)  編輯 收藏 引用 所屬分類: UnixC\C++

            久久亚洲AV成人无码电影| 久久天堂电影网| 东方aⅴ免费观看久久av| 久久精品日日躁夜夜躁欧美| 久久无码人妻一区二区三区| 久久青青草原国产精品免费| 亚洲国产高清精品线久久| 精品国产青草久久久久福利| 91久久精品91久久性色| 色婷婷久久久SWAG精品| 久久久久亚洲AV成人片 | 久久精品无码av| AV无码久久久久不卡蜜桃| 久久综合综合久久狠狠狠97色88 | 久久精品国产亚洲网站| 亚洲国产成人久久综合野外 | 亚洲午夜久久久| 国产精品久久久天天影视| 久久91精品国产91久| 成人国内精品久久久久影院VR| 久久91精品国产91久| 久久精品成人免费国产片小草| 久久久国产精品亚洲一区 | .精品久久久麻豆国产精品| 一本久久精品一区二区| 99久久精品费精品国产| 国产三级久久久精品麻豆三级| 亚洲国产小视频精品久久久三级 | 国产精品无码久久久久| 久久se精品一区二区| 久久婷婷五月综合97色| 99精品久久精品一区二区| 一本久道久久综合狠狠躁AV| 久久亚洲视频| 香蕉99久久国产综合精品宅男自| 曰曰摸天天摸人人看久久久| 精品久久久久久亚洲精品| 久久精品亚洲一区二区三区浴池| 亚洲精品无码久久久久去q| 久久丫忘忧草产品| 久久久久久精品免费免费自慰|