【linux草鞋应用编程系列】_2_ 环境变量和进程控制

一、 环境变量

    应用程序在执行的时候,可能需要获取系统的环境变量,从而执行一些相应的操作。
    在linux中有两种方法获取环境变量,分述如下。
 
1、通过main函数的参数获取环境变量
     main函数的多种定义方式:
     int main(void);
int main(int argc, char* argv[ ]);
int main(int argc, char* argv[ ], char* env[ ] )
    方式1和方式2比较常见,下面介绍一下方式3: 第三个参数获取系统的环境变量。
Exp:
#include <stdio.h>

int main(int argc,char* argv[], char* env[])
{
int i=; while(env[i])
{
puts(env[i++]);
} return ;
}

程序执行的时候就可以输出所有的环境变量。

 
2、访问全局变量 environ 获取环境变量
    在加载应用程序的时候,linux系统会为每一个应用程序复制一份系统环境变量副本,保存在全局变量 enviro 中。
可以通过这个全局的变量访问系统的环境变量。
Exp:
#include <stdio.h>

extern char** environ;

int main(int argc,char* argv[])
{
int i=; while(environ[i])
{
puts(environ[i++]);
} return ;
}

3、获取指定的环境变量

    linux提供环境变量操作相关的函数: getenv( )、putenv( )、setenv()、unsetenv()、clearenv( ).
   
getenv( )
GETENV()                  Linux Programmer’s Manual                 GETENV()
NAME
getenv - get an environment variable SYNOPSIS
#include <stdlib.h> char *getenv(const char *name); //要获取的环境变量,比方说传递的是 "HOME" ,将返回HOME的值
返回值:
        成功返回指向环境变量的值的指针,失败返回NULL。
 
putenv( )
UTENV()                  Linux Programmer’s Manual                 PUTENV()
NAME
putenv - change or add an environment variable //增加或者改变环境变量的值
SYNOPSIS
#include <stdlib.h> int putenv(char *string); //设置的环境变量字符串, string的格式如下: HOME=/home/volcanol

setenv( ) 和 unsetenv()

SETENV()                  Linux Programmer’s Manual                 SETENV()
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.

注意:

       unsetenv 会将环境变量删除,包括环境变量的名和环境变量的值
 
    clearenv()清除所有的环境变量,并设置environ 的值为NULL。
CLEARENV()                                                                                              CLEARENV()
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  的字样,表示这个函数需要慎重使用。

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", );
env=getenv("Test-env");
printf("the Test-env is: %s\n ",env); return ;
}
执行结果如下:
[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 ;
}
执行结果如下:
[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=;
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 ;
}
执行结果如下:
[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]#
二、进程控制
    进程控制包括: 创建新进程、执行新的应用程序和结束进程。
 
1、进程ID
    linux系统中,每一个进程都有一个唯一的标识符: PID, 即进程ID。
    在应用程序可以调用 getpid( )获取进程的ID;  调用 getppid()获取父进程的进程ID。
其原型如下:
GETPID()                  Linux Programmer’s Manual                 GETPID()
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 ;
}
程序执行结果如下:
[root@localhost fork]# ./a.out
pid=, ppid=
[root@localhost fork]# ps aux | grep "bash"
root 0.0 0.3 pts/ Ss : : bash

可以发现父进程的 进程ID为 714,我们通过 ps 命令查看,可以知道 bash 的PID 为 714 ,因为 ./a.out 是由

bash 这个进程创建的,因此./a.out 的父进程的PID为 714。
 
2、执行其他程序
    linux下提供 一个函数族 在进程中执行其他应用程序。函数族为  exec 。其原型如下:
EXEC()                    Linux Programmer’s Manual                   EXEC()

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[],getpid()); execl("test","from caller",NULL); //exec从默认路径搜索 test 可执行文件,我系统中默认路径没有 test 可知文件,执行会失败
perror("execl"); return ;
}
    下面的文件是生成 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[],getpid()); return ;
}

执行结果如下:

[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=
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[],getpid()); execl("./test","test","aa",NULL); //指定test可执行文件在当前目录下
perror("execl"); printf("if execl execute successfull this statement never reach");
return ;
}
    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[],getpid()); return ;
}
    测试结果如下:
