如何在一个程序中运行另外一个程序:exec系列调用

#include <unistd.h>extern char **environ;int execl(const char *path, const char *arg, .../* (char  *) NULL */);int execlp(const char *file, const char *arg, .../* (char  *) NULL */);int execle(const char *path, const char *arg, .../*, (char *) NULL, 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[]);// path 为程序路径,file为文件名(需要配置了环境变量才找得到),argv,arg都会被传递给新程序的main函数,evnp用于设置新程序的环境变量,若未设置则使用environ

exec不会关闭原程序打开的文件描述符,除非该文件描述符设置了SOCK_CLOEXEC等属性

失败才返回(-1)在原进程的调用点向下接着执行,成功则原程序exec之后的内容都不执行,调用后原进程的实体:代码段,数据段,堆栈等都被取代,只留下进程ID,等保持原样

使用execl,execl的第一个参数为路径

#include<stdio.h>
#include<unistd.h>int main()
{//使用execl()pid_t pid = fork();if(pid>0){printf("parent process pid = %d\n",getpid());}else if(pid==0){//注意这里是可执行程序,而不是程序源码execl("hello","hello",NULL);//第一个参数为路径,第二个为可执行程序名,以NULL结束printf("child process\n");//因为是在子进程中使用exec族,所以此行代码不会被执行}for(int i  = 0;i<3;i++){printf("i = %d pid = %d\n",i,getpid());//此处的代码也只会有父进程的执行}return 0;
}

execlp的第一个参数为可执行文件名,不是路径

函数回到环境变量中查找指定的可执行文件,找不到就执行失败返回-1

#include<stdio.h>
#include<unistd.h>int main()
{//使用execl()pid_t pid = fork();if(pid>0){printf("parent process pid = %d\n",getpid());}else if(pid==0){//注意这里是可执行程序,而不是程序源码execlp("ps","ps","aux",NULL);//第一个参数为可执行文件名,第二个为可执行程序名,以NULL结束printf("child process\n");//因为是在子进程中使用exec族,所以此行代码不会被执行}for(int i  = 0;i<3;i++){printf("i = %d pid = %d\n",i,getpid());//此处的代码也只会有父进程的执行}return 0;
}

查看环境变量:echo $PATH

env在这里面找到PATH条目

使用execvpe:会报错参数过多,但是man文档中写的是有三个参数

使用execve

//指定可执行文件搜索路径
#include<stdio.h>
#include<unistd.h>int main()
{//使用execl()pid_t pid = fork();if(pid>0){printf("parent process pid = %d\n",getpid());}else if(pid==0){//注意这里是可执行程序,而不是程序源码char *arglist[] = {"./hello","hello",NULL};char *envp[] = {"PATH=/root/unix_linux/chapter8",NULL};execve("hello",arglist,envp);//第一个参数为可执行文件名,第二个为可执行程序名,以NULL结束printf("child process\n");//因为是在子进程中使用exec族,所以此行代码不会被执行}for(int i  = 0;i<3;i++){printf("i = %d pid = %d\n",i,getpid());//此处的代码也只会有父进程的执行}return 0;
}

例如运行ls -la命令,调用execvp("ls",arglist);此处的arglist为命令行的字符串数组

流程:

graph LR; 程序条用execvp-->内核从磁盘载入程序; 内核从磁盘载入程序-->内核将arglist复制到进程; 内核将arglist复制到进程-->内核调用main;

第一个元素要置为程序名称,最后一个元素为NULL(0)

