日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

《嵌入式linux应用程序开发标准教程》笔记——7.进程控制开发

發(fā)布時(shí)間:2024/8/26 linux 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《嵌入式linux应用程序开发标准教程》笔记——7.进程控制开发 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  進(jìn)程是系統(tǒng)資源的最小單元,很重要。

?

 7.1 linux進(jìn)程的基本概念

  • 定義:一個(gè)程序的一次執(zhí)行過(guò)程,同時(shí)也是資源分配的最小單元。程序是靜態(tài)的,而進(jìn)程是動(dòng)態(tài)的。
  • 進(jìn)程控制塊:linux系統(tǒng)用進(jìn)程控制塊描述進(jìn)程,task_struct,在 include/linux/sched.h
  • PID,進(jìn)程唯一標(biāo)識(shí);PPID,父進(jìn)程的PID
#include <unistd.h>/* Get the process ID of the calling process. */ extern __pid_t getpid (void) __THROW;/* Get the process ID of the calling process's parent. */ extern __pid_t getppid (void) __THROW;

?

  進(jìn)程相關(guān)的還有用戶和用戶組標(biāo)識(shí)、進(jìn)程時(shí)間、資源利用的函數(shù),參考APUE.

  • 進(jìn)程運(yùn)行的狀態(tài)

?

  • ?進(jìn)程的結(jié)構(gòu):主要包含數(shù)據(jù)段、代碼段、堆棧段
  • 進(jìn)程模型:用戶態(tài)和內(nèi)核態(tài)
  • linux啟動(dòng)進(jìn)程的兩種方式:?
    • 手動(dòng)啟動(dòng):前臺(tái)啟動(dòng),最常見(jiàn)的是在終端里輸入命令,該命令的執(zhí)行就是一個(gè)進(jìn)程; 后臺(tái)啟動(dòng),用&,不影響終端,在終端后面默默運(yùn)行。
    • 調(diào)度啟動(dòng):制定時(shí)間運(yùn)行,有一些命令,at命令可以在指定時(shí)刻執(zhí)行相關(guān)進(jìn)程;cron命令可以自動(dòng)周期性的執(zhí)行相關(guān)進(jìn)程。

  常用進(jìn)程相關(guān)命令:

  

?

 7.2 linux進(jìn)程編程

? 7.2.1 fork

  • 從已創(chuàng)建的進(jìn)程中創(chuàng)建一個(gè)新的進(jìn)程,新進(jìn)程叫子進(jìn)程,原進(jìn)程叫父進(jìn)程。
  • 子進(jìn)程是父進(jìn)程的復(fù)制,集成父進(jìn)程的絕大部分內(nèi)容,包括:整個(gè)進(jìn)程的地址空間、進(jìn)程上下文、代碼段、進(jìn)程堆棧、內(nèi)存信息、打開(kāi)的文件描述符、信號(hào)控制設(shè)置、進(jìn)程優(yōu)先級(jí)、進(jìn)程組號(hào)、當(dāng)前工作目錄、根目錄、資源限制、控制終端等;
  • 子進(jìn)程獨(dú)有的部分:進(jìn)程號(hào)、資源使用、計(jì)時(shí)器等
  • fork的大體流程:父進(jìn)程執(zhí)行fork——>父進(jìn)程復(fù)制出一個(gè)子進(jìn)程——>父子進(jìn)程從fork函數(shù)返回開(kāi)始分別在兩個(gè)地址空間同時(shí)運(yùn)行,通過(guò)返回值區(qū)分父子進(jìn)程
  • fork的開(kāi)銷比較大,復(fù)制這么多東西,想想都覺(jué)得累。有些unix系統(tǒng)創(chuàng)建了vfork()函數(shù),vfork創(chuàng)建新進(jìn)程時(shí),不產(chǎn)生父進(jìn)程的副本,允許父子進(jìn)程訪問(wèn)相同的物理內(nèi)存而偽裝成拷貝父進(jìn)程。但是子進(jìn)程需要改變內(nèi)存時(shí)(寫),才復(fù)制父進(jìn)程,這就是“寫時(shí)復(fù)制”,linux的fork就是調(diào)用vfork函數(shù)實(shí)現(xiàn)的。
