当前位置: 首页 > news >正文

Linux系统环境编程之进程1

1.引言

问1. 什么是程序,什么是进程,有什么区别?

程序是静态的概念,gcc xxx.c –o pro 磁盘中生成pro文件,叫做程序。

进程是程序的一次运行活动, 通俗点意思是程序跑起来了,系统中就多了一个进程。

问2. 如何查看系统中有哪些进程?

a.使用ps指令查看 实际工作中,配合grep来查找程序中是否存在某一个进程。ps -aux | grep

b.使用top指令查看,类似windows任务管理器

问3. 什么是进程标识符?

每个进程都有一个非负整数表示的唯一ID, 叫做pid,类似身份证。

Pid=0:  称为交换进程(swapper) 作用—进程调度

Pid=1:init进程 作用—系统初始化

编程调用getpid函数获取自身的进程标识符 getppid获取父进程的进程标识符。

问4. 什么叫父进程,什么叫子进程?

进程A创建了进程B 那么A叫做父进程,B叫做子进程,父子进程是相对的概念,理解为人类中的父子关系。

问5. C程序的存储空间是如何分配?

2.进程创建实战

使用fork函数创建一个进程 pid_t fork(void); 

fork函数调用成功,返回两次 返回值为0,代表当前进程是子进程 返回值非负数,代表当前进程为父进程 调用失败,返回-1

fork创建一个子进程的一般目的:

(1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的一父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork使子进程处理此请求。父进程则继续等待下一个服务请求到达。

(2)一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec(我们将在8.10节说明exec)。

fork编程实战:

一个现有进程可以调用fork函数创建-个新进程。

#include <unistd.h>

pid_ t fork (void) ;

返回值:子进程中返回0,父进程中返回子进程ID.出错返回-1

由fork创建的新进程被称为子进程(child process)。fork函 数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID。fork使 子进程得到返回值0的理由是:一 个进程只会有个父进程,所以子进程总是可以调用getppid以获得其父进程的进程ID (进程ID 0总是由内核交换进程使用,所以一个子进程的进程ID不可能为0)。

子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父、子进程并不共享这些存储空间部分。父、子进程共享正文段(见7.6节)。

由于在fork,之后经常跟险着exec,所以以在的很多实现并不执行一个父进程数据段、栈和堆的完全复制。作为替代,使用了写时复制(Copy On Write, COW)技术。这些区域由父、子进程共享,而且内核将它们的访问权限改变为只读的。如果父、子进程中的任一 个试图修改这些区域,则内核只为修改区域的那块内存制作一一个副本, 通常是虚拟存储器系统中的一页 。Bach[ 19861)的9.2节和McKusick等1996的5 6节和5.7节对这种特征做了更详细的说明。

进程
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){

       pid_t pid;
       pid_t pid1;

       pid = getpid();
       printf("before fork pid is : %d\n",pid);

       fork();

       pid1 = getpid();
       printf("after fork pid is : %d\n",pid1);

       return 0;
}
 

进一步创建进程获取fork的返回值
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){

       pid_t pid;
       pid_t pid1;

       pid = getpid();
       printf("before fork pid is : %d\n",pid);

       pid_t retpid = fork();

       pid1 = getpid();
       printf("after fork pid is : %d\n",pid1);

       if(retpid > 0){
                printf("this is father pid , father fork : %d\n",retpid);
       }
       else if(retpid == 0){
               printf("this is child pid, child fork : %d \n",retpid);
       }

       return 0;
}

vfork函数 也可以创建进程,与fork有什么区别?

关键区别一: vfork 直接使用父进程存储空间,不拷贝。

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

//进程,vfork与fork的区别
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){

        int cnt = 0;

       pid_t retpid = vfork();

       if(retpid > 0){
               while(1){
                        printf("this is father pid , father fork : %d\n",retpid);
                        sleep(1);
               }
       }
       else if(retpid == 0){
               while(1){
                         printf("this is child pid, child fork : %d \n",retpid);
                        sleep(1);
               }
       }

       return 0;
}

进程退出

正常退出

1.Main函数调用return

2.进程调用exit(),标准c库

3.进程调用_exit()或者_Exit(),属于系统调用

补充:

1.进程最后一个线程返回

2。最后一个线程调用pthread_exit

异常退出

1.调用abort

2.当进程收到某些信号时,如ctrl+C

3.最后一个线程对取消(cancellation)请求做出响应

不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。

对上述任意一种终止情形、我们都希望终u止进程能够通知其父进程它是如何终止的。对于三个终止函数(exit、_ exit和_ Exit),实现这一点的方法是,将其退出状态(exit status)作为参数传送给函数。在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的终止状态(termination status)。 在任心一种情况下,该终止进程的父进程都能用wait或waitpid函数(在下一节说明)取得其终止状态。

