【linux草鞋應用編程系列】_2_ 環境變量和進程控制

1、 環境變量linux

    應用程序在執行的時候,可能須要獲取系統的環境變量,從而執行一些相應的操做。
    在linux中有兩種方法獲取環境變量,分述以下。
 
一、經過main函數的參數獲取環境變量
     main函數的多種定義方式:
     int main(void);
     int main(int argc, char* argv[ ]);
     int main(int argc, char* argv[ ],  char* env[ ] )
View Code
    方式1和方式2比較常見,下面介紹一下方式3: 第三個參數獲取系統的環境變量。
Exp:
#include <stdio.h>

int main(int argc,char* argv[], char* env[])
{
    int i=0;

    while(env[i])
    {
        puts(env[i++]);
    }

    return 0;
}

    程序執行的時候就能夠輸出全部的環境變量。編程

 
二、訪問全局變量 environ 獲取環境變量
    在加載應用程序的時候,linux系統會爲每個應用程序複製一份系統環境變量副本,保存在全局變量 enviro 中。
能夠經過這個全局的變量訪問系統的環境變量。
Exp:
#include <stdio.h>

extern char** environ;

int main(int argc,char* argv[])
{
    int i=0;

    while(environ[i])
    {
        puts(environ[i++]);
    }

    return 0;
}

三、獲取指定的環境變量vim

    linux提供環境變量操做相關的函數: getenv( )、putenv( )、setenv()、unsetenv()、clearenv( ).
   
getenv( )
GETENV(3)                  Linux Programmer’s Manual                 GETENV(3)
NAME
       getenv - get an environment variable

SYNOPSIS
       #include <stdlib.h>

       char *getenv(const char *name);    //要獲取的環境變量,比方說傳遞的是 "HOME" ,將返回HOME的值
返回值:
         成功返回指向環境變量的值的指針,失敗返回NULL。
 
putenv( )
UTENV(3)                  Linux Programmer’s Manual                 PUTENV(3)
NAME
       putenv - change or add an environment variable   //增長或者改變環境變量的值
SYNOPSIS
       #include <stdlib.h>

       int putenv(char *string);    //設置的環境變量字符串, string的格式以下:  HOME=/home/volcanol

 

setenv( ) 和 unsetenv()數組

SETENV(3)                  Linux Programmer’s Manual                 SETENV(3)
NAME
       setenv - change or add an environment variable    //改變或者增長環境變量
SYNOPSIS
       #include <stdlib.h>

       int setenv(const char *name,    //要設置的環境變量名;若是不存在就會建立新的環境變量,無論 overwrite的值
                  const char *value,   //要設置的環境變量的值
                  int overwrite);      // 若是環境變量已經存在,當 overwrite非零則改寫原值, overwrite=0 則不改變原值

       int unsetenv(const char *name);    //要刪除的環境變量
DESCRIPTION
       The  setenv()  function  adds  the  variable name to the environment with the value
       value, if name does not already exist.  If name does exist in the environment, then
       its  value is changed to value if overwrite is non-zero; if overwrite is zero, then
       the value of name is not changed.

       The unsetenv() function deletes the variable name from the environment.

注意: bash

       unsetenv 會將環境變量刪除,包括環境變量的名和環境變量的值
 
    clearenv()清除全部的環境變量,並設置environ 的值爲NULL。
CLEARENV(3)                                                                                              CLEARENV(3)
NAME
       clearenv - clear the environment
SYNOPSIS
       #include <stdlib.h>
       int clearenv(void);
DESCRIPTION
       The clearenv() function clears the environment of all name-value pairs and sets the
       value of the external variable environ to NULL.

  注意這個地方:  沒有 linux  program manual  的字樣,表示這個函數須要慎重使用。ide

Exp:  getenv( )  和 setenv( )
#include <stdio.h>
#include <stdlib.h>

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

    setenv("Test-env","this is a test env", 1);
    env=getenv("Test-env");
    printf("the Test-env is: %s\n ",env);

    return 0;
}
執行結果以下:
[root@localhost process]# gcc main.c 
[root@localhost process]# ./a.out 
the Test-env is: this is a test env
要點:
        這樣設置的環境變量僅對當前進程有效,其餘進程是無效的。
[root@localhost process]# ./a.out 
the Test-env is:this is a test env
[root@localhost process]# env | grep "test"
[root@localhost process]# env | grep "env"
_=/bin/env
[root@localhost process]# 

 

