嵌入式LinuxC编程之进程控制(二)

承接上一节所写内容,首先通过fork()和vfork()函数创建子进程,子进程与父进程执行的代码是相同的。通常创建了一个进程,目的是为了执行与父进程不同的操作,实现不用的功能,有错引进了exec()函数族。
一、exec()函数族:
    1、有六个exec开头的函数组成
  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,const char *argv[]);
  int execve(const char *path,const char *argv[],char *const envp[]);
  int execvp(const char *file,const char *argv[]);
  这些函数都定义在函数库中,在使用前需包含头文件<sys/types.h>和<unistd.h>,并且在预定义时定义一个外部的全局变量 extern char **environ;
  其作用就是,定义完此命令,可在当前目录执行系统程序,如同执行vim一样。
  exec()函数族中的函数,都实现对子进程的数据段、代码段和堆栈段进行替换的功能,如果调用成功,则加载新程序,没有返回值。如果出错返回-1.
2、如何记住exec函数的区别:
  (1) 函数名中带有p:代表文件的绝对路径(或称相对路径),当函数中带有p时可以不用书写文件的相对路径,只写出文件名即可。
  (2) 函数名中带有l:表示将新程序的每个命令行参数都当作一个参数传给它,参数个数可变,并且最后要输入一个NULL参数表示参数输入结束。
  (3) 函数名中带有v:表示该类函数支持使用参数数组,数组中的最后一个指针也要输入NULL参数作为结束标志,类似于main()函数的形参argv[].

  (4) 函数名以e结尾:该类函数表示可以将一份新的函数变量表传给它。

	示例:execve函数
	//new2.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
extern char **environ;
int main(void)
{
	puts("welcome to mrsoft");
	return 0;
}
//execve.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
extern char **environ;
int main(int argc,char* argv[])
{
	execve("new",argv,environ);//此处调用可执行文件new,也就是new2.c编译后的执行文件
	//此时进程中的代码段、数据段和进程段都进行了修改,使得新创建的进程只执行新加载的这个程序的代码
	puts("正常情况下无法输出此信息");
}
执行结果:welcome to mrsoft!

//修改后的execve.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
extern char **environ;
int main(int argc,char* argv[])
{
	pid_t pid;
	if((pid=fork())<0)
		printf("create child process failed!\n");
	else if(pid==0)
	    execve("new",argv,environ);
	else 
		puts("正常情况下输出此信息!\n");
}

//运行结果:正常情况下输出此信息!
//			welcome to mrsoft!
//再次修改execve.c
//在那个进程中调用,PID还是那个进程的
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int gvar=2;//全局变量
int main(void)
{
	pid_t pid;
	int var=5;//局部变量
	printf("process id:%ld\n",(long)getpid());//输出父进程的PID
	printf("gvar=%d var=%d\n",gvar,var);
	if((pid=fork())<0)//创建子进程失败
	{
		perror("error!");
		return 1;
	}
	else if(pid==0)//如果在子进程中执行以下程序
	{
		gvar--;
		var++;
		//输出子进程的PID和修改后的变量值
		printf("the child process id:%ld,gvar=%d var=%d\n",(long)getpid(),gvar,var);
		return 0;
	}
	else
	{   //输出父进程的PID以及变量值
		printf("the parent process id:%ld,gvar=%d var=%d\n",(long)getpid(),gvar,var);
		execve("new",argv,environ);
		return 0;
	}
}
//修改后的new.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
extern char **environ;
int main(void)
{
	puts("welcome to mrsoft");
	printf("newPID=%ld\n",(long)getpid());
	return 0;
}
/*运行结果:process id:4043
		   gvar=2 var=5
		   the parent process id:4043,gvar=2,var=5 
		   the child process id:4044,gvar=1,var=6 
		   welcome to mrsoft
		   newPID=4043*/

3、其他exec函数

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

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

int main(int argc, char *argv[])
{
  //以NULL结尾的字符串数组的指针,适合包含v的exec函数参数
  char *arg[] = {"ls", "-a", NULL};
  
  /**
   * 创建子进程并调用函数execl
   * execl 中希望接收以逗号分隔的参数列表,并以NULL指针为结束标志
   * 包含有 l ① 说明需要在另加上可执行程序作为第二个参数 当然可以加第三、四个等 待参数
   *          ② 注意不能够外加参数,因为它不能添加参数argv,因此不能再加参数
   * 不包含l时参数就不用以NULL结尾
   */
  if( fork() == 0 )
  {
    // in clild 
    printf( "1------------execl------------\n" );
    if( execl( "/bin/ls", "ls","-a", NULL ) == -1 )
    {
      perror( "execl error " );
      exit(1);
    }
  }
  
  /**
   *创建子进程并调用函数execv,包含v的特点 
   *execv中希望接收一个以NULL结尾的字符串数组的指针,
   *也可以说可以通过外加参数比如:./a -l 等价于 ls -l
   */
  if( fork() == 0 )
  {
    // in child 
    printf("2------------execv------------\n");
	//注意:execv( "/bin/ls","-l",arg) 是错误的写法,其他参数应另外添加
    if( execv( "/bin/ls",arg) < 0)
    {
      perror("execv error ");
      exit(1);
    }
  }
  /**
   *创建子进程并调用 execlp
   *execlp中与execl类似,多了个p
   *l希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
   *p是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
   */
  if( fork() == 0 )
  {
    printf("3------------execlp------------\n");
    if( execlp( "ls", "ls", "-a", NULL ) < 0 )
    {
      perror( "execlp error " );
      exit(1);
    }
  }
  
  /**
   *创建子里程并调用execvp
   *v 望接收到一个以NULL结尾的字符串数组的指针
   *p 是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
   */
  if( fork() == 0 )
  {
    printf("4------------execvp------------\n");
    if( execvp( "ls", arg ) < 0 )
    {
      perror( "execvp error " );
      exit( 1 );
    }
  }
  
  /**
   *创建子进程并调用execle
   *l 希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
   *e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
   */
  if( fork() == 0 )
  {
    printf("5------------execle------------\n");
    if( execle("/bin/ls", "ls", "-a", NULL, NULL) == -1 )
    {
      perror("execle error ");
      exit(1);
    }
  }
  
  /**
   *创建子进程并调用execve
   * v 希望接收到一个以NULL结尾的字符串数组的指针
   * e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
   *在运行时另加参数,如:./a -l 如同执行:ls -l 
   */
  if( fork() == 0 )
  {
    printf("6------------execve-----------\n");
    if( execve( "/bin/ls", arg, NULL ) == 0)
	//必须加上路径,绝对路径或相对路径否则找不到要执行的程序
    {
      perror("execve error ");
      exit(1);
    }
  }
  return EXIT_SUCCESS;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值