相關概念
- 程序: 編譯好的二進制文件, 存放在磁盤上(占用的是物理內存空間), 不占用系統資源(CPU, 內存, 打開的文件, 設備, 鎖...)
- 進程: 抽象的概念, 與操作系統原理聯系緊密, 是活躍的程序, 占用系統資源, 在內存中執行.
由原來的單道程序設計轉換成多道程序設計, 是CPU分成若干的時間碎片, 各個進程搶占資源去執行, 雖然程序有優先級高低, 但是只是搶占到的幾率高; 即使是多核, 也是需要時才會開啟其他核.
處理速度:寄存器->cache->內存->硬盤->網絡->存儲介質
其他概念: 串行和并發, CPU和MMU - 進程控制塊 PCB 在/usr/src/linux-headers-3.15.0-30/include/linux/sched.h文件中可以查看struct task_struct 結構體定義, 包含部分定義: 進程id, 進程狀態, 進程切換時需要保存和回復的CPU寄存器, 描述虛擬的地址空間信息, 當前工作目錄, 文件描述符表, 用戶id和組id, 會話和進程組, 資源上限(命令:ulimit -a 查看), umask掩碼...
- 進程狀態: 初始態(創建時, 非常短暫), 就緒態(有執行資格, 但是沒執行權限, 需要搶占資源), 運行態(正在執行), 掛起態(沒有執行資格和執行權限), 終止態
點我查看進程狀態圖
進程控制
程序默認的是單進程的; 下面介紹多進程的使用
創建進程函數: pid_t fork(void);(查看 Linux下使用命令:man 2 fork)
點擊查看描述圖
返回值: 失敗返回-1, 父進程返回子進程ID, 子進程返回0
查看當前進程 ps aux | grep "要查找的程序"; ps ajx
父子進程.png
剛fork完成時(父子進程之間) 1. 0-3G地址空間相同 2. PCB相同, 但是pid除外 3.其他內容由mmu映射到物理內存上, 讀時共享, 寫時復制
獲取pid 和 ppid 對應的函數是getpid(); getppid();
//wait 函數回收子進程
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid;
pid = fork();
if(pid == -1) {
perror("fork error");
exit(-1);
} else if(pid > 0) {
printf("parent process\n");
} else if(pid == 0) {
printf("child process\n");
}
printf("mul process\n");
return 0;
}
回收進程
進程有孤兒進程, 僵尸進程
- 孤兒進程 : 子進程活著, 父進程死了, linxu中的init進程會領養孤兒
- 僵尸進程 : 子進程死了, 父進程正在忙, 沒有去回收子進程
- 進程回收 : 代碼附在下邊
wait - 阻塞函數
pid_t wait(int* status);
調用一次回收一個子進程資源
返回值:
0: 回收的子進程的pid;
-1: 沒有子進程可以回收了;
status -- 傳出參數
獲取退出時候的返回值
- 正常退出( return num; exit(num); exit(num);
)
WIFEXITED(status) > 0
獲取返回值: WEXITSTATUS(status)
- 被信號殺死
WIFSIGNALED(status) > 0
獲取殺死子進程的信號
WTERMSIG(status)
waitpid -- pid_t waitpid(pid_t pid, int *status, int options);
(可以設置非阻塞, 提高wait的效率)
pid: -1: 所有的子進程
0: 當前進程組的子進程0(pid): 回收指定進程的pcb
-pid: 回收不在當前進程組的子進程
opttions: 阻塞: 0
非阻塞: WNOHANG 可以設置為非阻塞
可以有針對性的回收某一個子進程資源 ;需要注意的是即使是非阻塞, 也需要循環來回收子進程
//wait函數回收子進程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/wait.h>
int counter = 100;
int main(int argc, const char* argv[])
{
int number = 5;
int i;
pid_t pid;
for(i=0; i<number; ++i)
{
pid = fork();
if(pid == 0)
break;
}
if(i<number)
{
// 子進程
counter += 100;
printf("child pid = %d, ppid = %d\n", getpid(), getppid());
printf("counter = %d\n\n", counter);
//sleep(100);
exit(9);
}
else if(i == number)
{
counter += 300;
printf("parent pid = %d, ppid = %d\n", getpid(), getppid());
printf("counter = %d\n\n", counter);
sleep(1);
// 回收子進程
int status;
pid_t wpid;
while( (wpid=wait(&status)) != -1)
{
// 正常退出
if(WIFEXITED(status))
{
printf("porcess exit by number: %d\n", WEXITSTATUS(status));
}
// 被信號殺死
else if(WIFSIGNALED(status))
{
printf("process kill by signal: %d\n", WTERMSIG(status));
}
printf("child died pid = %d\n", wpid);
}
}
return 10;
}
//驗證父子進程是否文件共享
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main(int argc, const char* argv[]) {
int fd = open("temp", O_CREAT | O_RDWR, 0664);
if(fd == -1) {
perror("open error");
exit(1);
}
pid_t pid = fork();
if(pid == -1) {
perror("fork error");
exit(1);
}
if(pid > 0) {
char* p = "123123123";
write(fd, p, strlen(p)+1);
} else if(pid == 0) {
// 睡1s保證父進程已經完成了文件的寫操作
sleep(1);
char buf[1024];
lseek(fd, 0, SEEK_SET);
int len = read(fd, buf, sizeof(buf));
printf("%s\n", buf);
}
close(fd);
return 0;
}
//子進程執行不同任務, 父進程負責回收子進程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc, const char* argv[])
{
int i = 0;
int number = 3;
pid_t pid;
for(i = 0; i<number; ++i)
{
pid = fork();
if(pid == 0)
{
break;
}
}
// 父進程
if(i == number)
{
sleep(2);
// 回收子進程
pid_t wpid;
int status;
while( (wpid = waitpid(0, &status, WNOHANG)) != -1 )
{
if(wpid == 0)
{
continue;
}
printf("child pid = %d\n", wpid);
if(WIFEXITED(status))
{
printf("return number: %d\n", WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("exit by signal: %d\n", WTERMSIG(status));
}
}
}
else if(i == 0)
{
execl("/home/kevin/test/app", "app", NULL);
}
else if(i == 1)
{
execl("./error", "error", NULL);
}
else if(i == 2)
{
execlp("ps", "ps", "aux", NULL);
}
printf("over......\n");
return 0;
}
//waitpid 來回收子進程, 注意非阻塞時也需要循環去回收
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc, const char* argv[])
{
int num = 3;
int i = 0;
pid_t pid;
for(i=0; i<num; ++i)
{
pid = fork();
if(pid == 0)
{
break;
}
}
if(i == 0)
{
execlp("ps", "ps", "aux", NULL);
perror("execlp ps");
exit(1);
}
else if(i == 1)
{
execl("/home/kevin/test/app", "app", NULL);
perror("execl app");
exit(1);
}
else if(i == 2)
{
execl("./error", "error", NULL);
perror("execl error");
exit(1);
}
else if(i == num)
{
// 回收
int status;
pid_t wpid;
while( (wpid = waitpid(-1, &status, WNOHANG)) != -1 )
{
if(wpid == 0)
continue;
printf(" ----- child died pid = %d\n", wpid);
if(WIFEXITED(status))
{
printf("return value %d\n", WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("died by signal: %d\n", WTERMSIG(status));
}
}
}
return 0;
}