Exp: putenv()和unsetenv()
#include <stdio.h>
#include <stdlib.h>

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

    /*setenv("Test-env","this is a test env", 1);*/
    /*env=getenv("Test-env")*/

    putenv("test-env=this is a test env");
    env=getenv("test-env");
    printf("the test-env is:%s\n",env);

    unsetenv("test-env");
    env=getenv("test-env");
    printf("after unsetenv");
    printf("the test-env is:%s\n",env);

    return 0;
}
執行結果以下:
[root@localhost process]# gcc main.c 
[root@localhost process]# ./a.out 
the test-env is:this is a test env
after unsetenvthe test-env is:(null)          //環境變量已經刪除
[root@localhost process]# 
    unsetenv( ) 會將環境變量名和值所有刪除;
Exp:
#include <stdio.h>
#include <stdlib.h>

extern char** environ;

int main(int argc,char* argv[])
{
    int i=0;
    char* env;

    /*setenv("Test-env","this is a test env", 1);*/
    /*env=getenv("Test-env")*/

    putenv("test-env=this is a test env");
    env=getenv("test-env");
    printf("the test-env is:%s\n",env);

    unsetenv("test-env");
    env=getenv("test-env");
    printf("after unsetenv");
    printf("the test-env is:%s\n",env);

    while(environ[i])
    {
       printf("%s",environ[i++]);
    }
    return 0;
}
執行結果以下:
[root@localhost process]# gcc main.c 
[root@localhost process]# ./a.out  | grep "test"
the test-env is:this is a test env
after unsetenvthe test-env is:(null)
[root@localhost process]# 

 

2、進程控制
    進程控制包括: 建立新進程、執行新的應用程序和結束進程。
 
一、進程ID
    linux系統中,每個進程都有一個惟一的標識符: PID, 即進程ID。
    在應用程序能夠調用 getpid( )獲取進程的ID;  調用 getppid()獲取父進程的進程ID。
其原型以下:
GETPID(2)                  Linux Programmer’s Manual                 GETPID(2)
NAME
       getpid, getppid - get process identification
SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>

       pid_t getpid(void);
       pid_t getppid(void);

DESCRIPTION
       getpid()  returns  the  process  ID of the current process.  (This is often used by
       routines that generate unique temporary filenames.)

       getppid() returns the process ID of the parent of the current process.
Exp: 獲取進程的PID和父進程的PID
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    pid_t pid;
    pid_t ppid;

    printf("pid=%d, ppid=%d\n", getpid(),getppid());
    return 0;
}
程序執行結果以下:
[root@localhost fork]# ./a.out 
pid=19077, ppid=714
[root@localhost fork]# ps aux | grep "bash"
root       714  0.0  0.3   5940  1668 pts/1    Ss   04:46   0:01 bash

     能夠發現父進程的 進程ID爲 714,咱們經過 ps 命令查看,能夠知道 bash 的PID 爲 714 ,由於 ./a.out 是由函數

bash 這個進程建立的,所以./a.out 的父進程的PID爲 714。
 
二、執行其餘程序
    linux下提供 一個函數族 在進程中執行其餘應用程序。函數族爲  exec 。其原型以下:
EXEC(3)                    Linux Programmer’s Manual                   EXEC(3)

NAME
       execl, execlp, execle, execv, execvp - execute a file

SYNOPSIS
       #include <unistd.h>

       extern char **environ;

       int execl(const char *path, const char *arg, ...);
       int execlp(const char *file, const char *arg, ...);
       int execle(const char *path, const char *arg,
                  ..., char * const envp[]);
       int execv(const char *path, char *const argv[]);     //參數以數組的形式傳遞
       int execvp(const char *file, char *const argv[]);      //參數以數組的形式傳遞

    exec函數族的函數,將指定的可執行程序加載到調用exec函數的進程空間執行。若是exec函數執行成功,測試

則不返回,exec函數執行失敗才返回。加載後的可執行程序與調用exec函數的進程具備同樣的進程ID.
    要點:
         execl() 和 execlp ()最後一個參數必須設置爲 NULL, 不然就不能成功執行程序。
 
Exp:  
     測試exec()執行失敗的狀況
    下面的文件是生成  a.out 可執行文件 main.c
#include <stdio.h>
#include <unistd.h>

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

    printf("in program %s, pid=%d\n",argv[0],getpid());

    execl("test","from caller",NULL);  //exec從默認路徑搜索 test 可執行文件,我係統中默認路徑沒有  test 可知文件,執行會失敗
    perror("execl");

    return 0;
}
    下面的文件是生成 test 可執行文件的 test.c
 #include <stdio.h>
