LinuxC编程——进程
目录
- 一、概念
- 1.1 程序
- 1.2 进程
- 二、特点⭐⭐⭐
- 三、进程段
- 四、进程分类
- 五、进程状态
- 六、进程状态转换图
- 七、函数接口
- 1. 创建子进程
- 2. 回收进程资源
- 3. 退出进程
- 4. 获取进程号
- 八、守护进程
一、概念
进程和程序是密不可分的两组概念,相对比,便于理解。
1.1 程序
简单来说,程序即编译好的可执行文件
展开来说:
- 编译好的可执行文件
- 存放在磁盘上的指令和数据的有序集合(文件)
- 静态的,没有执行的概念
1.2 进程
程序一次执行过程,是动态的,包括创建、调度、执行以及消亡,它是一个独立的可调度的任务
展开来说:
- 进程是程序的一次执行过程。
- 进程是动态的,包含创建、调度、执行、消亡。
- 进程是执行一个程序分配资源的总称。
- 独立的可调度的任务
二、特点⭐⭐⭐
- 系统会为每一个进程分配0-4g的虚拟空间,其中0-3g(用户空间)是每个进程所独有的,3g-4g(内核空间)是所有进程共有的。
- CPU调度进程时会给进程分配时间片(几毫秒 ~ 十几毫秒),当时间片用完后,cpu再进行其他进程的调度,实现进程的轮转,从而实现多任务的操作。
三、进程段
Linux中的进程包含三个段:
- “数据段”存放的是全局变量、常数以及动态数据分配的数据空间(如malloc函数取得的空间)等。
- “正文段”存放的是程序中的代码
- “堆栈段”存放的是函数的返回地址、函数的参数以及程序中的局部变量
四、进程分类
- 交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。该类进程经常与用户进行交互,需要等待用户的输入,当接收到用户的输入后,该类进程会立刻响应,典型的交互式进程有:shell命令进程、文本编辑器等
- 批处理进程:该类进程不属于某个终端,它被提交到一个队列中以便顺序执行。
- 守护进程:该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才结束。
五、进程状态
- 运行态(TASK_RUNNING)R:指正在被CPU运行或者就绪的状态。这样的进程被成为runnning进程。
- 睡眠态(等待态):
- 可中断睡眠态(TASK_INTERRUPTIBLE)S:处于等待状态中的进程,一旦被该进程等待的资源被释放,那么该进程就会进入运行状态。
- 不可中断睡眠态(TASK_UNINTERRUPTIBLE)D:该状态的进程只能用wake_up()函数唤醒。
- 暂停态(TASK_STOPPED)T:当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态。可向其发送SIGCONT信号让进程转换到可运行状态。
- 死亡态X:进程结束
- 僵尸态(TASK_ZOMBIE)Z:当进程已经终止运行,但还占用系统资源,要避免僵尸态的产生(例如:在父子进程中,子进程比父进程先结束,而父进程没有对子进程及时回收,释放子进程占用的资源,此时子进程将变成一个僵尸进程)。
六、进程状态转换图
进程创建后,进程进入就绪态,当CPU调度到此进程时进入运行态,当时间片用完时,此进程会进入就绪态,如果此进程正在执行一些IO操作(阻塞操作)会进入阻塞态,完成IO操作(阻塞结束)后又可进入就绪态,等待CPU的调度,当进程运行结束即进入结束态。
七、函数接口
1. 创建子进程
pid_t fork(void);
-
功能:创建子进程
-
参数:无
-
返回值
- 成功:在父进程中:返回子进程的进程号 >0;在子进程中:返回值为0
- 失败:-1并设置errno
-
特点⭐⭐⭐
- 子进程几乎拷贝了父进程的全部内容,包括:代码、数据、系统数据段中的pc值、栈中的数据、父进程中打开的文件等;但它们的PID、PPID是不同的。
- 父子进程有独立的地址空间,互不影响;当在相应的进程中改变全局变量、静态变量,都互不影响。
- 若父进程先结束,子进程成为孤儿进程,被init进程收养,子进程变成后台进程。
- 若子进程先结束,父进程如果没有及时回收,子进程变成僵尸进程(要避免僵尸进程产生)
-
拓展:
- fork之前的代码被复制,但是不会重新执行一遍;fork之后的代码被复制,并且再被执行一遍。
- fork之后两个进程相互独立,子进程拷贝了父进程的所有代码,但内存空间独立
- fork之前打开文件,fork之后拿到的是同一个文件描述符,操作的是同一个文件指针。(而不同的进程打开相同的文件,操作的是不同的文件指针)
- fork函数创建父子进程后各自执行顺序不一定(vfork:先执行完子进程,再执行父进程)
-
例子:
#include <stdio.h>
#include <unistd.h>
// 若父进程先结束,子进程将变成孤儿进程 且被init进程收养,变成孤儿进程
// 若子进程先结束,父进程如果没与及时回收,子进程将变成僵尸进程
int main(int argc, char const *argv[])
{int a =0;pid_t pid = fork();if(pid<0){perror("fork err");return -1;}else if(pid == 0){ // 运行子进程printf("child a:%d\n",a);while(1);}else{ // 运行父进程a=3;printf("parent a:%d\n",a);//while(1);}return 0;
}
2. 回收进程资源
- wait:pid_t wait(int *status);
- 功能:回收子进程资源(阻塞)
- 参数
- status:子进程退出状态,不接受子进程状态设为NULL
- 返回值
- 成功:回收的子进程的进程号
- 失败:-1
- waitpid:pid_t waitpid(pid_t pid, int *status, int options);
- 功能:回收子进程资源
- 参数:
- pid:
- status:子进程退出状态
- options:
- 0:阻塞
- WNOHANG:非阻塞
- pid:
- 返回值
- 正常:结束的子进程的进程号
- 当使用选项WNOHANG且没有子进程结束时:0
- 出错:-1
- 例
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{int a = 0;pid_t pid = fork();if(pid<0){perror("fork err");return -1;}else if(pid == 0){printf("in child a=%d\n",a);// while(1);sleep(1);}else{a = 3;// wait(NULL); //阻塞回收所有子进程// waitpid(-1,NULL,0); //阻塞回收所有子进程waitpid(pid,NULL,0); // 阻塞回收pid进程// waitpid(pid,NULL,WNOHANG); //不阻塞回收进程号为pid的进程printf("in parent a = %d\n",a);// while(1);wait(NULL);printf("parent end...\n");}return 0;
}
3. 退出进程
- exit :void exit(int status);
- 功能:结束进程,刷新缓存
- 参数:退出的状态
- _exit:void _exit(int status);
- 功能:结束进程,不刷新缓存
- 参数:status是一个整型的参数,可以利用这个参数传递进程结束时的状态。
通常0表示正常结束;其他的数值表示出现了错误,进程非正常结束
注:exit与return的区别⭐⭐
- exit:函数,不管在子函数还是主函数,都可以结束进程(进程的退出)
- return:关键字,当子函数中有return时返回到函数调用位置,并不结束进程(函数的退出)
例子:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>void fun(int a, int b)
{printf("a+b=%d",a+b);// exit(0); // 结束进程,刷新缓存_exit(0); // 结束进程,不刷新缓存return;
}int main(int argc, char const *argv[])
{pid_t pid = fork();if(pid<0){perror("fork err");return -1;}else if(pid == 0){printf("in child\n");fun(3,4);sleep(1);}else{printf("in parent\n");// while(1);wait(NULL);printf("parent end...\n");}return 0;
}
运行结果:
exit:会刷新缓存
_exit:不会刷新缓存
4. 获取进程号
- getpid:pid_t getpid(void);
- 功能:获取当前进程的进程号
- getppid:pid_t getppid(void);
- 功能:获取当前进程的父进程号
例:
- 功能:获取当前进程的父进程号
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{pid_t pid = fork();if(pid<0){perror("fork err");return -1;}else if(pid == 0){printf("childpid:%d ppid:%d pid:%d\n",getpid(),getppid(),pid);exit(0);}else{printf("parentpid:%d ppid:%d pid:%d\n",getpid(),getppid(),pid);wait(NULL);}return 0;
}
运行:
练习:通过父子进程完成对文件的拷贝(cp),父进程从文件开始到文件的一半开始拷贝,子进程从文件的一半到文件末尾。要求:文件IO cp src dest
- 方法一
这里先执行子进程来进行源文件后半部分的复制,然后执行父进程来进行源文件前半部分的复制
方法二:
这里先执行父进程来进行源文件前半部分的复制,后执行子进程来进行源文件后半部分的复制
//通过父子进程完成对文件的拷贝(cp),父进程从文件开始到文件
//的一半开始拷贝,子进程从文件的一半到文件末尾。要求:文件IO cp src dest
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{if (argc != 3){printf("Please input:%s <srcfile> <destfile>\n", argv[0]);return -1;}int src = open(argv[1], O_RDONLY);if (src < 0){perror("srcfile open err");return -1;}int dest = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);if (dest < 0){perror("destfile open err");return -1;}off_t half = lseek(src, 0, SEEK_END) / 2; // 文件长度的一半int flag = 0; // 控制下面父子进程执行顺序,让父进程先执行ssize_t s;char buf[32] = {0};pid_t pid = fork();if (pid < 0){perror("fork err");return -1;}else if (pid == 0) // 子进程 复制源文件后半部分{while (flag == 1) // 父进程已复制完毕,子进程可以执行{lseek(src, half, SEEK_SET);lseek(dest, half, SEEK_SET);while ((s = read(src, buf, 32)) != 0)write(dest, buf, s);}}else // 父进程 复制源文件前半部分(先执行){lseek(src, 0, SEEK_SET);lseek(dest, 0, SEEK_SET);int n = half;if (n < 32){read(src, buf, n);write(dest, buf, n);flag = 1;wait(NULL);}else{while ((s = read(src, buf, 32)) != 0){write(dest, buf, s);n = n - s;if (n < 32){read(src, buf, n);write(dest, buf, n);flag = 1;wait(NULL);}}}}close(src);close(dest);return 0;
}
//通过父子进程完成对文件的拷贝(cp),父进程从文件开始到文件
//的一半开始拷贝,子进程从文件的一半到文件末尾。要求:文件IO cp src dest
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{if (argc != 3){printf("Please input:%s <srcfile> <destfile>\n", argv[0]);return -1;}int src = open(argv[1], O_RDONLY);if (src < 0){perror("srcfile open err");return -1;}int dest = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);if (dest < 0){perror("destfile open err");return -1;}off_t half = lseek(src, 0, SEEK_END) / 2; // 文件长度的一半int flag = 0; // 控制下面父子进程执行顺序,让父进程先执行ssize_t s;char buf[32] = {0};pid_t pid = fork();if (pid < 0){perror("fork err");return -1;}else if (pid == 0) // 子进程 复制源文件后半部分{while (flag == 1) // 父进程已复制完毕,子进程可以执行{lseek(src, half, SEEK_SET);lseek(dest, half, SEEK_SET);while ((s = read(src, buf, 32)) != 0)write(dest, buf, s);}}else // 父进程 复制源文件前半部分(先执行){lseek(src, 0, SEEK_SET);lseek(dest, 0, SEEK_SET);int n = half;if (n < 32){read(src, buf, n);write(dest, buf, n);flag = 1;wait(NULL);}else{while ((s = read(src, buf, 32)) != 0){write(dest, buf, s);n = n - s;if (n < 32){read(src, buf, n);write(dest, buf, n);flag = 1;wait(NULL);}}}}close(src);close(dest);return 0;
}
八、守护进程
- 特点
守护进程是后台进程;生命周期比较长,从系统启动时开启,系统关闭时结束;它是脱离控制终端且周期执行的进程。 - 实现步骤
- 创建子进程,父进程退出
让子进程成为孤儿进程,成为后台进程。fork - 在子进程中创建新会话
让子进程成为会话组组长,为了让子进程脱离控制终端。setsid() - 改变运行路径为根目录。chdir
原因:提高权限,让运行路径不能被删除或卸载。chdir - 重设权限掩码。umask
目的:增大进程创建文件的权限,提高灵活性。umask - 关闭不必要的文件描述符
目的:关闭不必要的文件描述符。close
- 创建子进程,父进程退出
- 练习:创建一个守护进程,循环间隔1s向文件中写入一串字符“hello”
/*创建守护进程实现往日志文件中循环写入hello,间隔时间为1秒*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>int main(int argc, char const *argv[])
{//1.创建父子进程pid_t pid = fork();if(pid < 0){perror("fork err");return -1;}else if(pid == 0) //子进程进入{setsid(); //2.在子进程中创建新会话,让子进程成为会话组组长chdir("/"); //3.改变运行目录为根目录umask(0); //4.重设文件掩码close(0);//5.关闭文件描述符->0 1 2close(1);int fd = open("/tmp/c.log",O_WRONLY|O_CREAT|O_APPEND,0666);if(fd < 0){perror("open err");return -1;}}else{exit(0); //1.父进程退出}return 0;
}
相关文章:

LinuxC编程——进程
目录 一、概念1.1 程序1.2 进程 二、特点⭐⭐⭐三、进程段四、进程分类五、进程状态六、进程状态转换图七、函数接口1. 创建子进程2. 回收进程资源3. 退出进程4. 获取进程号 八、守护进程 一、概念 进程和程序是密不可分的两组概念,相对比,便于理解。 1.…...

深入理解设计模式-结构型之适配器
适配器模式(Adapter Pattern): 适配器模式用于将一个类的接口转换成另一个类的接口,以使两者能够一起工作。适配器模式通常用于处理已经存在的类,让它们能够与其他类协同工作,而不需要修改原始类的代码。&…...

桥梁结构健康监测系统,智能预警降低桥梁安全隐患
桥梁通常位于现代综合交通网络中的咽喉部位,对区域经济发展起着重要的推进作用,然而在为社会经济发展做出巨大贡献的同时,它们不可避免地会在荷载作用、环境侵蚀和自然灾害等影响下出现材料腐蚀劣化、结构损伤开裂、性能退化和功能失效等损伤…...

夏威夷等全球多地深陷「末日狂烧」,关键时刻 AI 监测能否跑赢野火?
内容一览:当地时间 8 月 8 日,美国夏威夷州突发野火,当地居民和游客不得不跳入太平洋中躲避火势。截至 8 月 17 日,这场野火已经造成110 人死亡,超过 1000人失踪。与此同时,美国、加拿大、法国等地也正遭遇…...

解决多模块内核心模块有接口打包成jar后被依赖并调用遇到的问题(springcloud集成ruoyi.quartz)
项目准备开发个新功能,刚好很喜欢ruoyi写的任务调度,因此想到了集成ruoyi.quartz模块 ,遇到了很多问题: 首先因为ruoyi.quartz模块依赖了ruoyi.common模块,因此第一步我需要把common模块一部分依赖项复制到了quartz模块内…...

【kubernetes系列】Kubernetes之Kubelet运行机制和状态更新机制
Kubelet运行机制 Kubelet是Kubernetes中的一个重要组件,在每个 Node 节点上都会启动 kubelet 服务。 该服务主要用于处理 Master 节点下发到本节点的任务,管理 Pod及Pod 中的容器。每个kubelet 进程会在 API Server 上注册节点自身信息,定期…...

(学习笔记-进程管理)怎么避免死锁?
死锁的概念 在多线程编程中,我们为了防止多线程竞争共享资源而导致数据错乱,都会在操作共享资源之前加上互斥锁,只有成功获得到锁的线程,才能操作共享资源,获取不到锁的线程就只能等待,直到锁被释放。 那…...

【golang】链表(List)
List实现了一个双向链表,而Element则代表了链表中元素的结构。 可以把自己生成的Element类型值传给链表吗? 首先来看List的四种方法。 MoveBefore方法和MoveAfter方法,它们分别用于把给定的元素移动到另一个元素的前面和后面。 MoveToFro…...

android平台的语音聊天助手源码
目录 1 android平台的语音聊天助手源码 1.1 //js处理工具类 1.1.1 openImage 1.2 LoadWebDetails android平台的语音聊天助手源码package com.shrimp.xiaoweirobot.net; import java.util.ArrayList;...

Python读取Word统计词频输出到Excel
1.安装依赖的包 "# 读取docx\n", "!pip install python-docx\n", "!pip install -i https://pypi.tuna.tsinghua.edu.cn/simple python-docx\n", "# 中英文分词\n", "!pip install jieba\n", "!pi…...

windows docker mysql8.0 挂载配置文件不生效的问题
原因 mysql 8.0 遇到sql_modeonly_full_group_by的问题,于是就自定义my.cnf 去掉only_full_group_by,修改my.cnf 文件后,进行映射启动 docker run 命令 docker run -p 3306:3306 --privilegedtrue --restartalways -d --name axsc-mysql -…...

openGauss学习笔记-42 openGauss 高级数据管理-触发器
文章目录 openGauss学习笔记-42 openGauss 高级数据管理-触发器42.1 语法格式42.2 参数说明42.3 示例 openGauss学习笔记-42 openGauss 高级数据管理-触发器 触发器会在指定的数据库事件发生时自动执行函数。 42.1 语法格式 创建触发器 CREATE TRIGGER trigger_name { BEFORE…...

Leetcode33 搜索旋转排序数组
题解: /*** 旋转排序数组可分为N1 N2两个部分,如:[4,5,6,7,1,2,3],N1为[4,5,6,7],N2为[1,2,3]** 必然满足以下两个条件:* 1. N1和N2都是分别递增的;* 2. N1中的所有元素大于N2中的所有元素;** …...

docker 第一章
目录 1.安装 docker 2.镜像、容器 3.总结 1.安装 docker 2.镜像、容器 3.总结 容器在 linux 上的本机运行,与其他容器共享主机的内核。它运行的是一个独立的进程,不占用其他任何可执行文件的内存,非常轻量级。...

注册中心 —— SpringCloud Netflix Eureka
Eureka 简介 Eureka 是一个基于 REST 的服务发现组件,SpringCloud 将它集成在其子项目 spring-cloud-netflix 中,以实现 SpringCloud 的服务注册与发现,同时提供了负载均衡、故障转移等能力,目前 Eureka2.0 已经不再维护…...

2023年国赛数学建模思路 - 案例:异常检测
文章目录 赛题思路一、简介 -- 关于异常检测异常检测监督学习 二、异常检测算法2. 箱线图分析3. 基于距离/密度4. 基于划分思想 建模资料 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 一、简介 – 关于异常…...

⛳ Java 反射
目录 ⛳ Java 反射🎨 一、反射概述**🎃 使用反射的前提条件: **🎲 类正常加载过程如下图:反射优缺点:🧸 Java反射机制提供的功能: **🥅 反射主要API** 🏭 二、反射的使用Ἲ…...

Android 13 像Settings一样开启关闭深色模式
一.背景 由于客户定制的Settings需要开启关闭深色模式,所以需要自己调用开启关闭深色模式 二.前提条件 首先应用肯定要是系统应用,并且导入framework.jar包,具体可以参考: Android 应用自动开启辅助(无障碍)功能并使用辅助(无障碍)功能_android 自动开启无障碍服务_龚礼鹏…...

微服务实战项目-学成在线-项目优化(redis缓存优化)
微服务实战项目-学成在线-项目优化(redis缓存优化) 1 优化需求 视频播放页面用户未登录也可以访问,当用户观看试学课程时需要请求服务端查询数据,接口如下: 1、根据课程id查询课程信息。 2、根据文件id查询视频信息。 这些接口在用户未认…...

IDEA 找不到项目 ‘org.springframework.boot:spring-boot-starter-parent:3.1.2‘
找不到项目 ‘org.springframework.boot:spring-boot-starter-parent:2.6.7’ 这个问题主要是因为ide的缓存导致的,我们直接清理缓存并重启ide 重启之后ide会对pom文件进行编排索引完成之后问题就没有了...

thinkphp开发的在线学习培训考试模拟考试做题练习系统带商城功能证书管理课程系统
thinkphp开发的在线学习培训考试模拟考试做题练习系统带商城功能证书管理课程系统 1、做题界面 2、前端UI的展示 3、带商城购物功能...

Android 应用冷启动优化
冷启动相关概念 应用启动概念 冷启动:首次打开app或者app彻底销毁后再次打开app(开关机后),这也是我们进行启动速度优化的主要方向。热启动:应用运行中按home键再打开应用。温启动:介于两者之间ÿ…...

538页21万字数字政府智慧政务大数据云平台项目建设方案WORD
导读:原文《538页21万字数字政府智慧政务大数据云平台项目建设方案WORD》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。 根据业务的不同属性,…...

进程间通信——信号
信号的概念 信号是 Linux进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异…...

PAT 1039 Course List for Student
个人学习记录,代码难免不尽人意。 Zhejiang University has 40000 students and provides 2500 courses. Now given the student name lists of all the courses, you are supposed to output the registered course list for each student who comes for a query. …...

【Sklearn】基于决策树算法的数据分类预测(Excel可直接替换数据)
【Sklearn】基于决策树算法的数据分类预测(Excel可直接替换数据) 1.模型原理1.1 模型原理1.2 数学模型2.模型参数3.文件结构4.Excel数据5.下载地址6.完整代码7.运行结果1.模型原理 决策树是一种基于树状结构的分类和回归模型,它通过一系列的决策规则来将数据划分为不同的类…...

并发编程4:Java 中的并发基础构建模块
目录 1、同步容器类 1.1 - 同步容器类的问题 1.2 - 迭代和容器加锁 2、并发容器类 2.1 - ConcurrentHashMap 类 2.2 - CopyOnWriteArrayList 类 3、阻塞队列和生产者-消费者模式 3.1 - 串行线程封闭 4、阻塞方法与中断方法 5、同步工具类 5.1 - 闭锁 -> CountDow…...

Vue-10.集成(.editorconfig、.eslintrc.js、.prettierrc)
介绍 同时使用 .editorconfig、.prettierrc 和 .eslintrc.js 是很常见的做法,因为它们可以在不同层面上帮助确保代码的格式一致性和质量。这种组合可以在开发过程中提供全面的代码维护和质量保证。然而,这也可能增加一些复杂性,需要谨慎配置…...

PHP-FPM进程排查
1、查看php-fpm的进程个数 ps -ef |grep "php-fpm"|grep "pool"|wc -l2、查看每个php-fpm占用的内存大小 ps -ylC php-fpm --sort:rss3.查看PHP-FPM在你的机器上的平均内存占用 ps --no-headers -o "rss,cmd" -C php-fpm | awk { sum$1 } END…...

PHP-MD5注入
0x00 前言 有些零散的知识未曾关注过,偶然捡起反而更加欢喜。 0x01 md5 注入绕过 md5函数有两个参数,第一个参数是要进行md5的值,第二个值默认为false,如果为true则返回16位原始二进制格式的字符串。意思就是会将md5后的结果当…...