#include <sys/types> // pid_t #include <unistd.h>pid_t fork( void );
參數(shù):
返回值:
  0:子進(jìn)程
  >0:子進(jìn)程pid,父進(jìn)程
  -1:出錯(cuò)
注意事項(xiàng):
  fork調(diào)用一次,就創(chuàng)建一個(gè)子進(jìn)程,所以if、else if等分支處理時(shí),不能多次調(diào)用,應(yīng)該調(diào)用一次,記下返回值,然后if else等使用此返回值。

?

?

/* 7-1,fork */#include <stdio.h> // printf #include <stdlib.h> // exit #include <unistd.h> #include <fcntl.h> // open,fcntl #include <sys/types.h>int main(int args, char *argv[]) {pid_t pid_rtn;pid_rtn = fork(); if( pid_rtn == 0 ){printf("\r\nChild thread, pid %d, ppid %d",getpid(),getppid());}else if( pid_rtn > 0 ){sleep(1); // 如果父進(jìn)程先結(jié)束,則子進(jìn)程會(huì)被init進(jìn)程收養(yǎng),用getppid時(shí)獲取的就不是創(chuàng)建他的父進(jìn)程了printf("\r\nParent thread, pid %d, child %d",getpid(),pid_rtn);}else{ printf("\r\nfork err.");}printf("\r\nfinish.\r\n");exit(0); }

$ ./example
Child thread, pid 4087, ppid 4086
finish.

Parent thread, pid 4086, child 4087
finish.

如果父進(jìn)程不睡1s,則運(yùn)行結(jié)果如下:
$ ./example

Parent thread, pid 4121, child 4122
finish.
$
Child thread, pid 4122, ppid 2326
finish.

$ ps -A
*
2326 ???????? 00:00:01 upstart    // upstart就是ubuntu的init進(jìn)程,對(duì)于父進(jìn)程已經(jīng)結(jié)束的子進(jìn)程,會(huì)被這個(gè)進(jìn)程“收養(yǎng)”
*

  7.2.2 exec函數(shù)族

  • 執(zhí)行另一個(gè)程序,除了pid外,其他全被新的進(jìn)程替換
  • 一般先f(wàn)ork,然后exec執(zhí)行想執(zhí)行的程序
  • exec注意事項(xiàng):一定要加上錯(cuò)誤判斷語(yǔ)句,exec很容易出錯(cuò),常見(jiàn)錯(cuò)誤有:
    •   找不到文件或路徑,errno=ENOENT
    •   argv和envp忘記用NULL結(jié)束,errno=EFAULT;
    • ? ? ? 沒(méi)有對(duì)應(yīng)可執(zhí)行文件的運(yùn)行權(quán)限,errno=EACCES
  • 6個(gè)函數(shù)中,真正的系統(tǒng)調(diào)用只有execve,其他都是庫(kù)函數(shù),通過(guò)調(diào)用execve實(shí)現(xiàn)
#include <unistd.h>

int execl(const char *path, const char *arg, ...)   // list
int execv(const char *path, char *const argv[])    // vector
int execle(const char *path, const char *arg, ..., char *const envp[])  // enviroment
int execve(const char *path, char *const argv[], char *const envp[])
int execlp(const char *file, const char *arg, ...)
int execvp(const char *file, char *const argv[])

參數(shù):
  path和file:查找方式,path完整的文件目錄路徑;file(p結(jié)尾的函數(shù))只給出文件名,系統(tǒng)按照環(huán)境變量PATH指定的路徑查找;
  arg...和argv[]:參數(shù)傳遞方式,list和vector,這些參數(shù)必須以NULL結(jié)尾,以可執(zhí)行程序命令本身開(kāi)頭;
  envp:環(huán)境變量,e結(jié)尾,指定要執(zhí)行的進(jìn)程所使用的環(huán)境變量

返回值:-1 出錯(cuò)

?

?

/* 7-2,exec */

#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <unistd.h>
#include <fcntl.h> // open,fcntl
#include <sys/types.h>

