1. 程式人生 > >Linux系統程式設計1.程序

Linux系統程式設計1.程序

1. 程序的概念

Linux作業系統是面向多使用者的,在同一時間可以有許多使用者向作業系統發出各種命令。在現代作業系統中,都有程式和程序的概念。

通俗的講:程式是一個包含可以執行程式碼的檔案,是一個靜態的檔案;而程序是一個開始執行但還沒有結束的程式例項,就是可執行檔案的具體實現。

一個程式可能有許多程序,而每一個程序又可以有許多的子程序,依次迴圈下去,而產生子孫程序。

當程式被系統呼叫到記憶體以後,系統會給程式分配一定的資源(記憶體,裝置等等)然後進行一系列的複雜操作,使程式變成程序以供系統呼叫。在系統裡面只有程序沒有程式,為了區分各個不同的程序,系統給每一個程序分配了一個 ID(就象我們的身份證)以便識別。

為了充分的利用資源,系統還對程序區分了不同的狀態:新建、執行、阻塞、就緒和完成一共五個狀態。

新建:表示程序正在被建立。
執行:程序正在執行。
阻塞:程序在等待某個事件發生。
就緒:系統在等待CPU來執行命令。
完成:程序已經結束了系統正在回收資源。

2. 程序的標誌

程序的ID(也叫PID),通過系統呼叫getpid可以得到程序的ID,而getppid可以得到父程序(建立呼叫該函式程序的程序)的ID。

#include <unistd>;
pid_t getpid(void);//獲得程序ID
pid_t getppid(void);//獲得父程序ID

程序 - >程式 - > 使用者

程序是為程式服務的,而程式為使用者服務,系統為了找到該程序的使用者名稱,還為程序和使用者建立聯絡。這個使用者稱為程序的所有者。相應的每一使用者也有一個使用者ID。通過系統呼叫getuid可以得到程序所有者的ID。由於程序要用到一些資源。而Linux對系統資源是進行保護的,為了獲取一定資源程序還有一個有效使用者ID,這個ID和系統的資源使用有關,涉及到程序的許可權。通過系統呼叫geteuid得到程序的有效使用者ID。

#include <unistd>;
#include <sys/types.h>;
uid_t getuid(void);//獲得使用者ID
uid_t geteuid(void
);//獲得有效使用者ID gid_t getgid(void);//獲得組ID git_t getegid(void);//獲得有效組ID

有時候我們還對使用者的其他資訊感興趣,呼叫getpwuid來得到。

struct passwd {
char *pw_name; /* 登入名稱 */
char *pw_passwd; /* 登入口令 */
uid_t pw_uid; /* 使用者 ID */
gid_t pw_gid; /* 使用者組 ID */
char *pw_gecos; /* 使用者的真名 */
char *pw_dir; /* 使用者的目錄 */
char *pw_shell; /* 使用者的 SHELL */
};
#include <pwd.h>;
#include <sys/types.h>;
struct passwd *getpwuid(uid_t uid);

例項:

#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <stdio.h>
int main(int argc,char ** argv){
    /*定 義資料 */
    pid_t my_pid,parent_pid;
    uid_t my_uid,my_euid;
    gid_t my_gid,my_egid;
    struct passwd *my_info;
    /*呼叫 系統函式*/
    my_pid=getpid();
    parent_pid=getppid();
    my_uid=getuid();
    my_euid=geteuid();
    my_gid=getgid();
    my_egid=getegid();
    my_info=getpwuid(my_uid);
    /*輸出 */
    printf("Process ID: %ld\n",my_pid);
    printf("Parent ID: %ld\n",parent_pid);
    printf("User ID: %ld\n",my_uid);
    printf("Effective User ID: %ld\n",my_euid);
    printf("Group ID: %ld\n",my_gid);
    printf("Effective Group ID: %ld\n",my_egid);
    if(my_info){
        printf("My Login Name: %s\n" ,my_info->pw_name);
        printf("My Password : %s\n" ,my_info->pw_passwd);
        printf("My User ID : %ld\n",my_info->pw_uid);
        printf("My Group ID : %ld\n",my_info->pw_gid);
        printf("My Real Name: %s\n" ,my_info->pw_gecos);
    }
    printf("My Home Dir : %s\n", my_info->pw_dir);
    printf("My Work Shell: %s\n", my_info->pw_shell);
}

3. 程序的建立

建立一個程序的系統呼叫很簡單,通過系統呼叫fork函式。

#include <unistd.h>;
pid_t fork();

當一個程序呼叫了fork以後,系統會自動建立一個子程序,這個子程序和父程序不同的地方只有他的程序ID和父程序ID,其他的都是一樣的,就像父程序克隆自己一樣。當然建立兩個一模一樣的程序是沒有意義的。為了區分父程序和子程序,必須跟蹤fork的返回值。當fork呼叫失敗的時候(記憶體不足或者是使用者的最大程序數已到)fork返回-1,否則fork返回值就是根據有重要作用。對於父程序fork返回子程序ID,而對於fork子程序返回0。

根據這個來區分父子程序。Linux 是一個多使用者作業系統,在同一時間會有許多的使用者在爭奪系統的資源。有時程序為了早一點完成任務就建立子程序來爭奪資源。一旦子程序被建立,父子程序一起從fork 處繼續執行,相互競爭系統的資源。有時候我們希望子程序繼續執行,而父程序阻塞直到子程序完成任務。這個時候我們可以呼叫 wait 或者 waitpid 系統呼叫.

#include <sys/types.h>;
#include <sys/wait.h>;
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid,int *stat_loc,int options);

