父子进程exec,fork等WEXITSTATUS,僵尸进程

linux系统编程之进程(五):exec系列函数(execl,execlp,execle,execv,execvp)使用-CSDN博客

Linux下Fork与Exec使用 - 知乎

C++中的exec()函数_c++ exec函数_向阳逐梦的博客-CSDN博客

前言
fork 函数之后,如果想要把子进程换成一个我想要执行的进程,这时,就不得不使用 exec()函数了,这也是 fork()的意义所在。当然,exec系列的函数也可以将当前进程替换掉,不一定非要fork()一个子进程。常见的fork()调用例子有很多,比如从 wechat发起一个语音电话、从 bash或者zsh执行一个 a.out 程序,都是在利用exec系统调用将新产生的子进程完全替换成目标进程。

比如,这是一个死循环程序(目的是为了观察,让它活得久一点):

C++并行编程_c++ exec函数-CSDN博客

2.2 exec函数族
目的:让子进程不执行父进程正在执行的程序

exec函数族提供了让进程运行另一个程序的方法。exec函数族内的函数可以根据指定的文件名或目录名找到可执行程序,并加载新的可执行程序,替换掉旧的代码区、数据区、堆区、栈区与其他系统资源。这里的可执行程序既可以是二进制文件,也可以是脚本文件。在执行exec函数族函数后,除了该进程的进程号PID,其他内容都被替换了。
-----------

 所需头文件:#include<unistd.h>

    函数原型:

        //excel("/bin/ps","ps","-ef",NULL)
        //系统执行ps -ef,注意参数的写法
        int execl(const char *path, const char *arg,…)
 
 
        //execlp("ps","ps","-ef",NULL)
        //第一个参数只需要写ps即可,系统会根据环境变量自行寻找ps程序的位置
        int execlp(const char *file, const char *arg,…)
          
          
        //execle()函数将一个新的环境变量添加到子进程中
        // char *envp[]={"PATH=/tmp","USER=liyuge",NULL};
        //设定新的环境变量,注意使用NULL结尾
        //execle("/usr/bin/env","env",NULL,envp)
 
        int execle(const char *path, const char *arg,…, char *const envp[])
 
       
 
        //char *arg[]={"ps","-ef",NULL};execvp("ps",arg)
        //注意参数与execlp()函数的区别
        int execv(const char *path, char *const argv[])
 
 
 
        int execvp(const char *file, char *const argv[])
 
 
 
 
        //char *arg[]={"env",NULL};//设定参数向量表,注意使用NULL结尾
        //char *envp[]={"PATH=/tmp","USER=liyuge",NULL};
        //设定新的环境变量,注意使用NULL结尾
 
        int execve(const char *path, char *const argv[], char *const envp[])

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

int main(int argc, char *argv[])
{
    //char * const envp[] = {"AA=11", "BB=22", NULL};
    printf("Entering main ...\n");
    int ret;
    ret =execl("./hello", "hello", NULL);
    //execle("./hello", "hello", NULL, envp);
    if(ret == -1)
        perror("execl error");
    printf("Exiting main ...\n");
    return 0;
}

二、进程
2.1  fork()函数
 函数fork()

    所需头文件:#include<sys/types.h>

                        #include<unistd.h>

    函数原型:pid_t fork()

    函数参数:无

    函数返回值:

          0    子进程

        >0    父进程,返回值为创建出的子进程的PID

         -1    出错

以发现子进程和父进程之间并没有对各自的变量产生影响。

一般来说,fork之后父、子进程执行顺序是不确定的,这取决于内核调度算法。进程之间实现同步需要进行进程通信

vfork与fork对比:

相同:

返回值相同

不同:

fork创建子进程,把父进程数据空间、堆和栈复制一份;vfork创建子进程,与父进程内存数据共享;

vfork先保证子进程先执行,当子进程调用exit()或者exec后,父进程才往下执行

为什么需要vfork?

因为用vfork时,一般都是紧接着调用exec,所以不会访问父进程数据空间,也就不需要在把数据复制上花费时间了,因此vfork就是”为了exec而生“的。