int main(int args, char *argv[])
{
pid_t pid_rtn;

pid_rtn = fork();
if( pid_rtn == 0 )
{
execlp("ps","ps","-A","NULL");  // 第一個(gè)ps是文件名,后面是參數(shù),輸入?yún)?shù)時(shí),第一個(gè)參數(shù)是要運(yùn)行的程序,跟在shell里輸入是一樣的,注意要用NULL結(jié)尾
}

printf("\r\nfinish.\r\n");
exit(0);
}

?

相當(dāng)于執(zhí)行了“ps -A”命令,運(yùn)行結(jié)果:

PID TTY STAT TIME COMMAND
1 ? Ss 0:12 /sbin/init splash
2 ? S 0:00 [kthreadd]

......

?

?7.2.3 exit和_exit

  • 兩個(gè)函數(shù)會(huì)停止所有操作,清除PCB等數(shù)據(jù)結(jié)構(gòu);
  • 兩個(gè)函數(shù)有差別:
    •   _exit:直接停止運(yùn)行, 清除進(jìn)程使用的內(nèi)存空間,清除內(nèi)核中的數(shù)據(jù)結(jié)構(gòu);
    • ? ? ?exit = “清理IO緩存”+_exit, 清理IO緩存,指檢查文件的打開(kāi)情況,把文件緩沖區(qū)中的內(nèi)容寫回文件。linux里有“緩沖IO”操作,例如printf、fgets等,使用緩沖區(qū),類似cache。
    • ?只使用exit()就可以了

進(jìn)程調(diào)用exit()和_exit()后不會(huì)立即退出,而是進(jìn)入僵死zombie狀態(tài),變成僵尸進(jìn)程,僵尸進(jìn)程只在進(jìn)程列表里保留一個(gè)位置,記錄該進(jìn)程的退出狀態(tài)等供其他進(jìn)程收集(一般是父進(jìn)程用wait收集)。

#include <unistd.h>  // _exit
#include <stdlib.h>  // exit

void exit( int status );
void _exit( int status);

參數(shù):
  status 可以返回本進(jìn)程(調(diào)用exit的進(jìn)程)的退出狀態(tài),一般0表示正常,其他數(shù)值表示出錯(cuò),進(jìn)程非正常結(jié)束;
  父進(jìn)程用wait()系統(tǒng)調(diào)用接收子進(jìn)程的返回值。

?

#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <unistd.h>

int main(int args, char *argv[])
{
  printf("Start.\n");
  printf("content in buffer.");
  _exit(0);
}

$ ./example     // 在緩沖區(qū)里就沒(méi)有了,因?yàn)開(kāi)exit不刷緩沖區(qū)
Start.

?

#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <unistd.h>

int main(int args, char *argv[])
{
  printf("Start.\n");
  printf("content in buffer.");
  exit(0);
}

$ ./example      // 在緩沖區(qū)里的也刷出來(lái)了,exit干的
Start.
content in buffer.

?

【注意】

printf遇到“\n”換行符時(shí)自動(dòng)從緩沖區(qū)中將記錄讀出?

  7.2.4 wait和waitpid

  •  wait阻塞等待1個(gè)子進(jìn)程結(jié)束,如果該進(jìn)程在阻塞時(shí)接到了一個(gè)指定的信號(hào),則阻塞也可能終止。如果沒(méi)有子進(jìn)程或者子進(jìn)程已經(jīng)結(jié)束,則wait會(huì)立即返回。
  • ? ?waitpid比wait功能豐富,可提供非阻塞、作業(yè)控制、指定待等待進(jìn)程等功能
#include <sys/types.h>
#include <sys/wait.h>

