天天看點

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

标簽(空格分隔):20135321餘佳源

第十二周(11.23-11.29):

學習計時:共6小時

讀書:3

代碼:1

作業:1

部落格:1

一、學習目标

  • 掌握程序控制
  • 掌握信号處理的方法
  • 掌握管道和fifo進行程序間通信的方法

二、學習資源

編譯、運作、閱讀、了解process.tar.gz壓縮包中的代碼

exec1

#include <stdio.h>
#include <unistd.h>

int main()
{
    char    *arglist[3];

    arglist[0] = "ls";
    arglist[1] = "-l";
    arglist[2] = 0 ;//NULL
    printf("* * * About to exec ls -l\n");
    execvp( "ls" , arglist );
    printf("* * * ls is done. bye");

    return 0;
}
           

可以看到這個代碼中用了execvp函數。

表頭檔案:

#include

定義函數:

int execvp(const char file ,char const argv []);
           

exec1.c編譯并運作程式結果如下:

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

execvp()會從PATH 環境變量所指的目錄中查找符合參數file 的檔案名,找到後便執行該檔案,然後将第二個參數argv傳給該欲執行的檔案。

如果執行成功則函數不會傳回,執行失敗則直接傳回-1,失敗原因存于errno中。

可以看到,exevp函數調用成功沒有傳回,是以沒有列印出“* * * ls is done. bye”這句話。

exec2

exec2與exec1的差別就在于,execvp函數調用的語句變成了

execvp( arglist[0] , arglist );

不過由定義可得這兩個等價,是以運作結果是相同的。

編譯運作結果與exec1.c完全相同,說明arglist數組的第一項為要運作的程式的名稱。

exec3

代碼如下:

#include <stdio.h>
#include <unistd.h>

int main()
{
    char    *arglist[3];
    char*myenv[3];
    myenv[0] = "PATH=:/bin:";
    myenv[1] = NULL;

    arglist[0] = "ls";
    arglist[1] = "-l";
    arglist[2] = 0 ;
    printf("* * * About to exec ls -l\n");

    execlp("ls", "ls", "-l", NULL);
    printf("* * * ls is done. bye\n");
}
           

這個代碼裡使用了execlp函數,用法如下:

頭檔案:

#include

定義函數:

int execlp(const char * file,const char * arg,....);

函數說明:

execlp()會從PATH 環境變量所指的目錄中查找符合參數file的檔案名,找到後便執行該檔案,然後将第二個以後的參數當做該檔案的argv[0]、argv[1]……,最後一個參數必須用空指針(NULL)作結束。如果用常數0來表示一個空指針,則必須将它強制轉換為一個字元指針,否則将它解釋為整形參數,如果一個整形數的長度與char * 的長度不同,那麼exec函數的實際參數就将出錯。如果函數調用成功,程序自己的執行代碼就會變成加載程式的代碼,execlp()後邊的代碼也就不會執行了.

傳回值:
如果執行成功則函數不會傳回,執行失敗則直接傳回-1,失敗原因存于errno 中。
           

也就是說,這個代碼指定了環境變量,然後依然執行了ls -l指令,成功後沒有傳回,是以最後一句話不會輸出。運作結果同exec1.

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

forkdemo1

這個代碼先是列印程序pid,然後調用fork函數生成子程序,休眠一秒後再次列印程序id,這時父程序列印子程序pid,子程序傳回0。

#include    <stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
    int ret_from_fork, mypid;
    mypid = getpid();              
    printf("Before: my pid is %d\n", mypid);
    ret_from_fork = fork();
    sleep(1);
    printf("After: my pid is %d, fork() said %d\n",
            getpid(), ret_from_fork);

    return 0;
}
           

在Unix/Linux中用fork函數建立一個新的程序。程序是由目前已有程序調用fork函數建立,分叉的程序叫子程序,建立者叫父程序。該函數的特點是調用一次,傳回兩次,一次是在父程序,一次是在子程序。兩次傳回的差別是子程序的傳回值為0,父程序的傳回值是新子程序的ID。子程序與父程序繼續并發運作。如果父程序繼續建立更多的子程序,子程序之間是兄弟關系,同樣子程序也可以建立自己的子程序,這樣可以建立起定義關系的程序之間的一種層次關系。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

