二十二、Linux 進程與信號---進程創建(續)
阿新 • • 發佈:2018-06-06
裏的 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;//局部變量,調用的時候存放在棧中 12static 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 進程與信號---進程創建(續)