pid_t wait( int * status );
參數(shù):
  status:返回子進(jìn)程的退出狀態(tài)和異常終止?fàn)顟B(tài),若為NULL,則不獲取。可以通過(guò)一些linux特定的宏來(lái)測(cè)試具體狀態(tài)信息。
       【重要】:進(jìn)程退出有正常退出(子進(jìn)程exit或者return),此時(shí)的狀態(tài)記為“正常退出狀態(tài)”;還有異常退出的情況,例如被信號(hào)中斷等,這時(shí)的狀態(tài)記為“異常終止?fàn)顟B(tài)”。status可以反映這兩種狀態(tài)?!         ?/span>

        union wait
        {
          int w_status;
          struct
          {
            # if __BYTE_ORDER == __LITTLE_ENDIAN
            unsigned int __w_termsig:7; /* Terminating signal. */
            unsigned int __w_coredump:1; /* Set if dumped core. */
            unsigned int __w_retcode:8; /* Return code if exited normally. */
            unsigned int:16;
            # endif
          } __wait_terminated;   // 正常退出和異常終止,格式


          struct
          {
            # if __BYTE_ORDER == __LITTLE_ENDIAN
            unsigned int __w_stopval:8; /* W_STOPPED if stopped. */
            unsigned int __w_stopsig:8; /* Stopping signal. */
            unsigned int:16;
            # endif
          } __wait_stopped;   // 暫停,stop,格式
        };

           常用宏:            
           WIFEXITED(status),若為正常退出,則返回真。若為真,可用WEXITSTATUS(status)獲取exit返回的狀態(tài);
           WIFSIGNALED(status),若子程序?yàn)楫惓=K止,則返回真(被信號(hào)終止),可用WTERMSIG(status)獲取子進(jìn)程終止的信號(hào)編號(hào);可用WCOREDUMP(status)檢查是否產(chǎn)生core文件,產(chǎn)生時(shí)為真;
           WIFSTOPPED(status),如果子程序暫停,則為真,可通過(guò)WSTOPSIG(status),獲取使子程序暫停的信號(hào)編號(hào)
           WIFCONTINUED(status),若暫停后又繼續(xù)的子進(jìn)程返回狀態(tài),則為真,僅用于waitpid。

            #define WIFEXITED(status) (((status) & 0x7f)== 0)
            #define?WIFSIGNALED(status) (((signed char) (((status) & 0x7f) + 1) >> 1) > 0)
            #define?WIFSTOPPED(status) (((status) & 0xff) == 0x7f)

            #define?WEXITSTATUS(status)(((status) & 0xff00) >> 8)
            #define?WTERMSIG(status) ((status) & 0x7f)
            #define?WSTOPSIG(status) (((status) & 0xff00) >> 8)

          
返回值:
  成功:已結(jié)束運(yùn)行(被等待的)的子進(jìn)程的進(jìn)程號(hào)
  失敗:-1

pid_t waitpid( pid_t pid, int *status, int options );
參數(shù):
  pid: >0,等待進(jìn)程ID=pid的子進(jìn)程,不管別的;
     =-1,等待任何一個(gè)子進(jìn)程,與wait()作用一樣;
     = 0,等待“ID==調(diào)用進(jìn)程組ID”的任一子進(jìn)程;
     <-1,等待“ID==pid絕對(duì)值”的任一子進(jìn)程
  status:同wait()函數(shù)
  options:sya
     WNOHANG:不阻塞
     WUNTRACED:若實(shí)現(xiàn)某支持作業(yè)控制,則由 pid 指定的任一子進(jìn)程狀態(tài)已暫停,且其狀態(tài)自暫停以來(lái)還未報(bào)告過(guò),則返回其狀態(tài)
     0:同wait(),阻塞
  返回值:
     正常:已結(jié)束運(yùn)行的子進(jìn)程的進(jìn)程號(hào)
     使用WNOHANG且沒(méi)有子進(jìn)程:0
     調(diào)用出錯(cuò):-1

【注意】
1. 關(guān)于幾個(gè)測(cè)試退出狀態(tài)的特殊的宏

?

/* 7-4,waitpid */

#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <unistd.h>
#include <sys/types.h> // pid_t
#include <sys/wait.h>