forkdemo2

#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("before:my pid is %d\n", getpid() );
    fork();
    fork();
    printf("aftre:my pid is %d\n", getpid() );

    return 0;
}
           

這個代碼也是列印程序pid,然後調用fork函數生成子程序,因為調用兩次fork,一共産生四個子程序,是以會列印四個after輸出。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

forkdemo3

#include    <stdio.h>
#include    <stdlib.h>
#include    <unistd.h>

int main()
{
    int fork_rv;

    printf("Before: my pid is %d\n", getpid());

    fork_rv = fork();       /* create new process   */

    if ( fork_rv == -1 )        /* check for error  */
        perror("fork");
    else if ( fork_rv == 0 ){ 
        printf("I am the child.  my pid=%d\n", getpid());
    
        exit(0);
    }
    else{
        printf("I am the parent. my child is %d\n", fork_rv);
        exit(0);
    }

    return 0;
}
           

fork産生子程序,父程序傳回子程序pid,不為0,是以輸出父程序的那句話,子程序傳回0,是以會輸出子程序那句話。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

forkdemo4

代碼:

#include    <stdio.h>
#include    <stdlib.h>
#include    <unistd.h>

int main()
{
    int fork_rv;

    printf("Before: my pid is %d\n", getpid());

    fork_rv = fork();       /* create new process   */

    if ( fork_rv == -1 )        /* check for error  */
        perror("fork");

    else if ( fork_rv == 0 ){ 
        printf("I am the child.  my pid=%d\n", getpid());
        printf("parent pid= %d, my pid=%d\n", getppid(), getpid());
        exit(0);
    }

    else{
        printf("I am the parent. my child is %d\n", fork_rv);
        sleep(10);
        exit(0);
    }

    return 0;
}
           

先列印程序pid,然後fork建立子程序,父程序傳回子程序pid,是以輸出parent一句,休眠十秒;子程序傳回0,是以輸出child與之後一句。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

forkgdb

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int  gi=0;
int main()
{
    int li=0;
    static int si=0;
    int i=0;

    pid_t pid = fork();
    if(pid == -1){
        exit(-1);
    }
    else if(pid == 0){
        for(i=0; i<5; i++){
            printf("child li:%d\n", li++);
            sleep(1);
            printf("child gi:%d\n", gi++);
            printf("child si:%d\n", si++);
        }
        exit(0);
        
    }
    else{
        for(i=0; i<5; i++){
            printf("parent li:%d\n", li++);
            printf("parent gi:%d\n", gi++);
            sleep(1);
            printf("parent si:%d\n", si++);
        }
    exit(0);    
    
    }
    return 0;
}
           

父程序列印是先列印兩句,然後休眠一秒,然後列印一句,子程序先列印一句,然後休眠一秒,然後列印兩句。并且這兩個線程是并發的,是以可以看到在一個線程休眠的那一秒,另一個線程在執行,并且線程之間互相獨立互不幹擾。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

psh1

#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <unistd.h>

#define MAXARGS     20              
#define ARGLEN      100             

int execute( char *arglist[] )
{
    execvp(arglist[0], arglist);        
    perror("execvp failed");
    exit(1);
}

char * makestring( char *buf )
{
    char    *cp;

    buf[strlen(buf)-1] = '\0';      
    cp = malloc( strlen(buf)+1 );       
    if ( cp == NULL ){          
        fprintf(stderr,"no memory\n");
        exit(1);
    }
    strcpy(cp, buf);        
    return cp;          
}

