> Linux 进程是正在执行中的程序
[TOC]
## 1 进程简介
启动进程有两种方式:
- 手工启动
- 前台启动:执行一条命令
- 后台启动:在命令后输入一个`&`符号
- 调度启动
- 例如定时执行,输入 `at 11:27 12/25/2020`,输入命令`ls -l`,再输入`Ctrl + D`退出;或者使用相对时间,输入`at now +5 min`;结果不会在终端回显,可以重定向到文件中查看
> Linux 常见进程操作命令
命令 | 作用
---- | ----
ps | 查看系统中的进程
top | 动态显示系统中的进程
nice | 按用户执行的优先级运行
renice | 改变正在运行的进程的优先级
kill | 终止进程(包括后台进程)
crontab | 用于安装、删除或者列出用于驱动 cron 后台进程的任务
bg | 将挂起的进程放到后台执行
fg | 把后台进程转到前台执行
## 2 进程控制
系统会为每个新创建的进程分配一个唯一的正整数,称为进程标识符(PID),父进程的为(PPID),可以通过 `getpid` 和 `getppid` 获取
> Linux C 与进程相关的主要函数
函数名称 | 功能
---- | ----
getpid | 获取当前进程的进程号
getppid | 获取当前进程的父进程号
exec 函数族 | 在进程中启动另一个进程执行
system | 在进程中开始另一个进程
fork | 从已存在的进程中复制一个新进程
sleep | 令当前进程进入睡眠状态,直到达到指定时间,或者被信号中断
exit | 正常终止进程,并把参数 status 返回给父进程,进程中的所有缓冲区数据会自动写回并关闭未关闭的文件
_exit | 立即终止进程,并把参数 status 返回给父进程 ,并关闭未关闭的文件,不会处理标准I/O缓冲区
wait | 暂停父进程,等待子进程运行完成
waitpid | 暂停父进程,等待子进程运行完成
### 2.1 进程创建
**1. fork函数**
> fork 函数示例(备注:vfork 函数可以保证子进程一定优先于父进程开始执行)
```c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
pid_t pid;
char *message;
int n;
pid = fork();
if (pid < 0) { // 进程创建失败
perror("fork failed");
exit(1);
} else if (pid == 0) { // 子进程执行
message = "子进程执行";
n = 3;
} else { // 父进程执行
message = "父进程执行";
n = 6;
}
for (; n > 0; n--) {
printf(message); // 父进程与子进程交替打印
sleep(1);
}
return 0;
}
```
**2. exec 函数族**
```c
execl --- [把可变参数保存到以NULL结尾的指针函数中] ---> execv
execle --- [把可变参数保存到以NULL结尾的指针函数中] ---> execve
execlp --- [把可变参数保存到以NULL结尾的指针数组中] ---> execvp ---[依次在PATH环境变量指示的各目录中查找该程序] ---> execv --- [使用enviorn所指向的当前环境变量表] --> execve 系统调用
exec 函数族的六个成员函数的语法(头文件`#include <unistd.h>`),省略了`exec`前缀
int l (const char *path, const char *arg, ...) // 列表传递
int v (const char *path, char const *argv[]) // 字符串传递
int le (const char *path, const char *arg, ..., char *const envp[])
int ve (const char *path, char *const argv[], char *const envp[])
int lp (const char *file, const char *arg, ...)
int vp (const char *file, char *const argv[])
```
### 2.2 进程终止
有五种方式使进程终止:
- 正常终止
- 在 main 函数内执行 return 语句,等效于调用 exit
- 调用 exit 函数(由ANSI C定义),其操作包括调用各终止处理程序,然后关闭所有标准I/O流等
- 异常终止
- 调用 abort
- 由一个信号终止
### 2.3 僵尸进程
一个已经终止运行、但其父进程尚未对其**善后处理**(获取终止子进程的有关信息、释放它仍然占用的资源)的进程被称为僵尸进程(zombie),ps 将僵尸进程的状态显示为`Z`
可以在父进程中调用 `wait` 或 `waitpid` 函数,使子进程比父进程早终止,而让父进程有机会了解子进程终止时的状态
- wait 用于使父进程阻塞,直到一个子进程终止或者该进程接到了一个指定的信号为止
- waitpid 有若干选项,也能支持作业控制,wait 只是 waitpid 的一个特例
**init** 进程是Linux启动后创建的第一个进程,当一个程序退出,它的子进程会被 init 进程继承
### 2.4 守护进程
守护进程最重要的特性是**后台运行**,其次守护进程必须与其运行前的环境隔离开来(包括未关闭的文件描述符、控制终端、会话和进程组、工作目录以及文件创建掩码),可以通过 `/etc/rc.d`、`crond 进程`或用户终端启动
输入 `ps -aux`后:
- init 系统守护进程,PID 为 1,负责启动各层次特定的系统服务,这些服务通常是由它们自身的守护进程实现的
- keventd 守护进程,为在内核中运行计划执行的函数提供进程上下文
- kswapd 守护进程,页面置换守护进程
- bdflush 和 kupdated 守护进程,Linux内核使用
- portmap 端口映射守护进程,提供将RPC程序号映射为网络端口号的服务
- syslogd 守护进程,可帮助操作人员把系统消息记入日志
- inetd 守护进程(xinetd),监听系统网络接口,以便取得来自网络的对各种网络服务进程的请求
- nfsd,lockd,rpciod 守护进程,提供对网络文件系统(NFS)的支持
- cupsd 守护进程,打印脱机进程,处理对系统提出的所有打印请求
编写守护进程的要点:
1. 创建子进程,终止父进程
2. 在子进程中创建新会话(例如setsid)
3. 改变工作目录(chdir)
4. 重设文件掩码(unmask(0))
5. 关闭文件描述符(遍历NOFILE,close(i))
```c
void init_daemon(void);
int main() {
FILE *fp;
time_t t;
init_daemon();
while (1) {
sleep(10);
if (fp=fopen("xxx.log") >= 0) {
t = time(0);
fprintf(fp, "%s", asctime(localtime(&t)));
fclose(fp);
}
}
}
void init_daemon(void) {
pid_t child1, child2;
int i;
child1 = fork();
if (child1 > 0) {
exit(0); // 1. 创建子进程,终止父进程
} else if (child < 0) {
perror("创建子进程失败");
exit(1);
}
setsid(); // 2. 在子进程中创建新会话
chdir("/tmp"); // 3. 改变工作目录到 /tmp
umask(0); // 4. 重设文件创建掩码
for(i = 0; i < NOFILE; ++i) {
close(i); // 5. 关闭文件描述符
}
return;
}
```
- 空白目录
- 精简版Spring的实现
- 0 前言
- 1 注册和获取bean
- 2 抽象工厂实例化bean
- 3 注入bean属性
- 4 通过XML配置beanFactory
- 5 将bean注入到bean
- 6 加入应用程序上下文
- 7 JDK动态代理实现的方法拦截器
- 8 加入切入点和aspectj
- 9 自动创建AOP代理
- Redis原理
- 1 Redis简介与构建
- 1.1 什么是Redis
- 1.2 构建Redis
- 1.3 源码结构
- 2 Redis数据结构与对象
- 2.1 简单动态字符串
- 2.1.1 sds的结构
- 2.1.2 sds与C字符串的区别
- 2.1.3 sds主要操作的API
- 2.2 双向链表
- 2.2.1 adlist的结构
- 2.2.2 adlist和listNode的API
- 2.3 字典
- 2.3.1 字典的结构
- 2.3.2 哈希算法
- 2.3.3 解决键冲突
- 2.3.4 rehash
- 2.3.5 字典的API
- 2.4 跳跃表
- 2.4.1 跳跃表的结构
- 2.4.2 跳跃表的API
- 2.5 整数集合
- 2.5.1 整数集合的结构
- 2.5.2 整数集合的API
- 2.6 压缩列表
- 2.6.1 压缩列表的结构
- 2.6.2 压缩列表结点的结构
- 2.6.3 连锁更新
- 2.6.4 压缩列表API
- 2.7 对象
- 2.7.1 类型
- 2.7.2 编码和底层实现
- 2.7.3 字符串对象
- 2.7.4 列表对象
- 2.7.5 哈希对象
- 2.7.6 集合对象
- 2.7.7 有序集合对象
- 2.7.8 类型检查与命令多态
- 2.7.9 内存回收
- 2.7.10 对象共享
- 2.7.11 对象空转时长
- 3 单机数据库的实现
- 3.1 数据库
- 3.1.1 服务端中的数据库
- 3.1.2 切换数据库
- 3.1.3 数据库键空间
- 3.1.4 过期键的处理
- 3.1.5 数据库通知
- 3.2 RDB持久化
- 操作系统
- 2021-01-08 Linux I/O 操作
- 2021-03-01 Linux 进程控制
- 2021-03-01 Linux 进程通信
- 2021-06-11 Linux 性能优化
- 2021-06-18 性能指标
- 2022-05-05 Android 系统源码阅读笔记
- Java基础
- 2020-07-18 Java 前端编译与优化
- 2020-07-28 Java 虚拟机类加载机制
- 2020-09-11 Java 语法规则
- 2020-09-28 Java 虚拟机字节码执行引擎
- 2020-11-09 class 文件结构
- 2020-12-08 Java 内存模型
- 2021-09-06 Java 并发包
- 代码性能
- 2020-12-03 Java 字符串代码性能
- 2021-01-02 ASM 运行时增强技术
- 理解Unsafe
- Java 8
- 1 行为参数化
- 1.1 行为参数化的实现原理
- 1.2 Java 8中的行为参数化
- 1.3 行为参数化 - 排序
- 1.4 行为参数化 - 线程
- 1.5 泛型实现的行为参数化
- 1.6 小结
- 2 Lambda表达式
- 2.1 Lambda表达式的组成
- 2.2 函数式接口
- 2.2.1 Predicate
- 2.2.2 Consumer
- 2.2.3 Function
- 2.2.4 函数式接口列表
- 2.3 方法引用
- 2.3.1 方法引用的类别
- 2.3.2 构造函数引用
- 2.4 复合方法
- 2.4.1 Comparator复合
- 2.4.2 Predicate复合
- 2.4.3 Function复合
- 3 流处理
- 3.1 流简介
- 3.1.1 流的定义
- 3.1.2 流的特点
- 3.2 流操作
- 3.2.1 中间操作
- 3.2.2 终端操作
- 3.3.3 构建流
- 3.3 流API
- 3.3.1 flatMap的用法
- 3.3.2 reduce的用法
- 3.4 collect操作
- 3.4.1 collect示例
- 3.4.2 Collector接口