#include<stdio.h>
#include<unistd.h>
int main()
{char *arglist[3];arglist[0] = "ls";arglist[1] = "-l";arglist[2] = 0;printf("exec ls-l\n");execvp("ls",arglist);printf("ls -l done\n");return 0;
}/*
root@ziggy-virtual-machine:~/unix_linux/chapter8# gcc -o execdemo execdemo.c
root@ziggy-virtual-machine:~/unix_linux/chapter8# ./execdemo
exec ls-l
总用量 64
-rwxr-xr-x 1 root root 8720 10月 26 15:25 execdemo
-rw-r--r-- 1 root root  234 10月 26 15:25 execdemo.c
-rwxr-xr-x 1 root root 8760 10月 26 14:37 forkdemo
-rwxr-xr-x 1 root root 8712 10月 26 14:48 forkdemo2
-rw-r--r-- 1 root root  248 10月 26 14:48 forkdemo2.c
-rwxr-xr-x 1 root root 8760 10月 26 15:08 forkdemo.3
-rw-r--r-- 1 root root  359 10月 26 15:08 forkdemo3.c
-rw-r--r-- 1 root root  335 10月 26 14:37 forkdemo.c
*/

指定环境变量查找路径:

关于execve:

第三个参数,即为envp数组,不是用于查找课执行程序的,而是为可执行程序运行期间增加新的环境变量

shell程序

#include<stdio.h>
#include<string.h>
#include<unistd.h>#define ARGLEN 5
#define MAXARGS 20int main()
{char argbuf[ARGLEN];fgets(argbuf,ARGLEN,stdin);//要留一位给'\0',所以只有ARGLEN-1位有效字符printf("%d\n",strlen(argbuf)-1);printf("%c\n",argbuf[strlen(argbuf)-1]);printf("%s\n",argbuf);if(argbuf[strlen(argbuf)]=='\0'){printf("yes\n");}return 0;
}/*
root@ziggy-virtual-machine:~/unix_linux/chapter8# ./psh1 hello
3
l
hell
yes
*/char *makesting(char *buf)
{char *cp;printf("%d\n",strlen(buf));printf("%s\n",buf);printf("%c\n\n\n\n",buf[strlen(buf)-1]);buf[strlen(buf)-1]='\0';printf("%s\n",buf);cp = malloc(strlen(buf)+1);strcpy(cp,buf);printf("%s\n",cp);// buf[]return NULL;
}

为什么此函数中要将最后一位置为'\0'

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#define ARGLEN 100
#define MAXARGS 20char *makesting(char *buf)
{char *cp;// printf("%d\n",strlen(buf));// printf("%s\n",buf);// printf("%c\n\n\n\n",buf[strlen(buf)-1]);// if(buf[strlen(buf)]=='\0')// {//     printf("yes\n");// }// buf[strlen(buf)]='\0';//这样是错的printf("%d\n",strlen(buf));//生成字符串,c风格字符串是'\0'结尾,例如ls:strlen(buf) = 3,l,s分别在0,1下标处,所以应该在下标为2处设为'\0',buf[strlen(buf)-1] = '\0';// printf("%s\n",buf);cp = malloc(strlen(buf)+1);strcpy(cp,buf);// printf("%s\n",cp);return cp;
}
int excute(char*arglist[])//执行命令后就退出了,所以不能执行多条命令,且exec在错误时才返回值,且此原进程的代码被清除,会在此进程运行新的exec中的代码,所以shell无法接收新命令
{execvp(arglist[0],arglist);perror("execvp failed");exit(1);
}
int main()
{char argbuf[ARGLEN];int numsargs = 0;//用于记录一条命令的当前参数char* arglist[MAXARGS+1];while(numsargs<MAXARGS){printf("Arg[%d]?",numsargs);if(fgets(argbuf,ARGLEN,stdin)&&*argbuf!='\n'){arglist[numsargs++] = makesting(argbuf);}//要留一位给'\0',所以只有ARGLEN-1位有效字符else{if(numsargs>0){arglist[numsargs] = NULL;excute(arglist);//执行程序numsargs = 0;//reset参数个数,以便执行下一条命令}}}return 0;
}

execvp用命令指定的程序代码覆盖了shell代码,然后在命令指定的程序中运行结束后退出,所以shell无法接收新命令

所以要启动新进程来运行命令指定的程序

exec用新的程序替换原进程的用户区

为什么要设置strlen(buf)-1位'\0':

