二十二、Linux 进程与信号---进程创建(续)

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 }

  编译运行:

  

  

 

posted @ 2018-06-05 22:03  游戏进行中  阅读(187)  评论(0编辑  收藏  举报