Linux 进程控制

进程控制

fork()函数

#include<unistd.h>
pid_t fork(void);
返回值: 子进程返回0 父进程返回子进程的pid

fork之前父进程独立运行,fork之后,父子进程分别执行
——》 fork之后会有两个进程,进程具有独立性,所以父进程和子进程的代码和数据都是独立的。

(fork之后,父子进程谁先执行完全由调度器来决定)
进程调用fork,当控制转移到内核中的fork代码后,内核会:

  1. 分配新的内存块和内核数据结构给子进程
  2. 将父进程部分数据结构内容拷贝至子进程
  3. 添加子进程到系统进程列表中
  4. fork返回,开始调度器调度

写实拷贝
通常,父子代码共享,当不进行写入时,数据也是共享的,但是当任意一方试图写入数据,便以写实拷贝的方式各自一份副本。

在这里插入图片描述

fork调用失败的原因

  • 系统中有太多的进程
  • 实际用户的进程数超过了限制

进程终止

进程退出的几种情况

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止

c/c++中main函数最后一般都会有一个 return 0 ,那为什么是0 不能是别的值呢?而这个return 又是return给谁呢?

return是进程的退出码,表征进程的退出信息,是给父进程读取的,如果return 0表示代码运行完毕,并且结果正确,如果是非0说明结果不正确,一般而言,不同错误的退出码可以自定义,表征不同的错误信息,让父进程读取。 在bash中会保存最近一次的进程执行完毕的退出码,通过命令
echo $? 来查看

进程常见的退出方法:

  1. 从main返回
  2. 调用exit
  3. 调用_exit
    异常退出:
  • ctrl + c

_exit函数

#include<unistd.h>
void _exit(int status);
参数: status定义了进程的终止状态,父进程通过wait来获取该值。

  • 注意:虽然status是int,但是仅有低8位可以被父进程所用,所以当_exit(-1)时,在终端执行 echo $? 返回值是255

exit函数

#include<unistd.h>
void exit(int status);

exit和_exit很相似,但是exit还会多做一些工作:

  • 执行用户通过atexit 或 on_exit定义的清理函数
  • 关闭所有打开的流,刷新缓冲区
  • 调用_exit()

在这里插入图片描述


进程等待

进程等待的必要性:

  • 子进程退出,父进程如果不管不顾,不去回收子进程的资源,就会造成僵尸进程,从而导致内存泄漏。
  • 进程一旦进入僵尸状态,就无法被杀死,kill -9也无能为力
  • 父进程需要知道子进程的任务完成如何,运行是否完成,是否有异常,结果是否正确?
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程信息

进程等待的方法

wait方法

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status)
返回值:成功返回返回被等待进程pid,失败返回-1
参数:输出型参数,获取子进程退出状态,不关心则可以设置称为NULL。

waitpid()方法

pid_t waitpid(pid_t pid, int *status,int options);
返回值
当正常返回的时候waitpid返回收集到子进程的进程ID
如果设置了选项WHOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0
如果调用中出错,则返回-1,这是errno会被设置成相应的值以指示错误所在
参数:
pid:
pid = -1,等待任意一个子进程,与wait等效
pid>0 ,等待其进程ID与pid相等的子进程
status:
WIFEXITED(status):若正常终止子进程返回的状态,则为真。(查看进程是否正常退出)
WEXITSTATUS(status): 若WIFEXITED非0,则提取子进程的退出码。
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予等待。若正常结束,则返回该子进程的ID。

  • 如果子进程已经退出,则调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程的退出信息。
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  • 如果不存在该子进程,则立刻出错返回。

在这里插入图片描述

获取子进程status

  • wait和waitpid都有一个status参数,该参数作为输出型参数,由操作系统填充。
  • 如果传的值是NULL,表示不关心子进程的退出状态信息。
  • 否则操作系统会根据该参数将子进程的信息返回给父进程
  • status有点类似位图,具体细节见下图。

在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t id = fork();
    if (id == 0)
    {

        while (1)
        {
            printf("我是子进程, 我正在运行...Pid: %d\n", getpid());
            sleep(5);
            break;

        }
        exit(0);
    }
    else
    {
        int status = 0;
        printf("我是父进程:pid:%d, 我准备电脑等待子进程啦\n", getpid());
        pid_t ret = waitpid(id, &status, 0);
        if (ret > 0)
        {
            if (WIFEXITED(status))
            {
                printf("子进程是正常退出的,退出码: %d\n", WEXITSTATUS(status));
            }
            else
            {
                printf("子进程异常退出,%d", status & 0X7F);
            }

        }
        else
        {
            printf("出错");
        }


    }
    return 0;
}

在这里插入图片描述

上述我们用的都是进程阻塞等待,阻塞等待本质就是父进程由R状态进入S状态,从运行队列退出,进入等待队列,等待子进程的退出。

而在waitpid的参数option中我们可以设置为非阻塞等待,非阻塞等待就是进程会检查子进程是否运行结束,如果暂时没有运行结束,那么父进程不会等待子进程,不会把自己变为阻塞状态,而是继续去干自己的事情。我们可以多次调用非阻塞等待,进行轮询检测

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值