Linux浅谈进程2

此篇博客作为上一篇博客的补充
Linux浅谈进程1

进程的创建

进程创建的方式:

  1. fork()
  2. vfork()

关于fork()函数

头文件为#include<unistd.h>
pid_t fork(void)
返回值:
子进程返回0,父进程返回子进程id,出错返回-1

进程创建的一般过程

  1. 给新建的进程分配一个内部的标识符,在内核中分配PCB。
  2. 复制父进程的环境
  3. 为进程分配资源(代码,数据,堆栈)
  4. 父进程地址空间的内容也复制到新的进程空间中
  5. 将该进程放到就绪队列中。
  6. 向父进程返回子进程的进程号,对子进程返回0

当一个进程fork出一个子进程后,就有两个二进制代码相同的进程,并且运行到相同的地方,但每个进程都将开始执行自己的代码。

fork()调用失败的原因

  • 系统中进程太多了
  • 或实际用户的进程数超过了限制(每台电脑都有一个进程的最大数)
  • 系统不支持fork()函数,(如windows)

vfork函数
也是用来创建子进程(在fork函数没有出现写时拷贝时出现的,性能高,但是有Bug)

  1. 子进程一定先于父进程执行。
  2. 子进程调用exec或者exit之后父进程才能执行

vfork与fork区别

  1. fork:父子进程交替运行。vfork:子进程运行,父进程阻塞,直到子进程运行结束(创建出子进程,子进程运行完了才会运行父进程),程序最后用exit(0)进程退出
  2. fork实现了写时拷贝。vfork就算是写也不拷贝
  3. vfork必须使用exit或excl
  4. 就算是fork实现了写时拷贝,性能也没有vfork高
  5. vfork虽然性能高,但是每个系统上的vfork都有问题,不要使用

进程的终止(结束)

进程退出的场景:

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

进程常见退出方法
正常终止
正常退出返回码为0
1. 从main返回
2. 调用exit
3. _exit

异常退出:

  1. ctrl+C
  2. kill
  3. abort()

查看进程退出码
可以用echo$?查看进程的退出码

echo $?:查看上一个退出码 退出码返回0~255 只用了int的8个比特位,其余位有别的用途,比如,程序是否是正常退出,如果是异常退出,是什么原因导致异常退出

exit所做的工作

  1. 执行用户通过atext或on_exit定义的清理函数
  2. 关闭所有打开的流,所有的缓冲数据均被写入
  3. 调用_exit.

回调处理函数atexit(函数名):在程序结束前执行。有32个上限,运行顺序与定义顺序相反

_exit与exit区别

  • _exit()直接终止,清理缓冲区。
  • exit()直接退出程序
  • _exit是系统调用,exit最终也会调用_exit

进程的撤销(销毁)

  1. 关闭软中断
  2. 回收资源(如关闭文件)
  3. 将进程的状态置为僵尸态
  4. 写记账信息
  5. 转进程调度

进程的等待

进程等待是一个很重要的过程,如果子进程退出,父进程不回收资源,就会造成我们之前博客讲过的“僵尸进程”的问题,从而导致内存泄漏

进程等待就可以很好的解决这个问题
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

进程等待方法

  1. wait
  2. waitpid

wait

//#include<sys/types.h>
//#include<sys/wait.h>   //头文件

pid_t wait(int* status)
返回值:成功返回被等待进程pid,失败返回-1
参数:输出型参数,获取子进程的退出状态,不关心可以设置为NULL

wait:做的三件事

  1. 阻塞当前进程,直到子进程退出才返回
  2. 回收子进程残留资源
  3. 获取子进程退出信息(状态)

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):若WEXITSTATUS非零,提取子进程退出码(查看进程的退出码)
    options: 
        WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不等待,若正常结束,返回该子进程的ID。

用代码看一下

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

int main()
{
    pid_t pid;
    if((pid=fork())==-1)
    {
        perror("fork");
        exit(1);
    }
    if(pid==0)
    {
        int i=0;
        printf("this is maomaochong\n",getpid());
        sleep(1);

        exit(-1);
    }
    else
    {
        int status;
        pid_t w=wait(&status);
        if(w==-1)
            perror("wait");
        else
            printf("parent w=%d\n",w);
        if(WIFEXITED(status))
        {
            printf("exitcode=%d\n",WEXITSTATUS(status));

        }
        else if(WIFSIGNALED(status))
        {
            printf("signum=%d\n",WTERMSIG(status));
        }
    }
}

结果为:
发到

我们通过结果可以看出,进程正常退出。

进程程序替换

替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

替换函数:
有六种以exec开头的函数,都统称为exec函数

#include<unistd.h>

int execl(const char *path,const char *arg,...);
int execlp(const char *file,const char *arg,...);
int execle(const char *path,const char *arg,...,
    char *const envp[] );
int execv(const char *path,char *const argv[]);
int execvp(const char *file,char *const argv[]);
int execve(const char *path,char *const argv[],
    char *const envp[]);

函数解释:

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1
  • 所以exec函数只有出错的返回值而没有成功的返回值

命名解释

  • l(list):表示参数采用列表
  • v(vector):表示参数采用数组
  • p(path):带p的函数会自动搜索环境变量PATH
  • e(env):表示自己维护环境变量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值