wait系統呼叫會使父程序阻塞直到一個子程序結束或者父程序接受到了一個訊號,如果沒有父程序沒有子程序或者其他的子程序已經結束了wait會立即返回。成功時,wait將返回子程序的ID,否則返回-1,並設定全域性變數errno。stat_loc是子程序的退出狀態。子程序呼叫exit,_exit或者是return來設定這個值,為了得到這個值Linux定義了幾個巨集來測試這個返回值。

WIFEXITED(status):判斷子程序退出值是非 0.
WEXITSTATUS(status):判斷子程序的退出值(當子程序退出時非 0).
WIFSIGNALED(status):子程序由於有沒有獲得的訊號而退出.
WTERMSIG(status):子程序沒有獲得的訊號號(在 WIFSIGNALED 為真時才有意義).

waitpid等待指定的子程序直到子程序返回。如果 pid 為正值則等待指定的程序(pid)。如果為 0 則等待任何一個組 ID 和呼叫者的組 ID 相同的程序,為-1 時等同於 wait 呼叫,小於-1 時等待任何一個組 ID 等於 pid 絕對值的。stat_loc 和 wait 的意義一樣,options 可以決定父程序的狀態 。

optios可以取兩個值 :
WNOHANG:父程序立即返回當沒有子程序存在時 。
WUNTACHED:當子程序結束時 waitpid 返回,但是子程序的退出狀態不可得到。

父程序建立子程序後,子程序一般要執行不同的程式.為了呼叫系統程式,我們可以使用系統呼叫exec族呼叫。exec族呼叫有著5個函式:

#include <unistd.h>;
int execl(const char *path,const char *arg,...);
int execlp(const char *file,const char *arg,...);
int execle(const char *path,const char *arg,...);
int execv(const char *path,char *const argv[]);
int execvp(const char *file,char *const argv[]):
/*exec 族呼叫可以執行給定程式.關於 exec 族呼叫的詳細解說可以參考系統手冊(man execl). */

例項:

#include <unistd.h>;
#include <sys/types.h>;
#include <sys/wait.h>;
#include <stdio.h>;
#include <errno.h>;
#include <math.h>;
void main(void)
{
    pid_t child;
    int status;
    printf("描述怎樣獲得子程序的status\n");
    if((child=fork())==-1)
    {
        printf("Fork Error : %s\n",strerror(errno));
        exit(1);
    }
    else if(child==0) //判斷自己是不是子程序
    {
        int i;
        printf("I am the child: %ld\n",getpid());
        for(i=0;i<1000000;i++) sin(i);
        i=5;
        printf("I exit with %d\n",i);
        exit(i);
    }
    while(((child=wait(&status))==-1)&(errno==EINTR));
    /*等待一個子程序結束*/

    if(child==-1)/*子程序沒有結束wait()返回-1,否則返回子PID*/
        printf("Wait Error: %s\n",strerror(errno));
        /*strerror 函式會返回一個指定的錯誤號的錯誤資訊的字串.*/

    else if(!status)/*子程序的退出狀態為0*/
        printf("Child %ld terminated normally return status is zero\n",child);

    else if(WIFEXITED(status))/*利用巨集判斷子程序退出狀態非0*/
        printf("Child %ld terminated normally return status is %d\n",child,WEXITSTATUS(status));

    else if(WIFSIGNALED(status))/*利用巨集判斷子程序沒有獲得訊號*/
        printf("Child %ld terminated due to signal %d znot caught\n",child,WTERMSIG(status));
}

4. 守護程序的建立

由於 Linux 是多工作業系統,我們就是不編寫程式碼也可以把一個程式放到後臺去執行的。我們只要在命令後面加上&符號 SHELL 就會把我們的程式放到後臺去執行的.。這裡我們”開發”一個後臺檢查郵件的程式。這個程式每個一個指定的時間回去檢查我們的郵箱,如果發現我們有郵件了,會不斷的報警。後面有這個函式的加強版本加強版本後臺程序的建立思想: 首先父程序建立一個子程序,然後子程序殺死父程序。 訊號處理所有的工作由子程序來處理。

#include <unistd.h>;
#include <sys/types.h>;
#include <sys/stat.h>;
#include <stdio.h>;
#include <errno.h>;
#include <fcntl.h>;
#include <signal.h>;
/* Linux 的默任個人的郵箱地址是 /var/spool/mail/使用者的登入名 */
#define MAIL "/var/spool/mail/hoyt"
/* 睡眠 10 秒鐘 */
#define SLEEP_TIME 10
main(void)
{
    pid_t child;
    if((child=fork())==-1)/*建立程序錯誤*/
    {
        printf("Fork Error: %s\n",strerror(errno));
        exit(1);
    }
    else if(child>0) while(1);/*如果自己不是子程序則等待被殺死*/
    if(kill(getppid(),SIGTERM)==-1)/*kill父程序錯誤*/
    {
        printf("Kill Parent Error: %s\n",strerror(errno));
        exit(1);
    }
    {/*自己是子程序*/
        int mailfd;
        long i=0;
        while(1)
        {
            printf("我是子程序,我在監聽郵箱:%ld\r\n",i++);
            if((mailfd=open(MAIL,O_RDONLY))!=-1)
            {
                fprintf(stderr,"%s","\007");
                close(mailfd);
            }
            if(i==9999)i=0;
            sleep(SLEEP_TIME);
        }
    }
}

可以在預設的路徑下建立你的郵箱檔案,然後測試一下這個程式。當然這個程式還有很多地方要改善的。