[root@localhost fork]# gcc main.c
[root@localhost fork]# gcc -o test test.c
[root@localhost fork]# ./a.out
in program: ./a.out, pid= //执行a.out ,并加载启动 test 可知文件
in program: test, pid= //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] .
       
3、fork机制
    类Unix系统都使用fork机制来创建新进程。
    我原来一直不明白为什么要用 fork 这个字符组来表示创建新进程,后来查看英语的解释,才发现 fork 的意思是
分叉、分支、叉子的意思,soga,就是这样。
 
    fork机制的要点是:  
            1、创建一个新的进程,新进程拥有自己独立的进程空间和进程上下文
            2、fork会将父进程的空间对内容,完全的复制到子进程空间。
            3、fork出来的子进程和父进程一样,从fork函数调用位置开始执行
            4、fork函数在父进程中返回子进程的进程ID,  fork函数在子进程中返回0; 在代码中利用返回值
                  确定是在父进程还是子进程中。
    fork的原型如下:
FORK()                    Linux Programmer’s Manual                   FORK()
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( ==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(); //在子进程中退出
} //下面的代码在父进程的空间执行
printf("my pid= %d\n",getpid());
printf("the process that create me is :%d\n",getppid()); return ;
}
代码执行效果如下所示:
[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= //父进程空间
my pid= //子进程空间
the process that create me is : //子进程空间
the process that create me is : //父进程空间
[root@localhost fork]# ./a.out //第二次执行
Now you a in child process //子进程空间
my pid= //子进程空间
the process that create me is : //子进程空间
my pid= //父进程空间
the process that create me is : //父进程空间
[root@localhost fork]#
    可以发现子进程和父进程的执行顺序存在很大的随机性;父进程可能先执行,也可能后执行。有可能两个进程执行
是交叉的。
    
    有时候为了防止这种交叉性,可以在进程中等待其他进程执行完毕后再执行本进程的代码。在linux中利用wait( )实现
进程间的这种等待(又叫做同步)。
     wait的原型如下:
WAIT()                    Linux Programmer’s Manual                   WAIT()
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).
        要点:
                    1、 这些函数用在调用这些函数的进程中等待子进程的状态改变。
                    2、子进程的状态改变有三种形式:    子进程终止、子进程暂停、子进程从暂停状态中恢复执行。
                    3、如果子进程的状态改变,这些函数立即返回,否则就阻塞调用 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( ==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();
}
//等待子进程的状态改变, 只有子进程的状态改变了wait才能返回,否则就阻塞父进程
wait(&status); printf("my pid= %d\n",getpid());
printf("the process that create me is :%d\n",getppid()); return ;
}
 这段代码编译后,无论怎么执行都会是下面的结果:
[root@localhost fork]# gcc fork.c
[root@localhost fork]# ./a.out //第一次执行
Now you a in child process my pid=
the process that create me is : my pid=
the process that create me is :
[root@localhost fork]# ./a.out //第二次执行
Now you a in child process my pid=
the process that create me is : my pid=
the process that create me is :
[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( ==pid )
{
sleep();
printf("Now you a in child process ");
printf("my pid= %d\n",getpid());
printf("the process that create me is :%d\n",getppid());
exit();
} //等待子进程的状态改变
//wait(&status);
waitpid(pid,&status,WNOHANG);//函数立即返回,并且通过输出参数status获取子进程的状态 printf("my pid= %d\n",getpid());
printf("the process that create me is :%d\n",getppid()); return ;
}
执行情况如下:
[root@localhost fork]# gcc fork.c
[root@localhost fork]# ./a.out
my pid= //父进程中waitpid 已经返回
the process that create me is : //父进程输出信息后已经结束
[root@localhost fork]# Now you a in child process my pid= //子进程开始输出信息,
the process that create me is : [root@localhost fork]#
4、进程间同步
    如果在父子进程间同时操作一个文件,那么就会存在对文件操作的竞争状态,可能是文件的内容出现异常。
可以通过进程间同步机制完成进程间的同步。
   
    下面为测试代码:
#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[];
int size;
int i;
int j; fd=open("./txt",O_RDWR | O_CREAT | O_TRUNC);
if(- == fd)
{
perror("open txt");
exit();
} //创建新进程
pid=fork();
if( ==pid )
{
for(i=;i<;i++)
{
j=;
size=sprintf(buf,"in child process pid=%d ppid=%d i=%d\n",getpid(), getppid(),i);
while(buf[j])
{
usleep();
write(fd,&buf[j++],);
}
}
exit();
} for(i=;i<;i++)
{
j=;
size=sprintf(buf,"in parent process pid=%d ppid=%d i=%d\n",getpid(), getppid(),i);
while(buf[j])
{
write(fd,&buf[j++],);
usleep();
}
}
//waitpid(pid,&status,WNOHANG);//函数立即返回,并且通过输出参数status获取子进程的状态 close(fd);
return ;
}
    生成的txt 文件如下所示:
in ipnar ecnth i plrocde spsr opcieds=s2 7p8i4d3=  7p8p4i4d = 7p1p4i di==
8i4n3 pia=r0e
nitn cphriolcde spsr opcieds=s2 7p8i4d3= 7p8p4i4d = 7p1p4i di==
8i4n3 pia=r1e
nitn cphriolcde spsr opcieds=s2 7p8i4d3= 7p8p4i4d = 7p1p4i di==
8i4n3 pia=r2e
nitn cphriolcde spsr opcieds=s2 7p8i4d3= 7p8p4i4d = 7p1p4i di==
8i4n3 pia=r3e
nitn cphriolcde spsr opcieds=s2 7p8i4d3= 7p8p4i4d = 7p1p4i di==
8i4n3 pia=r4e
nitn cphriolcde spsr opcieds=s2 7p8i4d3= 7p8p4i4d = 7p1p4i di==
8i4n3 pia=r5e
nitn cphriolcde spsr opcieds=s2 7p8i4d3= 7p8p4i4d = 7p1p4i di==
8i4n3 pia=r6e
nitn cphriolcde spsr opcieds=s2 7p8i4d3= 7p8p4i4d = 7p1p4i di==
8i4n3 pia=r7e
nitn cphriolcde spsr opcieds=s2 7p8i4d3= 7p8p4i4d = 7p1p4i di==
8i4n3 pia=r8e
nitn cphriolcde spsr opcieds=s2 7p8i4d3= 7p8p4i4d = 7p1p4i di==
i=
    生成的信息,是乱码,不能传递信息。
 
    修改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[];
int size;
int i;
int j; fd=open("./txt",O_RDWR | O_CREAT | O_TRUNC);
if(- == fd)
{
perror("open txt");
exit();
} //创建新进程
pid=fork();
if( ==pid )
{
for(i=;i<;i++)
{
j=;
size=sprintf(buf,"in child process pid=%d ppid=%d i=%d\n",getpid(), getppid(),i);
while(buf[j])
{
usleep();
write(fd,&buf[j++],);
}
}
exit();
} wait(&status); //等待子进程状态改变,新增加的代码
for(i=;i<;i++)
{
j=;
size=sprintf(buf,"in parent process pid=%d ppid=%d i=%d\n",getpid(), getppid(),i);
while(buf[j])
{
write(fd,&buf[j++],);
usleep();
}
}
//waitpid(pid,&status,WNOHANG);//函数立即返回,并且通过输出参数status获取子进程的状态 close(fd);
return ;
}
      生成的TXT文件如下所示:
in child process pid=  ppid= i=
in child process pid= ppid= i=
in child process pid= ppid= i=
in child process pid= ppid= i=
in child process pid= ppid= i=
in child process pid= ppid= i=
in child process pid= ppid= i=
in child process pid= ppid= i=
in child process pid= ppid= i=
in child process pid= ppid= i=
in parent process pid= ppid= i=
in parent process pid= ppid= i=
in parent process pid= ppid= i=
in parent process pid= ppid= i=
in parent process pid= ppid= i=
in parent process pid= ppid= i=
in parent process pid= ppid= i=
in parent process pid= ppid= i=
in parent process pid= ppid= i=
in parent process pid= ppid= i=

  【linux草鞋应用编程系列】_2_环境变量和进程控制

  本系列文章未完,待续。

  如果您发现,文章有疏漏之处请不吝指教,包括错别字,标点符号等任何错误。

上一篇:【linux草鞋应用编程系列】_6_ 重定向和VT100编程


下一篇:The Water Problem(排序)