//退出子进程
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main(){

        int cnt = 0;

       pid_t retpid = fork();

       if(retpid > 0){
               while(1){
                       printf("cnt : %d",cnt);
                        printf("this is father pid , father fork : %d\n",retpid);
                        sleep(1);
               }
       }
       else if(retpid == 0){
               while(1){
                printf("this is child pid, child fork : %d \n",retpid);
                sleep(1);
                cnt++;
                if(cnt == 3){
                        exit(0);
                        }
               }
       }

       return 0;
}

//退出子进程,并获取子进程退出编号
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(){

        int cnt = 0;

        int status = 0;

       pid_t retpid = vfork();

       if(retpid > 0){
               wait(&status);
               while(1){
                       printf("father wait child end! status is : %d\n",WEXITSTATUS(status));
                       printf("cnt : %d\n",cnt);
                        printf("this is father pid , father fork : %d\n",retpid);
                        sleep(1);
               }
       }
       else if(retpid == 0){
               while(1){
                printf("this is child pid, child fork : %d \n",retpid);
                sleep(1);
                cnt++;
                if(cnt == 3){
                        exit(0);
                        }
               }
       }

       return 0;
}

为啥要等待子进程退出?

父进程等待子进程退出 并收集子进程的退出状态。

子进程退出状态不被收集,变成僵死进程(僵尸进程)

区别: waip使调用者阻塞,waitpid有一个选项,可以使调用者不阻塞

waitpid函数:

status参数: 是一个整型数指针 非空: 子进程退出状态放在它所指向的地址中。 空: 不关心退出状态。

//退出子进程,并获取子进程退出编号
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(){

        int cnt = 0;

        int status = 0;

       pid_t retpid = vfork();

       if(retpid > 0){
               wait(&status);
               while(1){
                       printf("father wait child end! status is : %d\n",WEXITSTATUS(status));
                       printf("cnt : %d\n",cnt);
                        printf("this is father pid , father fork : %d\n",retpid);
                        sleep(1);
               }
       }
       else if(retpid == 0){
               while(1){
                printf("this is child pid, child fork : %d \n",retpid);
                sleep(1);
                cnt++;
                if(cnt == 3){
                        exit(2);
                        }
               }
       }

       return 0;
}
 

孤儿进程

父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程 Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程。

//孤儿子进程
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(){

        int cnt = 0;

        int status = 0;

       pid_t retpid = fork();

       if(retpid > 0){
                printf("this is father pid , father fork : %d\n",retpid);
       }
       else if(retpid == 0){
               while(1){
                printf("this is child pid, child fork : %d \n",retpid);
                printf("this is a guer,father fork is : %d\n",getppid());
                sleep(1);
                cnt++;
                if(cnt == 3){
                        exit(2);
                }
         }
        }
       return 0;
}
 

http://www.lqws.cn/news/559045.html

相关文章:

  • Jina-Embeddings-V4:多模态向量模型的革命性突破与实战指南
  • 华为云Flexus+DeepSeek征文|基于Dify构建AI资讯语音播报工作流
  • 鸿蒙5:组件监听和部分状态管理V2
  • Conformal LEC:官方学习教程
  • 【软考高项论文】论信息系统项目的沟通管理
  • [Andrej Karpathy_2] vibe coding | 大型语言模型的1960年代 | 自主性滑块
  • 某省赛题-windows内存取证
  • 【Linux】mmap分析
  • Excel限制编辑:保护表格的实用功能
  • 嵌入式网络通信与物联网协议全解析:Wi-Fi、BLE、LoRa、ZigBee 实战指南
  • Linux环境安装Redis的多种方式分析
  • Flutter基础(Isolate)
  • cocos creator 3.8 - 精品源码 - 六边形消消乐(六边形叠叠乐、六边形堆叠战士)
  • docker解析
  • Netty 揭秘CompositeByteBuf:零拷贝优化核心技术
  • Flutter基础(路由页面跳转)
  • Neo4j无法建立到 localhost:7474 服务器的连接出现404错误
  • Nacos源码之服务拉取(RestTemplate)
  • 访问不了/druid/index.html (sql.html 或 login.html)
  • CPU内部总线方式对比
  • 领域驱动设计(DDD)【20】之值对象(Value Object):入门
  • Spring Cloud 微服务(负载均衡策略深度解析)
  • nt!IoSynchronousPageWrite函数分析之atapi!IdeReadWrite----非常重要
  • 23种设计模式——策略模式:像换口红一样切换你的算法
  • Learning to Prompt for Continual Learning
  • 数据结构与算法 --- 双向链表
  • 问卷标记语言(QML):简化调查问卷设计与部署的XML解决方案
  • 【YOLOv13保姆级教程#03】自建数据集训练与验证(Train Val)全流程 | 手把手教你构建数据集、标签格式转换与yaml配置
  • Go开发工程师-Golang基础知识篇
  • Vue工程化实现约定式路由自动注册