int main(int args, char *argv[])
{
  pid_t pid_fork,pid_wait;
  int status;

  pid_fork=fork();
  if( pid_fork == 0 ) // child
  {
    sleep(5);
    exit(0);
  }
  else if( pid_fork > 0 )
  {
    do
    {
      pid_wait = waitpid(pid_fork,&status,WNOHANG);
      if( pid_wait != pid_fork )
        printf("child thread %d is not over\r\n",pid_fork);
      else
        printf("child thread %d is over,status 0x%x\r\n",pid_fork,status);
      sleep(1);
    }
    while(pid_wait!=pid_fork);
  }
  else
  {
    printf("fork err code %d.\r\n",pid_fork);
  }

  exit(0);
}

?

$ ./example    

child thread 7575 is not over
child thread 7575 is not over
child thread 7575 is not over
child thread 7575 is not over
child thread 7575 is not over
child thread 7575 is over,status 0x0

?

$ ./example     // 子進(jìn)程exit(-1)時(shí),waitpid的獲取的值是0xff00,高位是exit的返回值,需要用到宏了
child thread 7605 is not over
child thread 7605 is not over
child thread 7605 is not over
child thread 7605 is not over
child thread 7605 is not over
child thread 7605 is over,status 0xff00

?

?

? 7.3 守護(hù)進(jìn)程

 7.3.1 守護(hù)進(jìn)程概念

    守護(hù)進(jìn)程,也叫deamon進(jìn)程,是后臺(tái)服務(wù)進(jìn)程;系統(tǒng)引導(dǎo)載入時(shí)啟動(dòng),系統(tǒng)關(guān)閉時(shí)終止,獨(dú)立于控制終端;常用于周期性的執(zhí)行某種任務(wù)或等待處理某些事件。守護(hù)進(jìn)程已d結(jié)尾,例如crond、lpd等。

    控制終端:系統(tǒng)與用戶進(jìn)行交流的界面稱為終端,每個(gè)從此終端開(kāi)始運(yùn)行的進(jìn)程都會(huì)依賴這個(gè)終端,這個(gè)終端就是這些進(jìn)程的控制終端??刂平K端關(guān)閉時(shí),相應(yīng)的進(jìn)程都會(huì)關(guān)閉。但是守護(hù)進(jìn)程不受影響。

    守護(hù)進(jìn)程不受終端、用戶和其他變化的影響,直到系統(tǒng)關(guān)閉時(shí)才退出。

? ?7.3.2 編寫守護(hù)進(jìn)程

?步驟:

??7.3.2.1?創(chuàng)建子進(jìn)程,父進(jìn)程退出

   父進(jìn)程退出后,子進(jìn)程編程了孤兒進(jìn)程,被init進(jìn)程收養(yǎng)。 形式上做到了與控制終端的脫離?!  ?/p>

!!!7.3.2.2 在子進(jìn)程中創(chuàng)建新會(huì)話

   先了解基本概念:進(jìn)程組、會(huì)話組、會(huì)話期

   進(jìn)程組:一個(gè)或多個(gè)進(jìn)程的集合,每個(gè)進(jìn)程組都有一個(gè)組長(zhǎng)進(jìn)程,進(jìn)程組ID=組長(zhǎng)PID

   會(huì)話組:一個(gè)或多個(gè)進(jìn)程組的集合

   會(huì)話期:通常一個(gè)會(huì)話開(kāi)始于用戶登錄,終止與用戶退出,在此期間該用戶運(yùn)行的所有進(jìn)程都屬于這個(gè)會(huì)話期。

? ? ? ??

    

setsid():創(chuàng)建新的會(huì)話,并擔(dān)任該會(huì)話組的組長(zhǎng)。調(diào)用后起到3個(gè)作用:
  • 讓進(jìn)程擺脫原會(huì)話的控制;
  • 讓進(jìn)程擺脫原進(jìn)程組的控制
  • 讓進(jìn)程擺脫原控制終端的控制
總之,跟之前的控制終端、進(jìn)程組、會(huì)話組都沒(méi)有關(guān)系了。使進(jìn)程完全獨(dú)立出來(lái),從而擺脫所有其他進(jìn)程的控制

#include <sys/types.h>
#include <unistd.h>

pid_t setsid( void );
返回值:
  成功:該進(jìn)程組ID
  出錯(cuò):-1

?

