Linux----进程控制(下)

3)进程等待

进程等待的意义:

  1. 回收僵尸进程(解决内存泄漏)
  2. 需要获取子进程的运行结束状态(不是必要)
  3. 父进程晚于子进程退出,可以规范化进行资源回收(编码)

wait()

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);


返回:等待任意一个子进程,当子进程退出,wait就可以返回,成功返回被等待进程pid,失败返回-1
int*status:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL


例:

int main()
{
	pid_t id=fork();
	if(id<0){
	    perror("fork");
	    return 1;
	}
	else if(id==0){
	    int count=5;
	    while(count--){
	        printf("child running: %d,pid:%d,ppid:%d\n",count,getpid(),getppid());
	        sleep(1);
	    }
	    printf("child quit...\n");
	    exit(0);
	}
	else{
	    printf("father is waiting...\n");
	    sleep(10);//让子进程维持5s的z状态
	    pid_t ret=wait(NULL);
	    printf("father wait done,ret is: %d\n",ret);
	}
	return 0;
}

shell监测脚本:

while :; 
do 
echo "===================================";
ps ajx | grep test1 | grep -v grep;
echo "===================================";
sleep 1;
done

在这里插入图片描述

waitpid()

#include <sys/wait.h>
pid_ t waitpid(pid_t pid, int *status, int options);


返回:

  1. 当正常返回的时候waitpid返回收集到的子进程的进程ID
  2. 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0
  3. 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在

pid:

  1. pid=(pid_t)-1,等待任一个子进程,与wait()等效
  2. pid>0,等待其进程ID与pid相等的子进程(等待指定的子进程:fork返回给父进程)
  3. pid=0,则请求其进程 组 ID 等于调用进程的任何子进程的状态(略)
  4. pid<(pid_t)-1,则为进程 组 ID 等于 pid 绝对值的任何子进程请求状态(略)

status:

  1. WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  2. WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

options:

  1. WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID

例:

int main()
{
	pid_t id=fork();
	if(id<0){
	    perror("fork");
	    return 1;
	}
	else if(id==0){
	    int count=5;
	    while(count--){
	        printf("child running: %d,pid:%d,ppid:%d\n",count,getpid(),getppid());
	        sleep(1);
	    }
	    printf("child quit...\n");
	    exit(121);
	}
	else{
	    printf("father is waiting...\n");
	    sleep(10);//让子进程维持5s的z状态
	    int status=0;
	    pid_t ret=waitpid(-1,&status,0);
	    int code = (status>>8)& 0xFF;
	    printf("father wait done,ret is the pid of child process: %d\n",ret);
	    printf("The code is: %d\n",code);
	    if(code==0)
	    {
	        printf("success\n");
	    }
	    else
	        printf("wrong\n");
	}
	return 0;
}

在这里插入图片描述

①status

wait和waitpid都有一个status参数
int status共32位,下图是低16位在这里插入图片描述
当子进程正常终止时用(status>>8)& 0xFF可以得到退出状态(不会收到任何信号)
当子进程异常终止(收到OS发送的信号),同时带有一位的core dump标志位,用status & 0x7F可以获得


例如下面由于段错误子进程终止:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


两个宏函数WIFEXITED和WEXITSTATUS可以用来判断子进程退出状态(正常/异常终止)

if(WIFEXITED(status)){
	printf("quit normal\n");
	printf("quit code is: %d\n",WEXITSTATUS(status));
}
else
	printf("process quit error\n");

在这里插入图片描述


问1:为什么不设置一个全局变量code,而要返回退出码?
:写时拷贝,子进程不能更改code
问2:waitpid从哪里拿到的status值的?子进程已经退出了
:子进程处于Z(僵尸状态),有内存泄漏(还有数据),waitpid从task_struct中获得的
下面是linux-2.6.39的源码 (进程退出码最终会被写入到exit_code里)
在这里插入图片描述

②option(阻塞与非阻塞)

如果option选项传入的是WNOHANG就是非阻塞等待方式,如果option传的是0默认是阻塞


非阻塞:
在这里插入图片描述
这里一共有三种返回状态:

  1. 失败返回(下一次再检测)
  2. 失败返回(真正的失败)
  3. 成功返回

阻塞等待理解:
在这里插入图片描述
阻塞等待:父进程等待,子进程运行

  1. 这里的“等待”:指OS把父进程放入等待队列,并将进程状态设置为非R状态
  2. 子进程运行完了再唤醒父进程,将父进程从等待队列中取出,放入运行队列,同时将其设为R状态

4)进程程序替换

进程程序替换

  1. 通过exec系列函数,让特定进程去加载磁盘中的其他程序,已达到运行的目的,期间不创建新的进程

为什么要进程程序替换:

  1. 子进程执行父进程的一部分代码
  2. 子进程自身新的程序需求

替换函数

头文件:#include <unistd.h>
返回:

  1. 如果调用成功则加载新的程序从启动代码开始执行,不再返回
  2. 如果调用出错则返回-1

只有execve是真正的系统调用,其它五个函数最终都调用 execve,这些函数之间的关系如下图:
在这里插入图片描述

①execl系列(l:list)

int execl(const char *path, const char *arg, ...);


path:执行的程序(带路径)
arg...:给执行程序传递的参数(…是可变参数)必须以NULL结尾


int execlp(const char *file, const char *arg, ...);


file:执行的程序(不带路径,会自动到环境变量找)
arg...:给执行程序传递的参数(…是可变参数)必须以NULL结尾


int execle(const char *path, const char *arg, ...,char *const envp[]);


path:执行的程序(带路径)
arg...:给执行程序传递的参数(…是可变参数)必须以NULL结尾
envp:传入默认的或者自定义的环境变量给目标可执行程序

②execv系列(v:vector)

int execv(const char *path, char *const argv[]);


path:执行的程序(带路径)
arg[]:给执行程序传递的参数(与execl系列区别就是将参数放入了一个数组)必须以NULL结尾


int execvp(const char *file, char *const argv[]);


file:执行的程序(不带路径,会自动到环境变量找)
arg[]:给执行程序传递的参数(与execl系列区别就是将参数放入了一个数组)必须以NULL结尾


int execve(const char *path, char *const argv[], char *const envp[]);


path:执行的程序(带路径)
arg[]:给执行程序传递的参数(与execl系列区别就是将参数放入了一个数组)必须以NULL结尾
envp[]:传入默认的或者自定义的环境变量给目标可执行程序


int execvpe(const char *file, char *const argv[], char *const envp[]);


file:执行的程序(不带路径,会自动到环境变量找)
arg[]:给执行程序传递的参数(与execl系列区别就是将参数放入了一个数组)必须以NULL结尾
envp[]:传入默认的或者自定义的环境变量给目标可执行程序


场景

shell解释器fork()创建子进程执行命令
Linux----50行简易shell

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值