Linux进程线程学习笔记:运行新程序
Linux进程线程学习笔记:运行新程序
周银辉
在上一篇中我们说到,当启动一个新进程以后,新进程会复制父进程的大部份上下文并接着运行父进程中的代码,如果我们使新进程不运行原父进程的代码,转而运行另外一个程序集中的代码,这就相当于启动了一个新程序。这里的代码我们可以理解成一个可执行程序。
所以,要运行一个新程序,需要最基本的两步:
1,创建一个可运行程序的环境,也就是进程。
2,将环境中的内容替换成你所希望的,也就是用你希望运行的可执行文件去覆盖新进程中的原有映像,并从该可执行文件的起始处开始执行。
要做到第一点,非常简单,fork函数就可以(参考上一篇) ,要做到第二点,则可以利用exec函数族。
exec是一族函数的简称,包含在<unistd.h>中它们作用都一样,用一个可执行文件覆盖进程的现有映像,并转到该可执行文件的起始处开始执行。
原型如下:
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[]*/);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
我们先以最简单的execl函数为例,其他的大同小异,其第一个参数path是可执行文件的路径,是绝对路径;从arg0参数开始及后面所有的是你要传递给可执行文件的命令行参数,值得注意的是,arg0是可执行文件本身(还记得C语言中老师讲main函数参数列表时所说的么),当然,不传程序本身或传一些乱七八糟的值并不代表不能通过编译或不能运行,只不过,如果可执行文件要用到arg0时会产生一些迷惑;最后有一个注释/*, (char*)0 */是提醒我们最后一个参数应该传空字符串。如何函数运行成功,则不会有任何返回值,否则返回-1,而具体的错误号会被设置在errno,errno是一个全局变量,用于程序设置错误号,跟win32的getLastError函数类似。
看下面的例子:
#include <stdio.h>
#include <unistd.h>
int main ()
{
printf("app start...\n");
execl("/bin/ls", "/bin/ls", "-l",NULL);
printf("app end\n");
return 0;
}
我们运行了bin目录下的ls程序,参数arg0时ls程序本身路径,arg1为-l,使得其以列表的形式列举当前目录,在我的计算机上程序输出如下:
app start...
total 12
-rw-r--r-- 1 zhouyh zhouyh 273 2010-09-06 11:09 temp.c
-rwxr-xr-x 1 zhouyh zhouyh 7175 2010-09-06 11:09 temp.exe
ls程序运行成功了。但注意到了吗?没有输出“app end”这个字符串,原因很简单,我们没有新起进程,而是直接用ls程序覆盖了main函数所在的进程。
那我们接下来,试着用fork吧,以免影响原进程。
#include <stdio.h>
#include <unistd.h>
int main ()
{
printf("app start...\n");
if(fork() == 0)
{
execl("/bin/ls", "/bin/ls", "-l", NULL);
}
printf("app end\n");
return 0;
}
我们用fork创建了一个新进程,当其成功创建后(返回值为0),我们用execl来加载ls程序并运行之。
程序的输出如下:
app start...
app end
zhouyh@ubuntu:~/Documents$ total 12
-rw-r--r-- 1 zhouyh zhouyh 229 2010-09-06 15:59 temp.c
-rwxr-xr-x 1 zhouyh zhouyh 7211 2010-09-06 16:00 temp.exe
程序的所有输出都OK了,但有一点可能和我们想象的不一样,那就是“app end”这个字符串很早就输出了而不是在最后,其实这并没有错,“app end” 是main函数所在的程序(temp.exe)即将结束时输出的,而列举文件目录的ls却完全在另外一个进程中,两个异步执行的进程,他们没有谁先谁后结束可言。
如果我们希望所有工作完成之后,即ls也执行玩以后,才输出“app end”,那么可以使用wait 以及waitpid函数,这里简单说一下wait,具体的会在“Linux进程线程学习笔记:进程控制”中讲。
pid_t wait (int * status); //包含在 <sys/wait.h> 中
wait函数讲当前进程休眠,直到该进程的某个子进程结束或者有特定的信号来唤醒。如果子进程正常结束,则讲子进程的进程id(pid)作为返回值,发生错误则返回-1,而status参数讲传出子进程的结束状态值。
针对刚才的例子,可以参考下面的代码:
#include <stdio.h> //for printf(const char)
#include <unistd.h> //for fork()
#include <sys/wait.h> //for wait(int* status)
int main ()
{
printf("app start...\n");
if(fork() == 0)
{
execl("/bin/ls", "/bin/ls", "-l", NULL);
}
int status;
wait(&status);
printf("app end\n");
return 0;
}
程序输出如下:
app start...
total 12
-rw-r--r-- 1 zhouyh zhouyh 337 2010-09-06 16:22 temp.c
-rwxr-xr-x 1 zhouyh zhouyh 7247 2010-09-06 16:22 temp.exe
app end
好了,现在回过头来看除execl外的其他几个函数 :
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
execlp和execl差不多,但区别在于前者会去系统环境变量查找file所指的程序的位置,所以如果通过环境变量能找到可执行文件,则file可以不是绝对路径了,比如 execlp("ls", "ls", "-l", NULL);
int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[]*/);
与execlp不同的是,其最后一个参数作为你自定义的环境变量参数传进去,而不是查找系统环境变量
char *env[] = { "HOME=/usr/home", "LOGNAME=home",(char *)0 };
execle("/bin/ls", "ls", "-l", NULL, env);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
这三个函数和前面的三个类似,函数名由后缀l变成了v,其表达的含义是参数不再用参数列表传递而是用一个参数数组argv[],当然,数组最后一个元素也必须是char* 0
名字这么相近的函数,感觉好容易混淆,那么就从l,v,p,e 这样的后缀来区分吧:
l:参数为一个逗号分隔的参数列表,并以char* 0作为列表结尾
v: 参数为字符串数组,数组的最后一个元素为char* 0
p: 可以通过系统环境变量查找文件位置
e:调用者显示传入环境变量
Linux进程线程学习笔记:运行新程序的更多相关文章
- linux 进程学习笔记-运行新进程
我们知道,当用fork启动一个新进程以后,新进程会复制父进程的大部份内存空间并接着运行父进程中的代码,如果我们使新进程不运行原父进程的代码,转而运行另外一个程序集中的代码,这就相当于启动了一个新程序. ...
- linux进程的学习笔记(未完)
1. 进程是程序执行的一个实例,如果16个用户同时运行vi,那么有16个独立的进程,尽管它们共享同一个可执行代码,问题在于FreeRTOS这种系统,是否可以建2个相同的任务,需要注意什么?在linux ...
- Linux进程间通信IPC学习笔记之管道
基础知识: 管道是最初的Unix IPC形式,可追溯到1973年的Unix第3版.使用其应注意两点: 1)没有名字: 2)用于共同祖先间的进程通信: 3)读写操作用read和write函数 #incl ...
- Linux进程间通信IPC学习笔记之有名管道
基础知识: 有名管道,FIFO先进先出,它是一个单向(半双工)的数据流,不同于管道的是:是最初的Unix IPC形式,可追溯到1973年的Unix第3版.使用其应注意两点: 1)有一个与路径名关联的名 ...
- C#线程学习笔记九:async & await入门二
一.异步方法返回类型 只能返回3种类型(void.Task和Task<T>). 1.1.void返回类型:调用方法执行异步方法,但又不需要做进一步的交互. class Program { ...
- Linux下iptables学习笔记
Linux下iptables学习笔记 在Centos7版本之后,防火墙应用已经由从前的iptables转变为firewall这款应用了.但是,当今绝大多数的Linux版本(特别是企业中)还是使用的6. ...
- Linux防火墙iptables学习笔记(三)iptables命令详解和举例[转载]
Linux防火墙iptables学习笔记(三)iptables命令详解和举例 2008-10-16 23:45:46 转载 网上看到这个配置讲解得还比较易懂,就转过来了,大家一起看下,希望对您工作能 ...
- linux基础命令学习笔记(二)
linux基础命令学习笔记(二) 1.kill :终止进程 kill pid (唯一标示一个进程) kill -9 强制终止 kill -15 命令未结束不能终止 # ps aux 查看所有进程 ...
- 兄弟连Linux运维学习笔记
最新经典linux运维兄弟连Linux运维学习笔记... --------------- 全程1.5倍播放.加油我一定可以学完Linux----------------------Unix与Linux ...
随机推荐
- Centos 7 安装google 浏览器(yum 方式)
过程: 1 vim /etc/yum/repo.s/google_chrome.repo 2 添加如下内容: [google-chrome] name=google-chrome ...
- ACM_出题人这样不好吧
出题人这样不好吧 Time Limit: 2000/1000ms (Java/Others) Problem Description: 作为编协的第一次月赛,肯定是要有防AK(ALL KILL)的题目 ...
- html基础代码演示2
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...
- jQuery :even
此选择器匹配所有索引值为偶数的元素,从0开始计数. jQuery1.0版本添加. 语法结构: jQuery( ":even" ) 代码实例: <!doctype html&g ...
- FFmpeg编译Android版本
FFmpeg是很好用的一个音视频库,功能强大,但是用起来并不是很方便.之前一直不想用FFmpeg,因为感觉编译太麻烦,但是到了不得不用的时候了,没办法,参考了网上大神的方法,在这里自己也记录一下方便以 ...
- JS——“==”与“===”
==: 两个等于号只是比较两个变量的值 var n1 = 1; var n2 = "1"; alert(n1 == n2);//返回true ===: 三个等于号不仅比较值而且比较 ...
- JS——indexOf replace search
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置 注释:indexOf() 方法对大小写敏感!如果要检索的字符串值没有出现,则该方法返回 -1. 语法:searchvalue, ...
- angular 零碎
相关链接 api(需要FQ) ui-router 知乎 作用域 angular 中作用域的概念是一个亮点,由不同的指令.controller等作用域组成的作用域树就是一个app.简单理解一个contr ...
- 2016.01.05 DOM笔记(一) 查找元素
DOM节点的种类 元素和标签是一个意思,例如<body>标签或者称为<body>元素 节点DOM的节点分为三类 元素节点,文本节点,属性节点 例如 <div id=‘b ...
- Centos6.7 ELK日志系统部署
Centos6.7 ELK日志系统部署 原文地址:http://www.cnblogs.com/caoguo/p/4991602.html 一. 环境 elk服务器:192.168.55.134 lo ...