7.3.2.3 改變當(dāng)前目錄為根目錄

  通常的做法是將守護(hù)進(jìn)程的當(dāng)前目錄設(shè)置為根目錄,用chdir()系統(tǒng)調(diào)用。

7.3.2.4 重設(shè)文件權(quán)限掩碼

  umask(0),基本思路是給最大權(quán)限。

7.3.2.5 關(guān)閉文件描述符

  父進(jìn)程那繼承來(lái)的文件描述符,一般不用,浪費(fèi),關(guān)閉。 連基本的輸入輸出都沒(méi)用了,setsid時(shí)已經(jīng)失去聯(lián)系了,可以關(guān)了。

/* 7-5,deamon */#include <stdio.h> // printf #include <stdlib.h> // exit #include <unistd.h> #include <sys/types.h> // pid_t #include <fcntl.h>int main(int args, char *argv[]) {pid_t pid_fork;int i;int fd;char buf[32]="The deamon info.\n";pid_fork = fork();if( pid_fork < 0 ){printf("fork err.\r\n");}else if( pid_fork > 0 ){exit(0);}// only child enter setsid(); chdir("/");umask(0);for( i=0;i<getdtablesize();i++ ) // 終端也關(guān)了,printf沒(méi)有效果了,需要用別的調(diào)試方法 close(i);if( fd=open("/tmp/log", O_RDWR|O_CREAT,0644) < 0 )printf("open file err\r\n"); while(1){write(fd,buf,sizeof(buf));sleep(1); }exit(0); }

?

7.3.3 守護(hù)進(jìn)程的出錯(cuò)處理

  printf不好使,咋辦?用linux提供的syslog服務(wù),系統(tǒng)中有syslogd守護(hù)進(jìn)程。不通版本linux的syslog日志文件的位置可能不通。

  

#include <syslog.h>

void openlog( char * ident, int options, int facility );
參數(shù):
  ident:向每個(gè)消息加入的字符串,通常為程序的名稱;
  option:LOG_CONS,如果消息無(wú)法送到系統(tǒng)日志服務(wù),則直接輸出到系統(tǒng)控制終端
       LOG_NDELAY:立即打開(kāi)系統(tǒng)日志服務(wù)的連接。在正常情況下,直接發(fā)送到第一條消息時(shí)才打開(kāi)連接
       LOG_PERROR:將消息也同時(shí)送到 stderr 上        LOG_PID:在每條消息中包含進(jìn)程的 PID

  facility: 指定程序發(fā)送的消息類型
       LOG_AUTHPRIV:安全/授權(quán)信息
       LOG_CRON:時(shí)間守護(hù)進(jìn)程(cron at
       LOG_DAEMON:其他系統(tǒng)守護(hù)進(jìn)程
       LOG_KERN:內(nèi)核信息
       LOG_LOCAL[0~7]:保留
       LOG_LPR:行打印機(jī)子系統(tǒng)
       LOG_MAIL:郵件子系統(tǒng)
       LOG_NEWS:新聞子系統(tǒng)
       LOG_SYSLOGsyslogd 內(nèi)部所產(chǎn)生的信息函數(shù)傳入值
       LOG_USER:一般使用者等級(jí)信息
       LOG_UUCPUUCP 子系統(tǒng)

void syslog(int priority, char *format, ...)
參數(shù): priority ,指定消息的重要性,       LOG_EMERG:系統(tǒng)無(wú)法使用
      LOG_ALERT:需要立即采取措施
      LOG_CRIT:有重要情況發(fā)生
      LOG_ERR:有錯(cuò)誤發(fā)生
      LOG_WARNING:有警告發(fā)生
      LOG_NOTICE:正常情況,但也是重要情況
      LOG_INFO:信息消息
      LOG_DEBUG:調(diào)試信息
    format,同printf

void closelog( void )

?

  

  

?

轉(zhuǎn)載于:https://www.cnblogs.com/liuwanpeng/p/6605985.html

總結(jié)

以上是生活随笔為你收集整理的《嵌入式linux应用程序开发标准教程》笔记——7.进程控制开发的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。