#include <stdlib.h>

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

    printf("argv[1]=%s, pid=%d\n",argv[1],getpid());

    return 0;
}

    執行結果以下:this

[root@localhost fork]# vim main.c 
[root@localhost fork]# vim test.c
[root@localhost fork]# gcc main.c 
[root@localhost fork]# gcc -o test test.c 
[root@localhost fork]# ./a.out 
in program ./a.out, pid=19402
execl: Bad address      //execl( ) 函數執行失敗返回
Exp:  
     測試exec()執行成功的狀況。
    將main.c 進行修改,以下所示:
#include <stdio.h>
#include <unistd.h>

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

    printf("in program: %s, pid=%d\n",argv[0],getpid());

    execl("./test","test","aa",NULL);    //指定test可執行文件在當前目錄下
    perror("execl");

  printf("if execl execute successfull this statement never reach");
    return 0;
}
    test.c 文件以下:
#include <stdio.h>
#include <stdlib.h>

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

    printf("in program: %s, pid=%d\n",argv[0],getpid());

    return 0;
}
    測試結果以下:
[root@localhost fork]# gcc main.c 
[root@localhost fork]# gcc -o test test.c
[root@localhost fork]# ./a.out 
in program: ./a.out, pid=19836     //執行a.out  ,並加載啓動 test 可知文件
in program: test, pid=19836         //test 可執行文件加啓動成功
[root@localhost fork]# 
    注意:
    a.out 和 test的 進程PID是同樣的,這是由於 test 被加載到 a.out 的進程空間運行,沒有建立新進程,因此
PID值是同樣的,並且執行成功的話,會在 test 裏面退出,而不是在 a.out 裏面退出。
     注意main.c 裏面的代碼。
 
要點:
      注意execl/execlp 函數調用的時候,傳遞的參數關係。 a.out 中調用 execl的第二個參數對應 test.c 中的
main函數的 argv[0] .
       
三、fork機制
    類Unix系統都使用fork機制來建立新進程。
    我原來一直不明白爲何要用 fork 這個字符組來表示建立新進程,後來查看英語的解釋,才發現 fork 的意思是
分叉、分支、叉子的意思,soga,就是這樣。
 
    fork機制的要點是:  
            一、建立一個新的進程,新進程擁有本身獨立的進程空間和進程上下文
            二、fork會將父進程的空間對內容,徹底的複製到子進程空間。
            三、fork出來的子進程和父進程同樣,從fork函數調用位置開始執行
            四、fork函數在父進程中返回子進程的進程ID,  fork函數在子進程中返回0; 在代碼中利用返回值
                  肯定是在父進程仍是子進程中。
    fork的原型以下:
FORK(2)                    Linux Programmer’s Manual                   FORK(2)
NAME
       fork - create a child process

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

       pid_t fork(void);
    fork測試代碼以下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

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

    //建立新進程
    pid=fork();
    if( 0==pid )      // 若是pid==0 則表示在子進程的進程空間
    { 
           //下面的代碼在子進程的進程空間執行
        printf("Now you a in child process\n");
        printf("my pid= %d\n",getpid());
        printf("the process that  create me is :%d\n",getppid());
        exit(0);   //在子進程中退出
    }

  //下面的代碼在父進程的空間執行
    printf("my pid= %d\n",getpid());
    printf("the process that  create me is :%d\n",getppid());

    return 0;
}
代碼執行效果以下所示:
[root@localhost fork]# vim fork.c 
[root@localhost fork]# gcc fork.c 
[root@localhost fork]# ./a.out           //第一次執行
Now you a in child process   //子進程空間
my pid= 22333   //父進程空間
my pid= 22334   //子進程空間
the process that  create me is :22333  //子進程空間
the process that  create me is :714     //父進程空間
[root@localhost fork]# ./a.out      //第二次執行
Now you a in child process         //子進程空間
my pid= 22337            //子進程空間
the process that  create me is :22336 //子進程空間
my pid= 22336             //父進程空間
the process that  create me is :714      //父進程空間
[root@localhost fork]# 
    能夠發現子進程和父進程的執行順序存在很大的隨機性;父進程 可能 先執行,也可能後執行。有可能兩個進程執行
是交叉的。
    
    有時候爲了防止這種交叉性,能夠在進程中等待其餘進程執行完畢後再執行本進程的代碼。在linux中利用wait( )實現
