进程基本-进程创建,僵尸进程,exec系列函数
Linux系统中,进程的执行模式划分为用户模式和内核模式,当进程运行于用户空间时属于用户模式,
如果在用户程序运行过程中出现系统调用或者发生中断事件,就要运行操作系统(即核心)程序,进程的运行模式就变为内核模式
在该模式下运行的进程可以执行机器特权指令,而且该进程的运行不受用户的干预
在Linux操作系统中,通过fork()系统调用来创建子进程
|
目标 |
创建进程 |
|
头文件 |
#include <unistd.h> |
|
函数原型 |
pid_t_result=fork(void) |
|
参数 |
无 |
|
返回值 |
-1 如果出错 0 返回到子进程 pid 将子进程的进程id返回给父进程 |
将执行fork的操作进程称为父进程,被fork()创建的进程称为该进程的子进程,
当父进程执行fork操作时,操作系统内核执行如下任务完成进程的创建工作:
1.为子进程分配新的内存块和内核数据结构
2.复制原来的进程信息到新的进程,即子进程和父进程具有相同的可执行代码
3.向运行进程集添加新的进程
4.fork执行结束后,将控制返回给2个进程,此时两个进程可独立执行,执行顺序取决于进程调度
#include <stdio.h>
main(){
int ret_from_fork,mypid;
mypid=getpid();
printf("Before :my pid is %d\n",mypid );
ret_from_fork=fork();
sleep();
printf("After:my pid is %5d,ret_from_fork:%5d,parent pid :%5d\n",getpid(),ret_from_fork,getppid());
}
[root@centos1 process]# ./fork
Before :my pid is
After:my pid is ,ret_from_fork:,parent pid :
After:my pid is ,ret_from_fork: ,parent pid :
父进程创建子进程后,子进程除了具有相同的代码段拷贝外,也具有相同的数据段,
即父进程的全局变量名称和值子进程也会一起拷贝过去,但他们之间是独立的,可独立改变相互不受影响
#include <sys/types.h> //pid_t 类型的在此
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> //exit 用 int glob=; //全局变量 int main(){
int local;
pid_t pid;
local = ;
if( (pid=fork()) == ){
sleep();
}else if(pid>){
glob =;
local =;
sleep();
}
printf("glob=%d,local=%d,mypid=%d\n",glob,local,getpid());
exit ();
}
[root@centos1 process]# ./var
glob=,local=,mypid=
glob=,local=,mypid= 第一行输出为子进程,第二行未父进程的输出,说明父子进程由相同的代码,但他们变量之间相互不影响
一般情况下,父进程创建子进程是为了执行特定的任务,通过执行exec家族系列调用让子进程执行新的任务
此时,由exec调用提供的命令的指令代码替换子进程的代码,相当于对子进程进行了换脑
int execl(const char*path,const char* arg0,const char* arg1,.... NULL)
int execlp(const char*path,const char* arg0,const char* arg1,.... NULL)
int execv(const char*path,const char* argv[])
int execvp(const char*path,const char* argv[])
|
目标 |
在指定路径中查找并执行一个文件 |
|
头文件 |
#include <unistd.h> |
|
函数原型 |
result=execvp(const *file,const char *argv[]) |
|
参数 |
file 要执行的文件名 argv 字符串数组 |
|
返回值 |
-1 如果出错 若成功,execvp 没有返回值 |
excel,excelp完全相同
excev,execvp 完全相同
excel,excev要求提供可执行文件的绝对路径或相对路径名
带p的 使用$PATH环境变量查找程序
主要区别是:
1.可执行文件的查找方式:不是P结尾的都是完整的目录路径,p结尾的可只给出文件名,系统自动从环境变量中进行查找
2.参数的传递方式:
有2种方式,逐个列举;将所有的参数整体构造指针数组传递,以函数名第5位字母来区分
l(list)的表示逐个列举方式 ,语法为char *arg;
字母为v(vertor)的表示将所有参数整体构造指针数组传递,语法为: *const argv[]
exec系列调用没找到和执行文件返回-1 ,否者进程用可执行文件替换它的代码、数据和堆栈
过程:
1.将制定的程序复制到它的进程
2.用制定的字符串数组作为argvp[]传给这个程序
3.运行这个程序
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> main(){
char *arglist[];
pid_t pid;
arglist[]="-l";
arglist[]="-a";
printf("parent:%d\n",getpid());
pid=fork();
if( pid== ){
printf("son1 pid is:%d\n",getpid() );
execvp("ls",arglist);
printf("son2 pid is:%d\n",getpid() );
}else{
printf("parnet pid:%d\n",getpid() );
}
printf("in %d\n",getpid()); }
/**
execvp 后的当前进程的代码不再执行
root@centos1 c]# ./exec
parent:26307
parnet pid:26307
in 26307
son1 pid is:26308
[root@centos1 c]# . .. exec exec.c fork.c var.c **/
僵尸进程
当父进程还没有结束而子进程结束运行,同时父进程没有使用wait系统调用获取子进程的结束状态时
子进程就成为僵尸进程
父进程先结束不会产生僵尸进程。
僵尸进程没有任何代码、数据和堆栈,占用不了多少资源,但它存在于系统的任务列表中。在进程表里仍占了1个位置占用进程号
一般要避免出现这种情况
当使用ps查看进程时 如果进程名称旁边出现defunct,则表明该进程为僵尸进程
#include <stdio.h>
#include <unistd.h> void parent_code(int delay){
sleep(delay);
}
main(){
pid_t pid;
int status; pid=fork();
if(pid == ){ }
if(pid>){
parent_code();
}
}
/*
[root@centos1 c]# gcc -o zombie zombie.c
[root@centos1 c]# ./zombie&
[1] 28577
[root@centos1 c]# ps
PID TTY TIME CMD
25201 pts/0 00:00:00 bash
28577 pts/0 00:00:00 zombie
28578 pts/0 00:00:00 zombie <defunct>
28580 pts/0 00:00:00 ps
*/
为了防止子进程成为僵尸进程,一般在父进程调用wait()系统调用等待子进程的结束并获取子进程的返回状态
wait调用做2件事:
首先暂停调用它的进程直到子进程结束,然后取得子进程结束时传给exit的值
|
目标 |
等待子进程的结束 |
|
头文件 |
#include <sys/wait.h> #include <sys/types.h> |
|
函数原型 |
pid_t pid=wait(int *status) |
|
参数 |
status指向一个保存子进程返回状态的整型变量 |
|
返回值 |
如果不存在子进程,返回-1 若有任何一个子进程结束,则返回该子进程的pid并保存其返回状态在status中,同时wait调用也结束 |
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> void child(int delay){
sleep(delay);
exit();
} void parent(int *status){
wait(status);
} main(){
pid_t pid;
int status;
printf("Before:%d\n",getpid());
pid=fork();
if(pid == ){
child();
}
if(pid > ){
printf("pid =%d\n",getpid());
parent(&status);
printf("status =%d\n",status);
}
}
/*
[root@centos1 c]# ./a.out
Before:14901
pid =14901
status =0 */
上述代码可防止僵尸进程的产生
waitpid()亦可实现wait()调用类似功能
多数情况下会使用waitpid()
他们的主要区别是:
1.wait()只能得到任何一个子进程结束的状态,当一个父进程有多个子进程时,在某个子进程结束,则wait可得到其结束状态
此时无法再得到其它子进程的结束状态,此时容易产生其它子进程的僵尸进程
2.wait()调用属于阻塞调用,父进程执行该指令后,器等待子进程结束之后才能执行它后面的代码,
而waitpid()可提供非阻塞调用的方式
3.waitpid()调用可以等待指定的子进程具有比wait多的功能
|
目标 |
等待某个子进程的结束 |
|
头文件 |
#include <sys/wait.h> #include <sys/types.h> |
|
函数原型 |
pid_t pid=waitpid(pid_t pid,int *status,int options) |
|
参数 |
pid=-1 等待任一个子进程.与wait等效 pid>0 等待进程id时pid的子进程 pid=0 等待其组id等于调用进程组id的任一子进程 pid<-1 等待其组id等于pid绝对值的任一进程 options选项: WNOHANG 表示如果没有任何已经结束的子进程则马上返回,不等待 WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会 0 作用和wait一样,阻塞父进程,等待子进程结束 |
|
返回值 |
如果不存在子进程,返回-1 若有指定子进程结束,则返回该子进程的pid并保存其返回状态在status中,同时waitpid调用也结束 |
子进程退出状态检测的宏
|
宏 |
说明 |
|
WIFEXITED(status) |
如果子进程正常结束,则为非0值,此时可调用WEXITSTATUS(status) 取得子进程exit()返回的结束代码 |
|
WIFSIGNALED(status) |
如果子进程是因为信号则此宏的值为真, 此时可用WIERMSIG(status)取得子进程因信号而终止的信号代码 |
|
WIFSTOPPED(status) |
如果子进程处于暂停执行情况则此宏为真.采用WSTOPPSIG(status) 取得引发子进程暂停的信号代码 |
进程基本-进程创建,僵尸进程,exec系列函数的更多相关文章
- linux系统编程之进程(五):exec系列函数(execl,execlp,execle,execv,execvp)使用
本节目标: exec替换进程映像 exec关联函数组(execl.execlp.execle.execv.execvp) 一,exec替换进程映像 在进程的创建上Unix采用了一个独特的方法,它将进程 ...
- exec系列函数和system函数
一.exec替换进程映象 在进程的创建上Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离.这样的好处是有更多的余地对两种操作进行管理.当我们创建 了一个进程之后,通常将子进程替换成新 ...
- exec系列函数(execl,execlp,execle,execv,execvp)使用
本节目标: exec替换进程映像 exec关联函数组(execl.execlp.execle.execv.execvp) 一,exec替换进程映像 在进程的创建上Unix采用了一个独特的方法,它将进程 ...
- fork()和vfork()的区别,signal函数用法,exec()系列函数的用法小结
一:fork()和vfork()的区别: fork()函数可以创建子进程,有两个返回值,即调用一次返回两个值,一个是父进程调用fork()后的返回值,该返回值是刚刚创建的子进程的ID;另一个是子 ...
- Linux系统编程(9)—— 进程之进程控制函数exec系列函数
在Linux中,并不存在exec()函数,exec指的是一组函数,一共有6个,分别是: #include <unistd.h> extern char **environ; int exe ...
- exec系列函数详解
execve替换进程映像(加载程序):execve系统调用,意味着代码段.数据段.堆栈段和PCB全部被替换.在UNIX中采用一种独特的方法,它将进程创建与加载一个新进程映像分离.这样的好处是有更多的余 ...
- linux的子进程调用exec( )系列函数
exec( )函数族 : 以下我们来看看一个进程怎样来启动还有一个程序的运行.在Linux中要使用exec函数族.系统调用execve()对当前进程进行替换,替换者为一个指定的程序,其參数包含文件名称 ...
- C语言在Linux下创建一个僵尸进程
第三章编程题3.12 1.僵尸进程是什么 每一个进程都有一个PCB(进程控制块),其中包含进程执行的状态等一系列信息. 当父进程fork()出一个子进程,子进程执行结束后操作系统会回收子进程使用的内存 ...
- 进程与fork()、wait()、exec函数组
进程与fork().wait().exec函数组 内容简介:本文将引入进程的基本概念:着重学习exec函数组.fork().wait()的用法:最后,我们将基于以上知识编写Linux shell作为练 ...
随机推荐
- 安装tensorflow报ImportError: libcublas.so.9.0: cannot open shared object file的解决方法【转】
本文转载自:https://blog.csdn.net/qq_37274615/article/details/81099738 转载自:https://blog.csdn.net/qysh123/a ...
- pix2pix-tensorflow搭建及其使用
目录 pix2pix-tensorflow搭建过程 1. 环境搭建 2. 环境说明 3. 开始搭建 4. 训练结果说明 5. 数据集 5.1 图片格式说明 5.3 从先用图片创建图像对 5.4 如何进 ...
- mybatis generator为实体类生成自定义注释(读取数据库字段的注释添加到实体类,不修改源码)
我们都知道mybatis generator自动生成的注释没什么实际作用,而且还增加了代码量.如果能将注释从数据库中捞取到,不仅能很大程度上增加代码的可读性,而且减少了后期手动加注释的工作量. 1.首 ...
- 服务器jupyter配置与ssh远程登录
jupyter 配置 首先安装jupyter,在anaconda套装中已包含,如果安装的是精简版的miniconda则通过conda install jupyter安装. 生成配置文件 jupyter ...
- 史上最强大的40多个纯CSS绘制的图形[转]
今天在国外的网站上看到了很多看似简单却又非常强大的纯CSS绘制的图形,里面有最简单的矩形.圆形和三角形,也有各种常见的多边形,甚至是阴阳太极和网站小图标,真的非常强大,分享给大家. Square(正方 ...
- Vue.js系列之项目搭建(1)
项目搭建具体步骤如下: 1.安装node 到官网下载安装,我这里是win7系统. (中)https://nodejs.org/zh-cn/ (英)https://nodejs.org/en/ 2.安装 ...
- 通过电信ADSL无线猫WLAN上网的方法
本教程只适合中国电信ADSL无线猫使用wifi(路由器不适合此帖)我的无线猫是电信赠送的华为[EchoLife]HG522c,亲测可用,解决网关无回应! 首先打开IE(注意,只能是IE,其他内核的浏览 ...
- hdu 3687 10 杭州 现场 H - National Day Parade 水题 难度:0
H - National Day Parade Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & % ...
- easyui combobox 动态加载数组数据
怕自己忘了,记录下来以后用方便 html部分 <input id="rzcode" name="businesItemId" style="wi ...
- BOM之其他浏览器对象的使用
body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...