1. 程式人生 > >二十二、Linux 進程與信號---進程創建(續)

二十二、Linux 進程與信號---進程創建(續)

裏的 close %s 關閉 lee else wro 執行 靜態

22.2 父子進程操作文件

文件操作由兩種模式:

  IO 系統調用操作文件

  標準C IO 操作文件

看代碼:

 1 #include <unistd.h>
 2 #include <string.h>
 3 #include <fcntl.h>
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 
 7 int g_val = 30;//全局變量,存放在數據段
 8 
 9 int main(void)
10 {
11     int a_val = 30;//局部變量,調用的時候存放在棧中
12
static int s_val = 30;//靜態變量,存放在數據段 13 printf("pid: %d", getpid()); 14 15 FILE *fp = fopen("s.txt", "w"); 16 int fd = open("s_fd.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU | S_IRWXG); 17 18 char *s = "hello world"; 19 ssize_t size = strlen(s) * sizeof(char); 20 21 /* fork 之前,為父進程調用
*/ 22 fprintf(fp, "s: %s, pid: %d\n", s, getpid());//標準 IO 寫入(帶緩存),針對文件操作的是全緩存 23 write(fd, s, size); //內核提供的 IO 系統調用(不帶緩存) 24 25 pid_t pid; 26 pid = fork();//創建子進程 27 //在 fork 後,會運行兩個進程(父進程和子進程) 28 if(pid < 0) { 29 perror("fork error"); 30 } else if(pid > 0) { 31 //
父進程(在父進程中返回的是子進程的 pid) 32 //父進程執行的代碼 33 g_val = 40; 34 a_val = 40; 35 s_val = 40; 36 37 printf("I am parent process pid is %d, ppid is %d, fork return is %d\n", 38 getpid(), getppid(), pid); 39 printf("g_val: %p, a_val: %p, s_val: %p\n", &g_val, &a_val, &s_val); 40 } else { 41 //子進程(在子進程中 fork 返回的是0) 42 //子進程執行的代碼 43 g_val = 50; 44 a_val = 50; 45 s_val = 50; 46 printf("I am child process pid is %d, ppid is %d, fork return is %d\n", 47 getpid(), getppid(), pid); 48 printf("g_val: %p, a_val: %p, s_val: %p\n", &g_val, &a_val, &s_val); 49 } 50 51 //這裏的代碼是父子進程都要執行的代碼,寫入父子進程各自的緩存當中 52 fprintf(fp, " pid: %d, g_val: %d, a_val: %d, s_val: %d\n", getpid(), g_val, a_val, s_val); 53 sleep(1); 54 55 return 0; 56 }

  編譯運行後,兩個文件都生成了。

  技術分享圖片

  父進程文件 s.txt

  技術分享圖片

  子進程文件 s_fd.txt

  技術分享圖片

  系統調用不經過緩存,執行 write 後就直接寫進了文件當中,標準IO是寫入緩存了。

  創建的緩存是在堆當中的,我們的代碼是在 fork 之前,那麽緩存就在父進程的虛擬空間的堆當中,當 fork 之後,子進程會 COPY 一份父進程的堆空間。

  同樣 fork 之後也由一份寫入緩存的 fprintf,此時是各自寫入各自的緩存,在結束的時候父子進程都會清緩存,都會寫入 fp 當中

  技術分享圖片

22.3 操作文件時的內核結構體變化

  • 子進程只繼承父進程的文件描述符表,不繼承但共享文件表項和 i-node
  • 父進程創建一個子進程後,文件表項中的引用計數器加1 變成 2,當父進程作 close 操作後,計數器減 1,子進程還是可以使用文件表項(即子進程還是可以操作文件),只有當計數器為 0 時,才會釋放文件表項。

  技術分享圖片

  運行 fork:

  技術分享圖片

  例子:父進程調節文件偏移量,子進程寫入

  process_append.c

 1 #include <unistd.h>
 2 #include <fcntl.h>
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 
 7 int main(int argc, char *argv[])
 8 {
 9     if(argc < 2)
10     {
11         fprintf(stderr, "usage: %s file\n", argv[0]);
12         exit(1);
13     }
14 
15     int fd = open(argv[1], O_WRONLY);
16     if(fd < 0)
17     {
18         perror("open error");
19         exit(1);
20     }
21 
22     pid_t pid = fork();
23     if(pid < 0)
24     {
25         perror("fork error");
26         exit(1);
27     }
28     else if(pid > 0)
29     {//父進程將文件偏移量調整到文件尾部
30         if(lseek(fd, 0L, SEEK_END) < 0) {
31             perror("lseek error");
32             exit(1);
33         }
34     }
35     else
36     {//子進程從文件尾部追加內容
37         char *str = "hello child";
38         ssize_t size = strlen(str) * sizeof(char);
39 
40         sleep(3);//保證父進程調節偏移量成功
41 
42         //從用戶角度去看,子進程會復制一份父進程的文件描述符,都指向同一個文件
43         //從內核角度區看,文件描述符表復制了一份,文件描述符表指向了同一個文件表項,都指向同一個文件
44         //此處的 fd 是從父進程中復制過來的
45         //但和父進程中的 fd 都是指向同一個文件的
46         if(write(fd, str, size) != size) {
47             perror("write error");
48             exit(1);
49         }
50     }
51 
52     printf("pid ; %d finish\n", getpid());
53     sleep(1);
54 
55     //父子進程都要去關閉文件描述符
56     close(fd);
57 
58     return 0;
59 }

  編譯運行:

  技術分享圖片

  技術分享圖片

二十二、Linux 進程與信號---進程創建(續)