LINUX編程學習筆記(十四) 創建進程與 父子進程內存空間

原地址: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
  1. #include <stdio.h>  
  2.     #include <unistd.h>  
  3.   
  4.   
  5.     int main()  
  6.     {  
  7.         printf("%d\n",getpid());  
  8.         sleep(2);  
  9.         return 254;  
  10.     }  
  11.   
  12.   
  13.     sys2.c  
  14.     #include <stdio.h>  
  15.     #include <unistd.h>  
  16.     #include <wait.h>  
  17.     #include <sys/types.h>  
  18.     int main()  
  19.     {  
  20.         int r;  
  21.         printf("%d\n",getpid());  
  22.         r = system("./sleep");  
  23.         //printf("%d\n",r>>8 );  
  24.         printf("%d\n",WEXITSTATUS(r));  
  25.           
  26.     }  


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);


案例:使用popen調用ls -l 並且建立一個管道讀取輸出

  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3.   
  4.   
  5.   
  6.   
  7. int main()  
  8. {  
  9.     FILE *fp = popen("cat file","r"); //注意這裏是讀取輸出,不是打開文件 不能直接寫文件名  
  10.     //FILE *fp = popen("ls -l","r");  
  11.     if(!fp)  
  12.     {  
  13.         perror("popen");  
  14.         return 1;  
  15.     }  
  16.       
  17.     int fd = fileno(fp);  
  18.     int r;  
  19.     char buf[1024] = {0};  
  20.     while((r=read(fd,buf,1023)) > 0)  
  21.     {  
  22.         buf[r] = 0;  
  23.         printf("%s",buf);  
  24.     }  
  25.     pclose(fp);  
  26.       
  27. }  




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  失敗

  1. #include <stdio.h>  
  2.   
  3.   
  4. int main()  
  5. {  
  6.     int r = execl("/bin/ls","ls","-l",0); // 只能調用當前路徑  
  7. //  int r = execlp("ls","ls","-l",0);// 能調用系統路徑    
  8.     printf("調用結束%d\n",r);  
  9.     return 0;  
  10. }  




1.4 fork

pid_t fork()

1 創建進程
2 新進程的代碼是什麼:克隆父進程的捱罵
而且克隆來執行的位置
3 在子進程中不調用fork 所有返回值=0
4 父子進程同時執行




  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. int main()  
  4. {  
  5.     printf("創建進程前\n");  
  6.     int pid = fork(); //父進程執行的  
  7.     while(1)  
  8. {  
  9.     if(pid == 0)  
  10.     {  
  11.         printf("子進程 %d\n",getpid());  
  12.     }  
  13.     else  
  14.     {  
  15.         printf("父進程 %d\n",getpid());  
  16.     }     
  17. }     
  18. }  
  19.       







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一起用。

  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <sys/wait.h>  
  4. #include <sys/types.h>  
  5. #include <signal.h>  
  6. void deal(int s)  
  7. {  
  8.     printf("回收中\n");  
  9.     sleep(5);  
  10.     int status;   
  11.     wait(&status);  
  12.     printf("回收完畢%d\n",WEXITSTATUS(status));  
  13. }  
  14.   
  15.   
  16. int main()  
  17. {  
  18.     printf("創建進程前\n");  
  19.     int pid = fork(); //父進程執行的  
  20.     if(pid == 0)  
  21.     {  
  22.         printf("子進程 %d\n",getpid());  
  23.         sleep(5);  
  24.         return 88;  
  25.     }  
  26.     else  
  27.     {  
  28.           
  29.         printf("父進程 %d\n",getpid());  
  30.         signal(SIGCHLD,deal);  
  31.         while(1)  
  32.         {  
  33.             sleep(1);  
  34.             printf("parent\n");  
  35.         }  
  36.         return 0;  
  37.     }     
  38. }  




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:映射到不同的物理空間。

  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <sys/mman.h>  
  4.   
  5.   
  6. int main()  
  7. {  
  8.     int *a = mmap(0,4,PROT_READ|PROT_WRITE,  
  9.               MAP_ANONYMOUS|MAP_SHARED,0,0);  
  10.     *a = 20;  
  11.     int pid = fork(); //父進程執行的  
  12.     if(pid == 0)  
  13.     {  
  14.         *a= 90;  
  15.         printf("parent :a=%d\n",*a);//90  
  16.     }  
  17.     else  
  18.     {  
  19.         sleep(3);  
  20.         printf("child :a=%d\n",*a);//90  
  21.     }     
  22.       
  23. }  
  24.       

因爲使用的是MAP_SHARED ,所以射到了同一物理空間, 改動會影響其它的.

若使用MAP_PRIVATE 就可以映射到不同物理空間.


6.3 文件描述符的拷貝

每個進程都維護一個文件描述符列表。
父子進程間,拷貝了文件描述符, 相同文件描述符指向的是同一個文件內核對象。
1 各個進程的文件描述符都需要close。
2 對文件的讀寫會改變文件對象的讀寫指針位置,對所有進程都有影響。


  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <sys/mman.h>  
  4. #include <fcntl.h>  
  5.   
  6.   
  7. int main()  
  8. {  
  9.     int fd = open("test.txt",O_RDWR);  
  10.     int pid = fork(); //父進程執行的  
  11.     if(pid == 0)  
  12.     {  
  13.         printf("parent:\n");  
  14.         char buf[1024] ={0};  
  15.         lseek(fd,0,SEEK_SET);  
  16.         read(fd,buf,1023);  
  17.         printf("%s\n",buf);  
  18.         close(fd);  
  19.     }  
  20.     else  
  21.     {  
  22.         printf("child:\n");  
  23.         char buf[1024] ={0};  
  24.         lseek(fd,0,SEEK_SET);  
  25.         read(fd,buf,1023);  
  26.         printf("%s\n",buf);  
  27.         close(fd);  
  28.     }     
  29.       
  30. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章