Linux中的进程2

进程之间的数据共享

使用fork函数之后,生成的父进程和子进程两个地址空间区完全相同,后续可以进行不同操作

各个进程的地址空间中的数据是完全独立的

对于同一个变量,我们是有一个物理地址存放的,在的时候数据是子进程和父进程共享的,在的时候分别在物理地址上拷贝一份变量进行单独读写

父子进程之间不能通过全局变量进行通信

通过代码验证,可知父子进程之间不能通过全局变量进行通信。

exec函数族

exec 命令用于替换当前 shell 进程的图像。也就是说,它会加载并运行指定的命令,而这个命令会完全替代当前的 shell 进程,而不是在当前 shell 中启动一个子进程。这意味着一旦 exec 命令后面的程序执行完毕,shell 就会退出。

函数的原型

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

/* path : 要执行程序的路径(最好是绝对路径)

变参arg : 要执行的程序需要的参数 第一位arg: 占位 后边的

arg: 命令的参数

参数写完之后:null 一般执行自己写的程序 */

归根结底只有三个参数:路径+占位符+指令参数+NULL(结束语)

下面代码通过excel函数使用ls -l指令并替换掉当前子进程的其他程序(父进程不变)

下面代码通过执行PATH环境变量能够搜索到的程序(系统自带),execlp函数实现ps指令

函数原型:

int execle(const char *path, const char *arg, ...

即:执行指令的名称+占位符+命令的参数+NULL

孤儿进程

一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进 程(进程号为1)所收养,并由init进程对它们完成状态收集工作

注:资源回收是父进程回收的

为了释放子进程的占用的系统资源:

1.进程结束之后,能够释放用户区空间

2.释放不了PCB,必须由父进程释放

下面展现一个孤儿进程,我们使用一个sleep指令(允许在脚本或命令行中暂停执行一段时间),使子进程一直运行,但是父进程早已结束,从而形成了孤儿进程。

僵尸进程

一个比较特殊的状态,当进程退出父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵尸进程。僵尸进程会在以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。(一般是父进程还在运行,子进程已经结束)

僵尸进程是一个已经死掉了的进程。

这里我们可以写一个僵尸进程,使父进程一直运行,子进程结束

我们通过ps指令找到这个进程,发现一进城出现  Z+  的状态(僵尸的缩写ZOMBIE),在这时我们通过kill指令是不能使该进程结束的,因为僵尸进程是一个已经死掉的进程,我们想要使这个进程停止,必须给a.out(进程所在的可执行程序)发送kill指令

成功停止

进程的回收

wait阻塞函数

阻塞:当一个函数在执行过程中,如果必须等待某个事件的发生或条件的满足才能继续执行,那么在这个等待期间,函数所在的线程或进程会被挂起(即停止执行),直到所等待的事件发生或条件满足后,函数才会继续执行并返回结果。这种现象就被称为函数的阻塞作用。

函数作用:

1.阻塞并等待子进程退出

2.回收子进程残留资源

3.获取子进程结束状态(退出原因)

函数原型

pid_t wait(int *wstatus);   

返回值:        

                  ‐1 : 回收失败,已经没有子进程了          

                  >0 : 回收子进程对应的pid 

参数  :           

 status判断子进程如何退出状态           

1.WIFEXITED(status):为非0 ,进程正常结束                

   WEXITSTATUS(status)                

   如上宏为真,使用此宏,获取进程退出状态的参数 

         

2.WIFSIGNALED(status):为非0,进程异常退出                

   WTERMSIG(status):                

   如上宏为真,使用此宏,取得使进程终止的那个信号的编号    

调用一次只能回收一个子进程 

     

 

再来一个例子

#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) {  
        // 如果fork失败,则打印错误信息并退出  
        perror("fork failed");  
        exit(EXIT_FAILURE);  
    } else if (pid == 0) {  
        // 子进程执行的代码  
        printf("This is the child process with PID %d\n", getpid());  
        sleep(5); // 让子进程睡眠5秒以模拟工作  
        exit(0); // 子进程正常退出  
    } else {  
        // 父进程执行的代码  
        int status;  
        printf("This is the parent process waiting for the child process\n");  
        wait(&status); // 父进程等待子进程结束  
  
        if (WIFEXITED(status)) {  
            // 检查子进程是否正常退出  
            printf("Child process exited normally with status %d\n", WEXITSTATUS(status));  
        } else if (WIFSIGNALED(status)) {  
            // 检查子进程是否被信号杀死  
            printf("Child process killed by signal %d\n", WTERMSIG(status));  
        }  
    }  
  
    return 0;  
}