int main()
{
    char    *arglist[MAXARGS+1];        
    int     numargs;            
    char    argbuf[ARGLEN];         

    numargs = 0;
    while ( numargs < MAXARGS )
    {                   
        printf("Arg[%d]? ", numargs);
        if ( fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n' )
            arglist[numargs++] = makestring(argbuf);
        else
        {
            if ( numargs > 0 ){     
                arglist[numargs]=NULL;  
                execute( arglist ); 
                numargs = 0;        
            }
        }
    }
    return 0;
}
           

這個代碼就相當于你輸入要執行的指令,回車表示輸入結束,然後輸入的每個參數對應到函數中,再調用對應的指令。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

psh2

#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <sys/types.h>
#include    <sys/wait.h>
#include    <unistd.h>
#include    <signal.h>

#define MAXARGS     20              
#define ARGLEN      100             

char *makestring( char *buf )
{
    char    *cp;

    buf[strlen(buf)-1] = '\0';      
    cp = malloc( strlen(buf)+1 );       
    if ( cp == NULL ){          
        fprintf(stderr,"no memory\n");
        exit(1);
    }
    strcpy(cp, buf);        
    return cp;          
}

void execute( char *arglist[] )
{
    int pid,exitstatus;             

    pid = fork();                   
    switch( pid ){
        case -1:    
            perror("fork failed");
            exit(1);
        case 0:
            execvp(arglist[0], arglist);        
            perror("execvp failed");
            exit(1);
        default:
            while( wait(&exitstatus) != pid )
                ;
            printf("child exited with status %d,%d\n",
                    exitstatus>>8, exitstatus&0377);
    }
}

int main()
{
    char    *arglist[MAXARGS+1];        
    int     numargs;            
    char    argbuf[ARGLEN];         

    numargs = 0;
    while ( numargs < MAXARGS )
    {                   
        printf("Arg[%d]? ", numargs);
        if ( fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n' )
            arglist[numargs++] = makestring(argbuf);
        else
        {
            if ( numargs > 0 ){     
                arglist[numargs]=NULL;  
                execute( arglist ); 
                numargs = 0;        
            }
        }
    }
    return 0;
}
           

比起1來,多了循環判斷,不退出的話就會一直要你輸入指令,并且對于子程式存在的狀态條件。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

testbuf1

#include <stdio.h>
#include <stdlib.h>
int main()
{
    printf("hello");
    fflush(stdout);
    while(1);
}
           

效果是先輸出hello,然後換行。之後不退出。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

testbuf2

#include <stdio.h>
int main()
{
    printf("hello\n");
    while(1);
}
           

程式輸出hello,無法退出。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

PS:可知:fflush(stdout)的效果和換行符\n是一樣的。

testbuf3

#include <stdio.h>

int main()
{
    fprintf(stdout, "1234", 5);
    fprintf(stderr, "abcd", 4);
}
           

将内容格式化輸出到标準錯誤、輸出流中。結果如圖:

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

testpid

#include <stdio.h>
#include <unistd.h>

#include <sys/types.h>

int main()
{
    printf("my pid: %d \n", getpid());
    printf("my parent's pid: %d \n", getppid());
    return 0;
}
           

輸出目前程序pid和目前程序的父程序的pid。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

testpp

#include <stdio.h>
#include <stdlib.h>
int main()
{
    char **pp;
    pp[0] = malloc(20);

    return 0;
}
           
20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

testsystem

#include    <stdlib.h>

int main ( int argc, char *argv[] )
{

    system(argv[1]);
    system(argv[2]);
    return EXIT_SUCCESS;
}               /* --------
           

system()——執行shell指令,也就是向dos發送一條指令。這裡是後面可以跟兩個參數,然後向dos發送這兩個指令,分别執行。如下圖,輸入ls和dir兩個指令後,可以看到分别執行了。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

waitdemo1

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

#define DELAY   4

void child_code(int delay)
{
    printf("child %d here. will sleep for %d seconds\n", getpid(), delay);
    sleep(delay);
    printf("child done. about to exit\n");
    exit(17);
}

void parent_code(int childpid)
{
    int wait_rv=0;      /* return value from wait() */
    wait_rv = wait(NULL);
    printf("done waiting for %d. Wait returned: %d\n", 
            childpid, wait_rv);
}
int main()
{
    int  newpid;
    printf("before: mypid is %d\n", getpid());
    if ( (newpid = fork()) == -1 )
        perror("fork");
    else if ( newpid == 0 )
        child_code(DELAY);
    else
        parent_code(newpid);

    return 0;
}
           

如果有子程序,則終止子程序,成功傳回子程序pid。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

waitdemo2

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

#define DELAY   10

void child_code(int delay)
{
    printf("child %d here. will sleep for %d seconds\n", getpid(), delay);
    sleep(delay);
    printf("child done. about to exit\n");
    exit(27);
}

void parent_code(int childpid)
{
    int wait_rv;    
    int child_status;
    int high_8, low_7, bit_7;

    wait_rv = wait(&child_status);
    printf("done waiting for %d. Wait returned: %d\n", childpid, wait_rv);

    high_8 = child_status >> 8;     /* 1111 1111 0000 0000 */
    low_7  = child_status & 0x7F;   /* 0000 0000 0111 1111 */
    bit_7  = child_status & 0x80;   /* 0000 0000 1000 0000 */
    printf("status: exit=%d, sig=%d, core=%d\n", high_8, low_7, bit_7);
}

int main()
{
    int  newpid;

    printf("before: mypid is %d\n", getpid());

    if ( (newpid = fork()) == -1 )
        perror("fork");
    else if ( newpid == 0 )
        child_code(DELAY);
    else
        parent_code(newpid);
}
           

這個比起1來就是多了一個子程序的狀态區分,把狀态拆分成三塊,exit,sig和core。

20135321餘佳源——資訊安全系統設計基礎第十二周學習總結

總結

  • 對于exec函數:
    在Linux中要使用exec函數族。系統調用execve()對目前程序進行替換,替換者為一個指定的程式,其參數包括檔案名(filename)、參數清單(argv)以及環境變量(envp)。exec函數族當然不止一個,但它們大緻相同,在 Linux中,它們分别是:execl,execlp,execle,execv,execve和execvp。一個程序一旦調用exec類函數,它本身就"死亡"了,系統把代碼段替換成新的程式的代碼,廢棄原有的資料段和堆棧段,并為新程式配置設定新的資料段與堆棧段,唯一留下的,就是程序号,也就是說,對系統而言,還是同一個程序,不過已經是另一個程式了。若是想啟動另一程式的執行但自己仍想繼續運作的話,那就得結合fork與exec的使用。
               
  • 對于fork函數:
    fork函數啟動一個新的程序,前面我們說過,這個程序幾乎是目前程序的一個拷貝:子程序和父程序使用相同的代碼段;子程序複制父程序的堆棧段和資料段。這樣,父程序的所有資料都可以留給子程序,但是,子程序一旦開始運作,雖然它繼承了父程序的一切資料,但實際上資料卻已經分開,互相之間不再有影響了,也就是說,它們之間不再共享任何資料了。
               

問題:

  • 運作testpp時,出現段錯誤(核心已轉儲),不知道原因。
  • 了解代碼時,對于管道實作父子程序之間通信不了解。
    其基本原理是這樣的:假如原先在父程序中檔案描述符3和4通過管道1連接配接起來(3是讀端,4是寫端),則fork建立子程序後,子程序中的檔案描述符3和4也通過管道1連接配接起來(3是讀端,4是寫端)。這樣一來,在父程序通過檔案描述符4向管道寫入内容後,在子程序中就可以通過檔案描述符3從管道中讀出資料(當然在父程序中也可以通過檔案描述符3從管道中讀出資料)。
               

參考資料

  1. 《程序間通信-命名管道FIFO》(http://blog.csdn.net/xiajun07061225/article/details/8471777)
  2. 《linux i/o重定向與管道程式設計》

    (http://blog.csdn.net/fulianzhou/article/details/48895327)

  3. 教材:第八章,詳細學習指導:http://group.cnblogs.com/topic/73069.html
  4. 闫佳歆同學的部落格:http://www.cnblogs.com/20135202yjx/p/5003653.html