進程間的這種等待(又叫作同步)。
     wait的原型以下:
WAIT(2)                    Linux Programmer’s Manual                   WAIT(2)
NAME
       wait, waitpid - wait for process to change state   //等待某一個進程的狀態的改變
SYNOPSIS
       #include <sys/types.h>
       #include <sys/wait.h>

       pid_t wait(int *status);
       pid_t waitpid(pid_t pid, int *status, int options);
       int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
    具體的描述信息:
    All  of  these  system  calls are used to wait for state changes in a child of the
       calling process, and obtain information about the child whose state  has  changed.
       A state change is considered to be: the child terminated; the child was stopped by
       a signal; or the child was resumed by a signal.   In  the  case  of  a  terminated
       child,  performing  a  wait  allows the system to release the resources associated
       with the child; if a wait is not performed, then terminated the child remains in a
       "zombie" state (see NOTES below).
        要點:
                    一、 這些函數用在調用這些函數的進程中等待子進程的狀態改變。
                    二、子進程的狀態改變有三種形式:    子進程終止、子進程暫停、子進程從暫停狀態中恢復執行。
                    三、若是子進程的狀態改變,這些函數當即返回,不然就阻塞調用 wait*( )函數的進程。
          wait( )等待全部的子進程中的某一個進程狀態改變,至關於 waitpid(-1, &status, 0)
          waitpid()等待指定子進程的狀態改變。這個函數須要注意。
          pid_t waitpid(pid_t pid,    //指定要等待的進程,-1 表示等待全部的子進程,>0 表示指定的子進程ID 
                                                       int *status,
                                                       int options); //指定要等待進程的什麼狀態
      pid 和 options 還有不少的取值,須要查看 mannual 手冊。  
  
Exp: 測試一下, 先測試 wait()
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>

int main(int argc,char* argv[])
{
    pid_t pid;
    int status;

    //建立新進程
    pid=fork();
    if( 0==pid )
    {
        printf("Now you a in child process ");
        printf("my pid= %d\n",getpid());
        printf("the process that  create me is :%d\n\n",getppid());
        exit(0);
    }
    //等待子進程的狀態改變, 只有子進程的狀態改變了wait才能返回,不然就阻塞父進程
    wait(&status);  
    
    printf("my pid= %d\n",getpid());
    printf("the process that  create me is :%d\n",getppid());

    return 0;
}
 這段代碼編譯後,不管怎麼執行都會是下面的結果:
[root@localhost fork]# gcc fork.c 
[root@localhost fork]# ./a.out                      //第一次執行
Now you a in child process my pid= 24896
the process that  create me is :24895
 
my pid= 24895
the process that  create me is :714
[root@localhost fork]# ./a.out                      //第二次執行
Now you a in child process my pid= 24898
the process that  create me is :24897
 
my pid= 24897
the process that  create me is :714
[root@localhost fork]#
    兩次執行的狀況都是 子進程先執行, 父進程後進行。  
Exp: 測試waitpid()
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>

int main(int argc,char* argv[])
{
    pid_t pid;
    int status;

    //建立新進程
    pid=fork();
    if( 0==pid )
    {
        sleep(5);
        printf("Now you a in child process ");
        printf("my pid= %d\n",getpid());
        printf("the process that  create me is :%d\n",getppid());
        exit(0);
    }

    //等待子進程的狀態改變
    //wait(&status);
    waitpid(pid,&status,WNOHANG);//函數當即返回,而且經過輸出參數status獲取子進程的狀態

    printf("my pid= %d\n",getpid());
    printf("the process that  create me is :%d\n",getppid());

    return 0;
}
執行狀況以下:
[root@localhost fork]# gcc fork.c 
[root@localhost fork]# ./a.out 
my pid= 25808                 //父進程中waitpid 已經返回
the process that  create me is :714    //父進程輸出信息後已經結束
[root@localhost fork]# Now you a in child process my pid= 25809    //子進程開始輸出信息,
the process that  create me is :1

[root@localhost fork]# 
四、進程間同步
    若是在父子進程間同時操做一個文件,那麼就會存在對文件操做的競爭狀態,多是文件的內容出現異常。
能夠經過進程間同步機制完成進程間的同步。
   
    下面爲測試代碼:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>

