Linux---创建、等待、终止进程

进程创建
父进程通过 fork(), vfork() 函数创建子进程
fork()测试代码:

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

int main()
{
    printf("Before:pid is %d\n",getpid());

    pid_t pid = fork();//创建子进程
    if(pid < 0)
    {
        perror("fork");
    }
    printf("After:pid is %d,fork return %d\n",getpid(),pid);
    sleep(1);
    return 0;
}

测试结果:
这里写图片描述
这里输出了三行结果。进程13417先打印Before,然后再打印After,而进程13418只打印了一条After,没有打印Before,这是为什么?如下图所示:
这里写图片描述
fork()之前父进程独立运行,fork()之后,父子两个执行流分别执行。(即fork后,父子都从fork之后开始执行)需要注意的是,fork()之后,谁先执行完全取决于调度器。

由上面的示例可以看出,fork有两个返回值,成功时,子进程返回0.父进程返回子进程的pid;失败返回-1
fork后父子代码共享,数据写时拷贝(即父子共享一块代码,如果不再写入时,数据也是共享的。但当任意一方试图写入时,便以写实拷贝的方式各自保存一份副本)。

fork失败的原因1.内存不够。我们知道,创建进程需要给他分配相应的系统资源,而内存是有限的,如果内存不够就会导致不能给新建的进程分配资源,因而创建失败。2.进程太多,占用着资源而没有释放,也会导致创建失败。

vfork和fork的区别在于
1.vfork父子进程共用同一个地址空间,而fork子进程具有独立的地址空间
2.vfork保证子进程先运行,在它调用exit或_exit后,父进程才能被调度运行。
来看下面的例子:

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

int data = 10;
int main()
{
    pid_t pid = vfork();
    if(pid < 0)
    {
        perror("fork");
    }
    else if(pid == 0)//子进程
    {
        sleep(1);
        data = 20;
        printf("child data is %d\n",data);
        exit(0);
        //return 0;
    }
    else//父进程
    {
        printf("father data is %d\n",data);
    }
    return 0;
}

结果:
这里写图片描述
可以看出,子进程直接改变了父进程的变量值,因为子进程在父进程的地址空间运行。

上述代码如果将子进程的退出方式改为return 0,就会出现段错误,程序异常终止。
这里写图片描述
因为用return 0返回,main函数的栈帧已经释放,父进程从vfork之后开始运行,但栈帧已经销毁,就会出现访问出错。

进程等待

子进程退出时,父进程通过wait()和waitpid()函数来获取子进程退出信息(毕竟你派给子进程的任务,得知道他完成的咋样啊),如果父进程不回收,子进程就会变成僵尸状态,进而导致内存泄露(僵尸进程刀枪不入,即使是杀人不眨眼的kill -9也无能为力,因为谁也没有办法杀死一个已经死去的进程)。

pid_t  wait(int* status)
返回值:
       成功时返回被等待进程的pid,失败返回-1.
参数:
      输出型参数,获取子进程退出信息,不关心设置为NULL
pid_t waitpid(pid_t pid,int* status,int options)
返回值:
      正常,返回等待子进程的pid;
      设置了WNOHANG选项,调用中waitpid发现没有子进程要等,返回0;
      失败返回-1,error会被设置成相应值,以指出错误所在。
参数:
     pid:
          pid = -1,等待任意一个子进程
          pid > 0,等待其进程id与pid相等的子进程
     status:
         WIFEXITED(status):查看进程是否正退出
         WEXITSTATUS(status):产看进程退出码
     options:
         WNOHANG:设置了这个选项,非阻塞式的等待(若pid指定子   进程没有结束,函数返回0,不予以等待,正常结束,返回子进程pid)

测试代码:

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

int main()
{
    pid_t pid = fork();//创建子进程
    if(pid < 0)
    {
        perror("fork");
        exit(1);
    }
    if(pid == 0)//子进程
    {
        printf("pid: %d,ppid: %d\n",getpid(),getppid());
        sleep(10);
        exit(10);
    }
    else//父进程
    {
        int status;
        int ret = wait(&status);//父进程等子进程退出
        if(ret > 0)//异常退出
        {
            printf("sig code: %d\n",status & 0x7F);
        }
        if(ret > 0 && (status & 0x7F) == 0)//正常退出
        {
            printf("child exit code: %d\n",(status>>8) & 0xFF);
        }
    }
    return 0;
}

测试结果:
这里写图片描述
10秒后输出child退出信息
这里写图片描述
通过在另一个终端发送2号信号来终止这个进程,此时异常退出,输出退出码。

扫描二维码关注公众号,回复: 2522516 查看本文章

进程终止

Linux下,进程正常退出方式:
1.在main函数中使用了return返回(return返回后把控制权交给了调用函数)
2.调用exit()或_exit()函数(exit()之后将控制权交给系统)

异常退出:
1.进程收到操作系统发来的某个信号来终止程序。
2.调用abort函数

无论哪种方式退出,最后都会执行内核中的同一段代码,这段代码用来关闭进程已打开的文件描述符和所占用的系统资源。

exit函数和_exit函数:

void exit(int status);

exit最后也会调用_exit,但在调用之前,还做了其他工作:
这里写图片描述
由上图可以看出,_exit直接使进程停止运行,释放其所占有的系统资源,而exit在此基础上做了一些包装,终止前检查该进程打开过哪些文件,将文件缓冲区内容写回文件。

猜你喜欢

转载自blog.csdn.net/y6_xiamo/article/details/80150880