1. 程式人生 > >linux 程序常用概念

linux 程序常用概念

當執行任何一個UNIX命令時,shell至少會建立一個程序來執行這個命令,所以可以把任何在UNIX系統中執行的程式叫做程序;但是程序並不是程式,程序是動態的,而程式是靜態的,並且多個程序可以併發的呼叫同一個程式。

   系統中每一個程序都包含一個task_struct資料結構,所有指向這些資料結構的指標組成一個程序向量陣列,系統預設的程序向量資料大小是512,表示系統中可同時容納512個程序。程序的task_struct資料結構包括了程序的狀態、排程資訊、程序識別符號等資訊。

   由於UNIX系統是一個多程序的作業系統,所以每一個程序都是獨立的,都有自己的許可權及任務,所以當某一程序失敗時並不會導致別的程序失敗。系統通過程序識別符號來區分不同的程序,程序識別符號是一個非負正數,他在任何時刻都是唯一的,當某個程序結束時,他的程序識別符號可以分配給另外一個新程序。系統將識別符號0分配給排程程序,識別符號1分配給初始化程序。


   程序在執行期間,會用到很多資源,包括最寶貴的CPU資源,當某一個程序佔用CPU資源時,別的程序必須等待正在執行的程序空閒CPU後才能執行,由於存在很多程序在等待,所以核心通過排程演算法來決定將CPU分配給哪個程序。

   系統在剛剛啟動時,運行於核心方式,這時候只有一個初始化程序在執行,他首先做系統的初始化,然後執行初始化程式(一般是/sbin/init)。初始化程序是系統的第一個程序,以後所有的程序都是初始化程序的子程序。

  2、程序的建立

   程序由fock函式建立,在unistd.h庫中定義如下:

    #include 
    pid_t fock(void);

   fock函式呼叫一次卻返回兩次;在父程序中返回子程序的ID,在程序中返回0,這是因為父程序可能存在很多過子程序,所以必須通過這個返回的子程序ID來跟蹤子程序,而子程序只有一個父程序,他的ID可以通過getppid取得。先面程式建立一個子程序:


   #include 
    #include 
    main() {
     pid_t pid;
     pid = fork();
     if(!pid)
      printf("this is child ");
     else if (pid>0)
      printf("this is parent,child has pid %d ",pid);
     else
      printf("fork fail ");
    }

   呼叫fock建立子程序後,父程序中所有開啟的描述字在子程序中是共享的,這個特性在網路伺服器中廣泛使用,例如父程序通過socket函式返回一個套接字,然後呼叫fock函式建立字程序,這個子程序就可以直接對這個已經存在的套接字進行操作。fock的另一個典型應用是建立一個子程序呼叫exec函式來代替自己去執行新的程式。


  3、程序的執行

   UNIX系統執行以檔案形式儲存在磁碟上的可執行程式的唯一方法是用一個現有的程序去呼叫六個exec函式之一,六個exec函式定義如下:

   #include 
    int execl(const char *pathname,const char *arg0,.../* (char *)0*/);
    int execv(const char *pathname,char *const argv[]);
    int execle(const char *pathname,const char *arg0,... / (char *)0,
   char *const envp[]*/);
    int execve(const char * pathname,char *const argv[],char *const envp[]);
    int execlp(const char *filename,const char *arg0,.../* (char *)0*/);
    int execvp(const char *filename,char *const argv[]);

   在以上六個exec函式中,第一個引數如果為pathname,則說明被執行程式是由路徑名指定,如果為filename,則說明是由檔名指定;第二個引數如果為陣列,說明被執行程式的引數是由一個數組來索引(陣列必須含有一個空指標來表示結束),否則需要將引數一一列出;execle()及execve()的envp指標陣列表示這兩個函式顯示指定一個環境表(這個陣列必須以一個空指標結束),而另外四個函式則用外部變數environ的當前值來建立一個環境表傳遞給新程式。

   exec函式把新程式裝入呼叫程序的記憶體空間,來改變呼叫程序的執行程式碼。當exec函式執行成功,呼叫程序將被覆蓋,相當於產生一個新程序,但是新程序的程序表識符號和呼叫程序的程序表識符相同,所以exec並沒象fock函式一樣建立一個新程序,而是用新程序取代了呼叫程序。下面是一個fork()結合exec()的程式:

   #include 
    #include 
    main() {
     int pid;
     pid = fork();
     switch(pid) {
      case -1:
          perror("fork failed");
          exit(1);
      case 0:
          execl("/bin/ls","ls","-l",NULL);
          perror("execl failed");
          exit(1);
     }
    }


  二、守護程序

  1、什麼是守護程序

   守護程序是脫離於終端並且在後臺執行的程序。守護程序脫離於終端是為了避免程序在執行過程中的資訊不在任何終端上顯示並且程序也不會被任何終端所產生的終端資訊所打斷。

  2、守護程序的啟動

   守護程序一般可以通過以下方式啟動:

  A:在系統啟動時由啟動指令碼啟動,這些啟動指令碼通常放在/etc/rc.d目錄下 B:利用inetd超級伺服器啟動,大部分網路服務都是這樣啟動的,如ftp、telnet等 C:另外,由cron定時啟動以及在終端用nohup啟動的程序也是守護程序 
  3、守護程序的錯誤輸出

   由於守護程序是脫離於任何終端的,所以他的任何資訊都無法直接輸出到標準輸出裝置和標準錯誤輸出裝置中,在Linux系統中提供了syslog()系統呼叫來解決這個問題。syslog()在shslog.h定義如下:

   #include 
   void syslog(int priority,char *format,...);

   其中引數priority指明瞭程序要寫入資訊的等級和用途。等級見表1,用途見表2:


  第二個引數是一個格式串,指定了記錄輸出的格式,在這個串的最後需要指定一個%m,對應errno錯誤碼。

  4、守護程序的建立

   首先fock一個子程序並關閉父程序,如果此程序是作為一個shell命令在控制檯執行的,當父程序被終止時,shell就認為該命令已經結束,這時子程序自動轉為後臺執行,由於子程序在父程序那裡繼承了組識別符號同時又擁有自己的程序識別符號,就保證了子程序不是一個程序組的首程序;接著呼叫setsid()建立一個新程序組,呼叫程序成為該程序組的首程序,這就使該程序脫離於原終端;接下來重新呼叫fock(),使程序不再是程序組的首程序,從而忽略SIGHUP訊號,以避免在某些情況下程序以外開啟終端而重新與終端發生聯絡;最後改變工作目錄,關閉全部已經開啟的檔案控制代碼,並開啟log系統,到此守護程序成功建立。