原地址:http://blog.csdn.net/a8887396/article/details/9127701
1什麼是進程:進程是一個執行中的程序
執行的程序: 代碼->資源->CPU進程有很多數據維護:進程狀態/進程屬性
所有進程屬性採用的一個樹形結構體維護
ps -a//所有進程
ps -aue //有效進程
進程狀態:(man ps)
D Uninterruptible sleep (usually IO)
R Running or runnable (on run queue)
S Interruptible sleep (waiting for an event to complete)
T Stopped, either by a job control signal or because it is being traced.
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z Defunct ("zombie") process, terminated but not reaped by its parent.
For BSD formats and when the stat keyword is used, additional characters may be
displayed:
< high-priority (not nice to other users)
N low-priority (nice to other users)
L has pages locked into memory (for real-time and custom IO)
s is a session leader
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) 多進程
+ is in the foreground process group 前臺進程
top 實時查看進程
pstree 查看進程樹
kill 向進程發送信號 kill -s 信號 進程ID
kill -l //查看所有信號
kill -s 9 224 //關閉224進程
2 創建進程
1 進程有關的創建函數
1.1 system
int system(const char*filename);建立一個獨立的進程,擁有獨立的代碼空間
等待新的進程執行完畢,system才返回(阻塞)
使用system調用函數(觀察進程ID 觀察阻塞)
新進程的返回值和system返回值有關係
1任何進程的返回值不要超過255
2 system中進程的返回值存放在system返回值的8-15位
可以通過printf("%d\n",r>>8 ); 這樣來獲得返回值
3 linux中有專用的獲得返回狀態的宏
WEXITSTATUS(status) //#include<wait.h> #include <sys/wait.h>
sys.c
- #include <stdio.h>
- #include <unistd.h>
- int main()
- {
- printf("%d\n",getpid());
- sleep(2);
- return 254;
- }
- sys2.c
- #include <stdio.h>
- #include <unistd.h>
- #include <wait.h>
- #include <sys/types.h>
- int main()
- {
- int r;
- printf("%d\n",getpid());
- r = system("./sleep");
- //printf("%d\n",r>>8 );
- printf("%d\n",WEXITSTATUS(r));
- }
gcc sys.c -o sleep
gcc sys2.c
./a.out
1.2 popen:創建子進程
FILE *popen(const char *command, const char *type);int pclose(FILE *stream);
- #include <stdio.h>
- #include <unistd.h>
- int main()
- {
- FILE *fp = popen("cat file","r"); //注意這裏是讀取輸出,不是打開文件 不能直接寫文件名
- //FILE *fp = popen("ls -l","r");
- if(!fp)
- {
- perror("popen");
- return 1;
- }
- int fd = fileno(fp);
- int r;
- char buf[1024] = {0};
- while((r=read(fd,buf,1023)) > 0)
- {
- buf[r] = 0;
- printf("%s",buf);
- }
- pclose(fp);
- }
1.3 exec系列函數
int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
作用: 替換當前代碼空間中的數據
函數本身不創建新的進程
第一個參數:替換的程序
第二個參數: 命令行
命令行格式:命令名 選項參數 NULL
命令行結尾必須是空字符串
execl execlp的區別:
execl 只在當前路徑搜索
execlp 可以使用系統搜索路徑(which能找到)
如果都找不到,可以使用絕對路徑
命令鎖指向的程序 命令本身 參數列表
如果返回-1 失敗
- #include <stdio.h>
- int main()
- {
- int r = execl("/bin/ls","ls","-l",0); // 只能調用當前路徑
- // int r = execlp("ls","ls","-l",0);// 能調用系統路徑
- printf("調用結束%d\n",r);
- return 0;
- }
1.4 fork
pid_t fork()
1 創建進程2 新進程的代碼是什麼:克隆父進程的捱罵
而且克隆來執行的位置
3 在子進程中不調用fork 所有返回值=0
4 父子進程同時執行
- #include <stdio.h>
- #include <unistd.h>
- int main()
- {
- printf("創建進程前\n");
- int pid = fork(); //父進程執行的
- while(1)
- {
- if(pid == 0)
- {
- printf("子進程 %d\n",getpid());
- }
- else
- {
- printf("父進程 %d\n",getpid());
- }
- }
- }
3 應用進程
使用fork實現多任務(unix本身是不支持線程的)1 進程
2 線程
3 信號
4 異步
5 進程池與線程池
4 理解進程
1父子進程的關係獨立的兩個進程
互爲父子關係
使用pstree看到
├─gnome-terminal─┬─bash───a.out───a.out //父子關係
│ ├─bash───pstree
│ ├─gnome-pty-helpe
│ └─2*[{gnome-terminal}]
2 問題:
1如果父進程先結束 子進程在麼辦
子進程將變成孤兒進程,直接依託根進程(init)
孤兒 進程是沒有危害的
init─┬─NetworkManager─┬─dhclient
│ └─{NetworkManager}
├─a.out
2 如果子進程先結束 父進程怎麼辦
子進程先結束,子進程會成爲殭屍進程。
殭屍進程的特點: 不佔用內存 cpu,但在進程任務管理樹上佔用一個節點(寶貴)
實際上殭屍進程會造成進程名額的資源浪費。一定要處理殭屍進程
├─gnome-terminal─┬─bash───pstree
│ ├─bash───a.out───a.out
3 殭屍進程使用wait回收(阻塞函數)
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
wait 阻塞直到任意進程結束,status來接收進程返回值,返回值表示返回的進程號
waitpid 阻塞直到指定進程結束
WEXITSTATUS(status) 解析返回值 status的 8-15位是進程的返回值
4 父進程怎麼知道子進程退出?
子進程結束時,通常會向父進程發送一個SIGCHLD信號
5父進程處理子進程的信號
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal 的功能 :
向系統註冊,只要接收到信號signal,系統停止進程,執行handler函數,
當函數執行完畢,繼續原來的進程 (軟中斷)
5.1實現處理函數
5.2 使用signal綁定信號與函數
只有當子進程退出時才用wait,因爲wait是一個阻塞函數。所以wait和signal一起用。
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/wait.h>
- #include <sys/types.h>
- #include <signal.h>
- void deal(int s)
- {
- printf("回收中\n");
- sleep(5);
- int status;
- wait(&status);
- printf("回收完畢%d\n",WEXITSTATUS(status));
- }
- int main()
- {
- printf("創建進程前\n");
- int pid = fork(); //父進程執行的
- if(pid == 0)
- {
- printf("子進程 %d\n",getpid());
- sleep(5);
- return 88;
- }
- else
- {
- printf("父進程 %d\n",getpid());
- signal(SIGCHLD,deal);
- while(1)
- {
- sleep(1);
- printf("parent\n");
- }
- return 0;
- }
- }
zhao@ubuntu:~/unix/5$ ./a.out
創建進程前
父進程 2324
子進程 2325
parent
parent
parent
parent
回收中
回收完畢88
parent
parent
parent
^C
6 父子進程的內存空間
6.1 全局變量 局部變量 堆變量 都會被子進程拷貝,但與原先的獨立。
注意 堆內存被複制了,需要分別在各個進程中手動釋放。子進程克隆了父進程的全局區和局部區內存,但內存區域指向不同的物理空間。
儘管克隆但內存獨立,不能相互訪問。
進程間通信(IPC)是大問題。
6.2 內存映射與子進程:
內存映射的屬性,決定子進程和父進程是否映射在同一物理空間。MAP_SHARED: 映射到同一物理空間。 (改一個進程中的,其他進程的也變化)
MAP_PRIVATE:映射到不同的物理空間。
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/mman.h>
- int main()
- {
- int *a = mmap(0,4,PROT_READ|PROT_WRITE,
- MAP_ANONYMOUS|MAP_SHARED,0,0);
- *a = 20;
- int pid = fork(); //父進程執行的
- if(pid == 0)
- {
- *a= 90;
- printf("parent :a=%d\n",*a);//90
- }
- else
- {
- sleep(3);
- printf("child :a=%d\n",*a);//90
- }
- }
因爲使用的是MAP_SHARED ,所以射到了同一物理空間, 改動會影響其它的.
若使用MAP_PRIVATE 就可以映射到不同物理空間.
6.3 文件描述符的拷貝
每個進程都維護一個文件描述符列表。父子進程間,拷貝了文件描述符, 相同文件描述符指向的是同一個文件內核對象。
1 各個進程的文件描述符都需要close。
2 對文件的讀寫會改變文件對象的讀寫指針位置,對所有進程都有影響。
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/mman.h>
- #include <fcntl.h>
- int main()
- {
- int fd = open("test.txt",O_RDWR);
- int pid = fork(); //父進程執行的
- if(pid == 0)
- {
- printf("parent:\n");
- char buf[1024] ={0};
- lseek(fd,0,SEEK_SET);
- read(fd,buf,1023);
- printf("%s\n",buf);
- close(fd);
- }
- else
- {
- printf("child:\n");
- char buf[1024] ={0};
- lseek(fd,0,SEEK_SET);
- read(fd,buf,1023);
- printf("%s\n",buf);
- close(fd);
- }
- }