实现一个真正的shell

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#define ARGLEN 100
#define MAXARGS 20char *makesting(char *buf)
{char *cp;// printf("%d\n",strlen(buf));// printf("%s\n",buf);// printf("%c\n\n\n\n",buf[strlen(buf)-1]);// if(buf[strlen(buf)]=='\0')// {//     printf("yes\n");// }// buf[strlen(buf)]='\0';//这样是错的// printf("%d\n",strlen(buf));//生成字符串,c风格字符串是'\0'结尾,例如ls:strlen(buf) = 3,l,s分别在0,1下标处,所以应该在下标为2处设为'\0',buf[strlen(buf)-1] = '\0';// printf("%s\n",buf);cp = malloc(strlen(buf)+1);strcpy(cp,buf);// printf("%s\n",cp);return cp;
}
int excute(char*arglist[])//执行命令后就退出了,所以不能执行多条命令,且exec在错误时才返回值,且此原进程的代码被清除,会在此进程运行新的exec中的代码,所以shell无法接收新命令
{int pid,status;pid = fork();switch (pid){case -1:perror("fork failed");break;case 0:execvp(arglist[0],arglist);perror("execvp failed");exit(1);break;default:while(wait(&status)!=pid);//等待子进程返回printf("child exited with status %d,%d\n",status>>8,status&0377);}
}
int main()
{char argbuf[ARGLEN];int numsargs = 0;//用于记录一条命令的当前参数char* arglist[MAXARGS+1];while(numsargs<MAXARGS){printf("Arg[%d]?",numsargs);if(fgets(argbuf,ARGLEN,stdin)&&*argbuf!='\n'){arglist[numsargs++] = makesting(argbuf);}//要留一位给'\0',所以只有ARGLEN-1位有效字符else{if(numsargs>0){arglist[numsargs] = NULL;excute(arglist);//执行程序numsargs = 0;//reset参数个数,以便执行下一条命令}}}return 0;
}
#运行结果
root@ziggy-virtual-machine:~/unix_linux/chapter8# ./psh2 Arg[0]?ls
Arg[1]?-l
Arg[2]?demodir
Arg[3]?
总用量 0
-rw-r--r-- 1 root root 0 10月 26 22:49 new.cpp
child exited with status 0,0
Arg[0]?ls
Arg[1]?
demodir     forkdemo2    forkdemo.c  psh2
execdemo    forkdemo2.c  psh         psh2.c
execdemo.c  forkdemo.3   psh1        psh.c
forkdemo    forkdemo3.c  psh1.c      waitdemo.c
child exited with status 0,0
Arg[0]?

shell流程:

需要改进的点,会产生的错误:

退出此shell程序的唯一方法是ctrl+c

如果在psh2等待子进程运行时按此键,则其生成的SIGINT信号会杀死运行的子进程和psh2进程

原因:键盘信号发给所有连接的进程

psh2,和子进程都连接到终端,按下中断键,驱动向所有这个终端控制的进程发送SIGINT信号

Linux多进程(二)相关推荐

  1. Linux多进程多线程编程笔记

    文章目录 Linux多进程多线程编程 一.多进程编程 1.fork 函数 2.exec 系列函数 3.wait.waitpid 函数 4.pipe 管道 5.信号量 5.1.semget 5.2.se ...

  2. HTTP服务器项目2:Linux多进程开发

    HTTP服务器项目2:Linux多进程开发 1.进程概述: 01 / 程序和进程 程序是包含一系列信息的文件,这些信息描述了如何在运行时创建一个进程: ◼ 二进制格式标识:每个程序文件都包含用于描述可 ...

  3. Linux -- 多进程编程之 - 守护进程

    内容概要 一.守护进程概述 二.守护进程创建 2.1.创建子进程,父进程退出 2.2.在子进程中创建新会话 2.2.1.进程组和会话期 2.2.2.setsid()函数说明 2.3.改变当前工作目录 ...

  4. Linux(二):VMware虚拟机中Ubuntu安装详细过程

    Linux(二):VMware虚拟机中Ubuntu安装详细过程 文章目录 1 准备 2 安装 2.1 虚拟机的建立 2.2 虚拟机安装Ubuntu系统 2.3 虚拟机设置 3 完成 1 准备 1.操作 ...

  5. linux实验报告makefile,linux实验二交叉编译和Makefile实验报告.doc

    linux实验二交叉编译和Makefile实验报告 实验二 交叉编译和Makefile 实验目的 了解和掌握交叉编译模式和方法: 了解和掌握makefile文件的编写 学会使用交叉编译工具和make工 ...

  6. Linux多线程与Linux多进程混合项目的死锁问题

    目录 背景 线程和fork 内核原理分析 背景 本文并不是介绍Linux多进程多线程编程的科普文,如果希望系统学习Linux编程,可以看<Unix环境高级编程>第3版>. 本文是描述 ...

  7. 精简linux (二)背景图片的设置 网络功能的实现

    精简linux(二) 上一篇写到了精简linux的基本的关机重启阶段,但是我们打造的linux不能仅仅实现这些功能.下面要做的是能实现linux的网络查看主机名等功能等. 首先要做的是看看linux能 ...

  8. Linux多进程编程之在线词典

    在线词典是基于Linux 多进程并发服务器编程,由服务器端和客户端构成,客户端可以运行在多个不同的主机上连接服务器,服务器对员工信息的操作结果通过数据库sqlite来保存.当用户登录后,根据用户名判断 ...

  9. Big-man进军Linux系统(二)

    Big-man进军Linux系统(二) 前言: Big-man现在书写的代码代建在服务器上的,而服务器的环境是Linux, 所以对linux进行一些操作. 所以需要去熟悉一下Linux的指令了. Bi ...

  10. Linux任督二脉之内存管理(三) PPT

    五节课的第三节课-进程的内存消耗和泄漏 *进程的VMA. *进程内存消耗的4个概念:vss.rss.pss和uss *page fault的几种可能性,major和minor *应用内存泄漏的界定方法 ...

最新文章

  1. linux下使profile和.bash_profile立即生效的方法
  2. HDU 5616 Jam's balance(01背包)
  3. Meta 发布 Bean Machine 帮助衡量 AI 模型的不确定性
  4. 基于深度学习的Person Re-ID(综述)
  5. Linux内核中大小端判定宏
  6. postgresql mysql fdw_PostgreSQL使用MySQL外表(mysql_fdw)
  7. opencv 通过网络连接工业相机_Raspberry Pi上的OpenVINO,OpenCV和Movidius NCS
  8. 201621123058 《java课程设计》第九周学习总结
  9. (85)FPGA显示激励(monitor)
  10. python怎么背景实现循环_在Python的一段程序中如何使用多次事件循环详解
  11. Leetcode每日一题:127.word-ladder(单词接龙)
  12. 用Heartbeat构建Web Ha
  13. 机器学习中的统计学基础知识
  14. 第一款低代码应用平台搭建的设备管理系统
  15. 异步方法中取消异步操作
  16. RSA加密、解密 JAVA版 lua版 js版
  17. MATLAB期末复习
  18. R shiny 交互式表格
  19. Couldn‘t find meta-data for provider with authority xxx.fileProvider
  20. 如何查询SCI和EI检索号

热门文章

  1. 什么是状态模式(State)?
  2. 修改公众号的微信管理员方法
  3. 无废话硬核分享:Linux 基础知识点总结很详细,全的很,吐血奉献
  4. python爬取豆瓣书籍_python 爬取豆瓣书籍信息
  5. 利用zc序列进行简单的帧同步
  6. TeamWork#3,Week5,Scrum Meeting 11.16
  7. pat乙级 1028 人口普查
  8. zabbix服务端和客户端的搭建
  9. 绩效考核的5大标准是什么?
  10. SF习题答案(2)(LF-Induction)