IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 3分钟带你熟悉进程的等待与替换!!! -> 正文阅读

[系统运维]3分钟带你熟悉进程的等待与替换!!!

进程的相关操作

main函数返回值:返回给操作系统

echo $?//查看最近一次程序退出码

进程退出码:进程退出时的返回值

0:正常退出
!0:退出异常

各个退出码所对应的各个文字信息,例如下面:

#include<stdio.h>
#include<string.h>
int main()
{
    for(int i=0;i<100;i++)
    {
        printf("%d:%s\n",i,strerror(i));
    }
    return 0;
}

image-20210928161244780

进程等待:

为了回收子进程资源,获得子进程退出信息

如果子进程退出了,而父进程没有回收子进程的资源,对子进程不管不顾,那么子进程就会变成僵尸进程,就算是kill
-9也无法杀死,,所以必须对子进程资源进行回收,而这个回收工作通常由父进程来完成,父进程完成子进程资源回收的这个过程叫做进程等待

关于子进程资源回收我们经常用wait/waitpid进行回收

父子进程谁先运行不一定,但是在进程等待之后,子进程先退出,父进程在回收子进程资源后再退出

wait操作

pid_t wait(int status)
返回值:
返回成功返回子进程pid,失败返回-1
参数:
输出型参数,不关心设置为NULL
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
    pid_t id=fork();
    if(id==0)
    {
        int count=0;
        while(count<5)
        {
            printf("I am child ,pid:%d,ppid:%d\n",getpid(),getppid());
            sleep(1);
            count++;
        }
        exit(1);
    }
    else
    {
        int ret=wait(NULL);
        if(ret>=0)
        {
            printf("child exit success:pid:%d\n",ret);
        }
        printf("Father is running\n");
        sleep(5);

    }
    return 0;
}

image-20210928160832534

wait获取子进程相关信息成功,返回子进程的pid,返回失败返回-1

waitpid(pid_t id int*status,int options),头文件是<sys/wait.h>

waitpid当正常返回时返回子进程的id,失败时返回0

阻塞等待的方式

#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    pid_t id=fork();
    if(id==0)
    {
        int count=0;
        while(count<5)
        {
            printf("I am child,pid:%d,ppid:%d\n",getpid(),getppid());
            sleep(1);
            count++;
        }
        exit(10);
    }
    else
    {
        int status=0;
        //waitpid(id,&status,0),最后一个参数为0代表的是阻塞状态,代表父进程在等待子进程的时候没有做其他的事情
        //status次低八位代表退出时的退出码(status>>8)&0xFF代表退出时的退出码
        //status低七位代表退出时的信号,status&0x7F获得进程退出时的信号
        pid_t ret=waitpid(id,&status,0);
        if(ret>=0)
        {
            printf("child exit success,ret:%d\n",ret);
            printf("child exit code:%d\n",(status>>8)&0xFF);
            printf("child exit signal:%d\n",status&0x7F);
        }
        printf("Father is running\n");
        sleep(5);
    }
    return 0;
}

image-20210928165426863

等待成功,并不意味着进程运行成功了,仅仅代表着进程退出了,要看进程是否运行成功还要看进程退出时发出的信号,例:

image-20210928170359734

如上所示,我们利用kill -9强行杀死子进程,但是上面依然显示等待成功,所以退出码并不能代表进程运行是否成功了,还要看退出信号

上面我们利用status来说明退出信息和退出码比较繁琐,我们一般不使用,经常使用下面这种

WIFEXITED(status):如果正常终止子进程返回的状态,则为真(查看进程是否正常退出),本质是查看信号是否正常

WEXITSTATUS(status):若WIFEXITED为真,则提取子进程的退出码(查看进程的退出码)(这时候的退出码就代表子进程正常执行执行完毕了)
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    pid_t id=fork();
    if(id==0)
    {
        int count=0;
        while(count<20)
        {
            printf("I am child,pid:%d,ppid:%d\n",getpid(),getppid());
            sleep(1);
            count++;
        }
        exit(10);
    }
    else
    {
        int status=0;
        //使用waitpid进行进程等待
        pid_t ret=waitpid(id,&status,0);
        //WIFEXITED(status):如果进程正常退出的,那么返回值就为真,异常退出返回值为假
        if(WIFEXITED(status))
        {
            printf("child exit success\n");
            //打印退出码
            printf("child exit code:%d\n",WEXITSTATUS(status));
        }
        //走到这代表子进程异常退出
        else
        {
        	//打印子进程退出时的退出信号
            printf("child exit signal:%d\n",status&0x7F);
        }
        printf("Father is running\n");
        sleep(5);
    }
    return 0;
}