结束子进程的调用是exit()而不是return,如果你在vfork中return了,那么,这就意味main()函数return了,注意因为函数栈父子进程共享,所以整个程序的栈就跪了。

系统编程二:vfork+exec+进程通信方式管道+信号_exec在vfork-CSDN博客

#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
 
 
//int a = 100;
 
int main()
{
	//原本进程的代码
	//int a = 100; //a是局部变量,是栈区
	static int a = 100; //a是静态变量,存放在数据段
	printf("main\n");
	
	//使用vfork创建一个子进程,父进程与子进程共享数据段
	pid_t id = vfork();
	if(id == -1)//出错
	{
		printf("fork error\n");
		return -1;
	}
	else if(id >0)//父进程
	{
		
		printf("parent:%d a addr:%p value:%d\n",getpid(),&a,a);		
	}
	else if(id == 0)//子进程
	{		
		a = 250; //子进程修改变量a的值
		printf("child:%d a addr:%p value:%d\n",getpid(),&a,a);
		sleep(5);//这里就算延时5s,父进程也不会执行,父进程一定是等子进程结束之后才执行
		exit(0);//让子进程到这里就结束,接下来父进程就开始执行了
	}
	
	printf("111\n");
	//阻塞等待 子进程退出
	wait(NULL);
	
	return 0;
}	

2.2 exec函数族
————————————————
版权声明:本文为CSDN博主「Egozjuer」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42494287/article/details/96134339

TCP/IP,父进程fork产生的子进程变成僵尸进程,僵尸进程产生原因,查看方法,关闭父进程回收僵尸子进程,代码中回收子进程函数wait,waitpid_父子进程间的通信出现僵尸进程怎么处理?-CSDN博客

关于僵死进程的产生原因以及解决方案_进程僵死-CSDN博客
Linux僵尸进程以及wait函数_僵尸进程 wait作用-CSDN博客

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
 
int main()
{
    pid_t pid1,pid2;
    int statu,m = -1;
    pid1 = fork();
    if(0 == pid1)
    {
        printf("This is son! it's PID %d\n",getpid());
        exit(3);                    //结束进程,此处返回一个特殊的值3
    }
    if(0 < pid1)
    {
        pid2 = wait(&statu);        //父进程等待子进程执行,子进程结束后的状态保存在statu里
        if (WIFEXITED(statu))       //如果为真,子进程正常结束,提取状态
        {
            m = WEXITSTATUS(statu);
        }
        printf("This is father!\n");
        printf("son exit code is %d\n",m);
        printf("son PID is %d\n",pid2);
    }
    return 0;
}


#include <iostream>
#include <stdio.h>
#include <unistd.h> // for getpid() function
#include <sys/wait.h>
#include <string.h>

int main() {

    char *p = (char*)malloc(5000);
    strcpy(p, "ifsddsffdsdsdfsfdssdffds");

    FILE* pipe = popen("ls", "r"); // 这里以"ls"命令为例,也可以根据需求修改成其他命令或脚本

    if (pipe == nullptr) {
        std::cout << "Failed to create child process." << std::endl;
        return -1;
    }

    pid_t pid = fork(); // 创建子进程

    if (pid > 0) {
        int status;

        wait(&status); // 等待子进程结束

        pclose(pipe); // 关闭管道

        std::cout << "Child process ID is: " << pid << std::endl;
    } else if (pid == 0) {
        char *a = (char*)malloc(100);
        sleep(60);
        // 子进程部分
        char buffer[256];
        while (!feof(pipe)) {
            fgets(buffer, sizeof(buffer), pipe);

            printf("result: %s\n", buffer);
        }

        exit(EXIT_SUCCESS);
    } else {
        std::cerr << "Fork failed!" << std::endl;
        return -1;
    }

    return 0;
}

一、僵尸进程产生原因:
1、子进程return。
2、子进程exit。
3、父进程没return或exit,父进程没有主动请求获取子进程的返回值。

二、查看僵尸进程的方法:
ps au
看运行状态是Z+的进程,就是zombie进程。

