> 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接口