image-20210928174458527

非阻塞等待:

父进程在等待子进程是不等待,还在做其他事情

int main()
{
    pid_t id=for();
	if(id==0)
    {
        int count=0;
        while(cout<10)
        {
            printf("I am child,pid:%d,ppid%d",getpid(),getppid());
            sleep(1);
            count++;
        }
        exit(1);
    }
    while(1)
    {
        int status=0;
        pid _t ret=waitpid(id,&status,WNOHANG);
        if(ret>0)
        {
            printf("watit success\n");
            printf("exit codfe:%d",WEXITSTATUS(staus));
            break;
        }
        else if(ret==0)
            //子进程没有退出,等待是成功的
            printf("father do   other thing ret:%d\n",ret);
        else
        {
            printf("waitpid 
                  error\n");
                   break;
        }
    }

    return 0;
}
WNOHANG:
ret=waitpid(id,&status,WNOHANG);
ret==0代表等待成功,但是子进程还在执行
ret==-1等待失败
ret>0等待成功,子进程还在运行
ret==0,当前子进程还没有退出

进程替换

用fork创建子进程后执行的是和父进程相同的程序(但是可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序,当进程调用一种exec函数时,该进程的用户空间的代码和数据就会被新程序替换,从新程序的启动例程开始执行,调用exec并不会创建心得进程,只是发生了进程替换,所以调用exec前后该进程的id并未发生变化

替换函数

#include<unistd.h>
execl("/usr/bin/ls","ls","-al",NULL);
execlp("ls","ls","-a","-l",NULL);
char*envp[]={"/usr/bin/ls",NULL};
execle("ls","ls","-a","-l",NULL,envp);
char*argv[]={"ls","-l","-a",NULL};
execv("/usr/bin/ls",argv);
execvp(“ls”,argv);

execve("/usr/bin/ls",argv,envp);

函数理解:

这些函数如果调用成功则加载新的程序从开始代码开始执行,不在返回

如果调用出错返回-1

所以exec函数只有出错时的返回值而没有成功是的返回值

命名规则:(exec+后缀)

l:代表列表,将所有选项列出来,需要自己写路径,

execl("usr/bin/ls","ls","-a","-l",NULL);

p:代表环境变量,也就是可以不用写环境变量

execlp("ls","ls","-a","-l",NULL);

e:envp表示自己配置环境变量,将环境变量保存到数组中

char*envp[]={"/usr/bin/ls",NULL};
execle("ls","ls","-a","-l",envp);

v:代表参数用数组

char*argv[]={"ls","ls","-a","-l",NULL};
execv("usr/bin/ls",argv);

vp:有环境变量了

char*argv[]={"ls","ls","-a","-l",NULL};
execvp(argv);

ve:带e,需要自己写全路径

execve("/bin/ls",argv,envp);

ps:通过替换函数我们可以将两个不同类型的程序相连接,exec函数可以连接,c++,python,java等语言所写的程序

简易shell的实现
本质是通过exec系列函数实现系统接口的调用

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
#define Len 1021
int main()
{
    char cmd[Len];
    while(1)
    {
        printf("sjw@iZ2zedu4njy79sqivntvprZ test_9_29_execl$");//伪装Linux输入命令时的开头
        fgets(cmd,Len,stdin);//将输入内容保存到cmd数组中
        cmd[strlen(cmd)-1]='\0';
        char *myargv[Len];
        pid_t id=fork();
                int i=1;
            myargv[0]=strtok(cmd," ");//以空格进行分割
            while(myargv[i]=strtok (NULL," "))
            {
                i++;
            }
        if(id==0)
        {
    		execvp(myargv[0],myarg);//调用execvp函数
            exit(1);
        }
        else
        {
        	//父进程wait,等待子进程退出
            int status=0;
            pid _t ret=waitpid(id,&status,0);
            id(WIFEXITED(status))
            {
                printf("exit code:%d\n",WEXITSTATUS(status));
            }
        }
        
    }
    return 0;
}
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-09-30 12:19:58  更:2021-09-30 12:20:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 16:27:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码