僵尸进程的解决方法:关闭父进程,或者在父进程中调用后面的函数wait和waitpid。

*三、wait函数,父进程主动获取某个结束的子进程的返回值。
pid_t wait(int wstatus);
返回值是子进程的pid,wstatus是子进程的返回值信息,如果父进程调用wait时没有子进程结束,父进程就会阻塞blocking并等到有子进程结束,获取子进程的信息。

*四、waitpid函数
pid_t waitpid(pid_t pid, int wstatus, int options);
pid是等待回收的子进程的pid,如果传入-1,可以等任意子进程终止。
wstatus等价于wait中的wstatus,用来看终止信息和返回信息的。
options传递头文件sys/wait.h中声明的常量WNOHANG,这个参数可以让当前没有已返回的子进程时,父进程不会阻塞,而是会返回0然后退出函数。

五、两个宏对wstatus的返回值信息进行判断,获取子进程的结束状况和返回值。
WIFEXITED(wstatus)可以返回子进程的终止信息,子进程正常终止时返回true
WEXITSTATUS(wstatus)可以返回子进程的返回值

调用wait函数回收子进程后都要对子进程的终止信息进行判断并做上日志。

Linux进程控制_linux 两个程序有时序问题怎么办-CSDN博客

  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<assert.h>
void fun(int sign)
{
    pid_t pid=wait(NULL);
    printf("fun=%d\n",pid);//打印子进程到pid
 
}
int main()
{
    signal(SIGCHLD,fun);//子进程结束时会给父进程发送SIGCHLD这个信号,接受到以后父进程调用fun函数,在fun函数中调用wait()给子进程收尸。
    pid_t pid=fork();//创建子进程
    assert(pid!=-1);
    if(pid==0)//子进程
    {
        printf("child start\n");
        sleep(5);
        printf("child end\n");
    }
    else//父进程
    {
        printf("father start\n");
        sleep(10);//为了使僵死进程出现所以父进程晚结束
        sleep(3);//为了给大家截图看状态,后面会有讲解
        printf("father end\n");
 
    }
}

大家看代码知道父进程中睡眠10s,子进程中睡眠5s,5s后子进程结束会给父进程发SIGCHLD信号,父进程接受到信号会被唤醒执行fun()方法调用wait()给子进程收尸,那大家是不是有个问题,父进程本来睡10s但是睡了5s被子进程打断,唤醒去调用了fun(),那不是还有5s的睡眠时间吗?要不要把剩下的5s睡完,答案是不需要,因为运行过程就是执行代码的过程,sleep(10)这行代码已经执行过了,至于效果那就是另一回事,所以父进程下一步做的是执行下一行代码sleep(3),为了给大家截上面的图反映看父子进程的状态,所以让他睡眠3s来打印出此时子进程还在不在进程表中。
现在大家应该理解僵死进程的产生以及解决了吧

fork和exec一起使用

https://www.cnblogs.com/liyou-blog/p/5089279.html

/*************************************************************************
    * File: a.cpp
    * Brief: 
    * Created Time: Wed 23 Dec 2015 08:50:13 AM CST
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<signal.h>
using namespace std;

#define Sleep(ms) usleep(ms * 1000)

int main(int argc,char* argv[])
{
    printf("pid=%d, ",getpid());
    for(int i=0; i<argc;i++)
        printf("[%d]=%s, ",i,argv[i]);
    printf("\n");
    
    struct sigaction sigchld_action;
    sigchld_action.sa_handler = SIG_DFL;
    sigchld_action.sa_flags = SA_NOCLDWAIT;
    sigaction(SIGCHLD, &sigchld_action, NULL);
    
    //new process
    int m_subProcessID=fork(); 
    if (m_subProcessID==0)
    {
        int ret=execl("./slave.out","a=abc","b=def","c=12345",NULL);
        if(0!=ret)
        {
            printf("execl fails.\n");
            return -1;
        }
        printf("execl done wit ok\n");
        return 0;
    }
    for(int i=0;i<10;i++)
    {
        Sleep(2000);
        printf("master say: i=%d\n",i);
    }
    printf("master say: i am done\n");
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值