Linux 进程(二)

前言

fork创建一个子进程的一般目的有哪些呢?
(1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络进程中是常见的—父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
(2)一个进程要执行不同的程序。这对shell是最常见的情况,这种情况下,子进程从fork返回后立即调用exec。

上一节我们详细介绍了第一点,接下来我们介绍下第二点。
我们用fork函数创建新进程后,经常在新进程调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有发生改变。

API介绍

一、exec族:execl, execlp, execle, execv, execvp, execvpe

功能:在调用进程内部执行一个可执行文件,可执行文件既是可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
函数原型:
#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[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
返回:exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字(没有带路径),且arg必须以NULL结束
argv[]:可执行文件所带参数指针数组
file:如果参数file中包含/,则就将其视为路径名,否则就按PATH环境变量,在它所指定的各目录中搜寻可执行文件

exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l:使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后该数组的地址作为这些函数的参数
e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量。

带有l的exec族函数,包括execl, execlp, execle,要求将新程序的每个命令行参数都说明为一个单独的参数,这种参数表以空指针结尾。注意第一个命令行参数即argv[0]为可执行文件名字。

以execl函数为例子来说明:

#include <unistd.h>
#include <stdio.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main()
{
		printf("before exec!\n");
        if(execl("./pro","pro","hello","world",NULL)==-1)
        {
                printf("exec failed!\n");
				perror("why");
        }
        printf("code end!\n");
        return 0;
}
//文件pro.c
#include <stdio.h>
int main(int argc,char *argv[])
{
        int i=0;
        printf("argc:%d\n",argc);
        printf("argv:\n");
        for(i=0;i<argc;i++)
        {
                printf("%s\n",*argv);
                argv++;
        }
        return 0;
}

如果该路径不存在pro可执行文件,调用失败,会设置errno并返回-1,通过perror函数打印错误原因。同时程序也会往下运行,打印字符串”code end!”。

before exec!
exec failed!
why: No such file or directory
code end!

如果调用成功,程序不会往下运行,不打印字符串”code end!”,直接执行pro,输出命令行参数信息。

before exec!
argc:3
argv:
pro
hello
world

如何将当前路径添加到环境变量?
查看PATH环境变量。

Ubuntu@Embed_Learn:~/learn/process$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

通过pwd指令获取当前路径。

Ubuntu@Embed_Learn:~/learn/process$ pwd
/home/Ubuntu/learn/process

通过添加当前路径到PATH环境变量。

Ubuntu@Embed_Learn:~/learn/process$ export PATH=$PATH:/home/Ubuntu/learn/process

查看当前路径添加到PATH环境变量是否成功。

Ubuntu@Embed_Learn:~/learn/process$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/Ubuntu/learn/process

带有p的exec族函数,包括execlp, execvp, execvpe,如果参数file中包含‘/’,则就将其视为路径名,否则就按PATH环境变量,在它所指定的各目录中搜寻可执行文件。

以execlp函数为例子来说明:

#include <unistd.h>
#include <stdio.h>
//函数原型:int execlp(const char *file, const char *arg, ...);
int main()
{
        printf("before exec!\n");
        if(execlp("pro","pro","hello","world",NULL)==-1)
        {
                printf("exec failed!\n");
                perror("why");
        }
        printf("code end!\n"); 
        return 0;
}
//文件pro.c
#include <stdio.h>
int main(int argc,char *argv[])
{
        int i=0;
        printf("argc:%d\n",argc);
        printf("argv:\n");
        for(i=0;i<argc;i++)
        {
                printf("%s\n",*argv);
                argv++;
        }
        return 0;
}

实验结果与execl函数一样,区别在于execlp函数中的file参数不需要带有’/’,只需要将当前路径添加到环境变量PATH即可,如果没有添加的话,会调用失败。除此之外,还可以运行常见指令可执行文件,比如cp、pwd等。
带有v的exec族函数,包括execv, execvp, execvpe,参数argv为可执行文件所带参数指针数组。

以execv函数为例子来说明:

#include <unistd.h>
#include <stdio.h>
//函数原型:int execv(const char *path, char *const argv[]);
int main()
{
		char *argv[4]={"pro","hello","world",NULL};
        printf("before exec!\n");
        if(execv("./pro",argv)==-1)
        {
                printf("exec failed!\n");
                perror("why");
        }
        printf("code end!\n"); 
        return 0;
}
//文件pro.c
#include <stdio.h>
int main(int argc,char *argv[])
{
        int i=0;
        printf("argc:%d\n",argc);
        printf("argv:\n");
        for(i=0;i<argc;i++)
        {
                printf("%s\n",*argv);
                argv++;
        }
        return 0;
}

介绍完exec族函数,我们结合fork函数来进行应用。

例程:写一段程序,当父进程检测到输入为1的时候,创建子进程把配置文件的字段值修改掉。

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
        int data=0;
        pid_t pid;
        while(1)
        {
                scanf("%d",&data);
                if(data==1)
                {
                        pid=fork();
                        if(pid==0)
                        {
					        if(execl("./doconfig","doconfig","LENG","100",NULL)==-1)
					        {
					                printf("exec failed!\n");
									perror("why");
					        }
                        }
                }
                else
                {
                        printf("do nothing!\n");
                }
        }
        return 0;
}
//文件doconfig.c
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc,char** argv){
        int fd;
        char* find_str=malloc(128);
        char* value=malloc(128);
        if(argc!=3)
        {
                printf("arguments errors!\n");
                return -1;
        }
        strcpy(find_str,argv[1]);
        strcpy(value,argv[2]);
        fd=open("config",O_RDWR);
        if(fd==-1)
        {
                printf("no config file\n");
                return -1;
        }
        int size=lseek(fd,0,SEEK_END);
        lseek(fd,0,SEEK_SET);
        char *read_buf=(char*)malloc(sizeof(char)*size+1);
        int n_read=read(fd,read_buf,size);
        if(n_read==-1)
                printf("read fail");
        char* str_start=strstr(read_buf,find_str);
        if(str_start==NULL)
        {
                printf("no designative argument!\n");
                return -1;
        }
        str_start+=strlen(find_str)+1;
        while(1)
        {
                if(*str_start=='\n')
                        break;
                *str_start=*value;
                str_start++;
                value++;
        }
        printf("read_count:%d context:\n%s\n",n_read,read_buf);
        lseek(fd,0,SEEK_SET);
        int n_write=write(fd, read_buf, strlen(read_buf));
        if(n_write==-1)
                printf("write fail!\n");
        close(fd);
        return 0;
}
//文件config
SPEED=5
LENG=125
SCORE=75
LEVEL=95

二、system函数

功能:执行一条shell命令
函数原型:
#include <stdlib.h>
int system(const char *command);
返回:运行成功,则返回进程的状态值。当sh不能执行时,返回127。运行失败,返回-1。
参数说明:
command:shell命令字符串

在命令行执行文件的方式
1、./pro
2、sh -c ./pro

例程:通过system函数把配置文件的字段值修改掉。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
        int status=0;
        status=system("./doconfig LENG 125");
        printf("system status:%d\n",WEXITSTATUS(status));
        return 0;
}

三、popen函数

功能:执行一条shell命令,并获取执行结果
函数原型:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
返回: FILE型指针
参数说明:
command:shell命令字符串
type:”r”—读 “w”—写

例程:编写一段代码,通过popen函数把配置文件的字段值修改掉,并获取结果存储到ret数组。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
        FILE* fp;
        char ret[1024];
        memset(ret,'\0',1024); 
        fp=popen("./doconfig LENG 125","r");
        int nread=fread(ret,1,1024,fp);
        printf("nread:%d bytes ret:%s\n",nread,ret);
        pclose(fp);
        return 0;
}

popen函数与system函数区别在于popen函数可以获取到执行结果,而system函数不可以。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一盆电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值