waitpid 函数

waitpid函数 函数作用:同wait函数

pid_t waitpid(pid_t pid, int *status, int options); 

参数      

1.pid: 指定回收某个子进程                   

pid == ‐1 回收所有子进程                   while(  (wpid=waitpid(‐1,status,0)) != ‐1)            

 pid > 0 回收某个pid相等的子进程            

 pid == 0 回收当前进程组的任一子进程             

pid < 0 子进程的PID取反(加减号)    

2.status: 子进程的退出状态,用法同wait函数      

3.options:设置为WNOHANG,函数非阻塞,设置为0,函数阻塞 

返回值:       

 >0 :返回清理掉的子进程ID        

‐1 :回收失败,无子进程        

如果为非阻塞              

 =0 :参数3为WNOHANG,且子进程正在运行 

在这个示例中,我们将使用waitpid函数来演示非阻塞等待和等待特定子进程的功能。

#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 failed");  
        exit(EXIT_FAILURE);  
    } else if (pid == 0) {  
        // 子进程执行的代码  
        printf("This is the child process with PID %d\n", getpid());  
        sleep(10); // 让子进程睡眠10秒以模拟长时间工作  
        exit(0); // 子进程正常退出  
    } else {  
        // 父进程执行的代码  
        int status;  
        pid_t ret;  
  
        // 非阻塞等待子进程结束  
        ret = waitpid(pid, &status, WNOHANG);  
        if (ret == 0) {  
            printf("Child process is still running\n");  
        } else if (ret == pid) {  
            // 如果ret等于pid,说明子进程已经结束  
            printf("Child process ended\n");  
            if (WIFEXITED(status)) {  
                printf("Child process exited normally with status %d\n", WEXITSTATUS(status));  
            }  
        } else {  
            // 如果ret不是pid,说明发生了错误  
            perror("waitpid failed");  
        }  
  
        // 如果需要,可以在这里执行其他操作  
        // ...  
  
        // 阻塞等待子进程结束(如果需要的话)  
        // ret = waitpid(pid, &status, 0);  
        // if (ret == pid) {  
        //     printf("Child process ended (blocking wait)\n");  
        //     if (WIFEXITED(status)) {  
        //         printf("Child process exited normally with status %d\n", WEXITSTATUS(status));  
        //     }  
        // } else {  
        //     perror("waitpid failed (blocking)");  
        // }  
    }  
  
    return 0;  
}

非阻塞运行结果

阻塞运行结果

vfork创建进程

区别一:vfork可以直接使用父进程存储空间(共享物理地址),不拷贝

区别二:vfork可以保证子进程先运行,当子进程调用exit退出后,父进程才执行

 

 

我们可以写一个程序去验证

可以得出使用vfork函数可以让子进程先运行,通过exit可以退出子进程(不能使用break,因为使用break可以理解为把子进程完全关闭了,里面i的值也被清空了,在父进程调用的时候i的值已经被清空了会被系统随机赋予一个随机的值),再进行父进程。当 exit(0) 被调用时,程序会执行一系列清理操作,比如刷新输出缓冲区、关闭打开的文件描述符等在 exit_exit 后面加上 (0) 表示将进程的退出状态码设置为 0,这通常表示程序成功执行完毕。

进程的退出

1. 正常退出
  • 从main函数返回:当程序的main函数执行完毕并返回一个值时,进程会正常退出。这个返回值通常用于表示程序的执行状态,0表示成功,非0值表示出现了某种错误或异常情况。
  • 调用exit函数exit函数是C标准库提供的一个用于正常终止进程的函数。它会执行一些清理工作(如刷新输出缓冲区、关闭文件描述符等),然后向操作系统发送退出信号,并传递退出状态码。
  • 调用_exit或_Exit函数:这两个函数是系统调用,用于立即终止进程,而不执行exit函数中的清理工作。它们通常用于在子进程中快速退出,以避免不必要的资源消耗。
  • 进程最后一个线程返回或调用pthread_exit:在多线程程序中,当最后一个线程返回或调用pthread_exit时,整个进程会退出。
2. 异常退出
  • 调用abort函数abort函数用于异常终止进程。它会生成一个SIGABRT信号,该信号的默认行为是终止进程并产生核心转储(如果启用了该选项)。虽然abort会调用exit,但通常不会到达exit的调用点,因为SIGABRT信号会先被处理。
  • 进程收到某些信号:当进程收到某些无法忽略或处理的信号时(如SIGKILLSIGTERM等),它会异常终止。这些信号通常由操作系统或其他进程发送,用于强制终止进程。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值