int main(int argc,char* argv[])
{
    pid_t pid;
    int status;
    int fd;
    char buf[128];
    int size;
    int i;
    int j;

    fd=open("./txt",O_RDWR | O_CREAT | O_TRUNC);
    if(-1 == fd)
    {
        perror("open txt");
        exit(0);
    }

    //建立新進程
    pid=fork();
    if( 0==pid )
    { 
        for(i=0;i<10;i++)
        {
            j=0;
            size=sprintf(buf,"in child process pid=%d  ppid=%d i=%d\n",getpid(), getppid(),i);
            while(buf[j])   
            {
                usleep(2);
                write(fd,&buf[j++],1);
            }
        }
        exit(0);
    }
    
    for(i=0;i<10;i++)
    {
        j=0;
        size=sprintf(buf,"in parent  process pid=%d  ppid=%d i=%d\n",getpid(), getppid(),i);
        while(buf[j])
        {
            write(fd,&buf[j++],1);
            usleep(2);
        }
    }
    //waitpid(pid,&status,WNOHANG);//函數當即返回,而且經過輸出參數status獲取子進程的狀態
   
    close(fd);
    return 0;
}
    生成的txt 文件以下所示:
in ipnar ecnth i plrocde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==207
8i4n3  pia=r0e
nitn   cphriolcde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==217
8i4n3  pia=r1e
nitn   cphriolcde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==227
8i4n3  pia=r2e
nitn   cphriolcde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==237
8i4n3  pia=r3e
nitn   cphriolcde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==247
8i4n3  pia=r4e
nitn   cphriolcde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==257
8i4n3  pia=r5e
nitn   cphriolcde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==267
8i4n3  pia=r6e
nitn   cphriolcde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==277
8i4n3  pia=r7e
nitn   cphriolcde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==287
8i4n3  pia=r8e
nitn   cphriolcde spsr opcieds=s2 7p8i4d3= 2 7p8p4i4d = 7p1p4i di==297
843 i=9
    生成的信息,是亂碼,不能傳遞信息。
 
    修改fork.c  以下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>

int main(int argc,char* argv[])
{
    pid_t pid;
    int status;
    int fd;
    char buf[128];
    int size;
    int i;
    int j;

    fd=open("./txt",O_RDWR | O_CREAT | O_TRUNC);
    if(-1 == fd)
    {
        perror("open txt");
        exit(0);
    }

    //建立新進程
    pid=fork();
    if( 0==pid )
    { 
        for(i=0;i<10;i++)
        {
            j=0;
            size=sprintf(buf,"in child process pid=%d  ppid=%d i=%d\n",getpid(), getppid(),i);
            while(buf[j])   
            {
                usleep(2);
                write(fd,&buf[j++],1);
            }
        }
        exit(0);
    }
   
    wait(&status);     //等待子進程狀態改變,新增長的代碼
    for(i=0;i<10;i++)
    {
        j=0;
        size=sprintf(buf,"in parent  process pid=%d  ppid=%d i=%d\n",getpid(), getppid(),i);
        while(buf[j])
        {
            write(fd,&buf[j++],1);
            usleep(2);
        }
    }
    //waitpid(pid,&status,WNOHANG);//函數當即返回,而且經過輸出參數status獲取子進程的狀態
    
    close(fd);
    return 0;
}
      生成的TXT文件以下所示:
in child process pid=27878  ppid=27877 i=0
in child process pid=27878  ppid=27877 i=1
in child process pid=27878  ppid=27877 i=2
in child process pid=27878  ppid=27877 i=3
in child process pid=27878  ppid=27877 i=4
in child process pid=27878  ppid=27877 i=5
in child process pid=27878  ppid=27877 i=6
in child process pid=27878  ppid=27877 i=7
in child process pid=27878  ppid=27877 i=8
in child process pid=27878  ppid=27877 i=9
in parent  process pid=27877  ppid=714 i=0
in parent  process pid=27877  ppid=714 i=1
in parent  process pid=27877  ppid=714 i=2
in parent  process pid=27877  ppid=714 i=3
in parent  process pid=27877  ppid=714 i=4
in parent  process pid=27877  ppid=714 i=5
in parent  process pid=27877  ppid=714 i=6
in parent  process pid=27877  ppid=714 i=7
in parent  process pid=27877  ppid=714 i=8
in parent  process pid=27877  ppid=714 i=9

 

  【linux草鞋應用編程系列】_2_環境變量和進程控制spa

  本系列文章未完,待續。

  若是您發現,文章有疏漏之處請不吝指教,包括錯別字,標點符號等任何錯誤。

相關文章
相關標籤/搜索