zoukankan      html  css  js  c++  java
  • 二十二、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
    ", 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
    ",
    38                 getpid(), getppid(), pid);
    39         printf("g_val: %p, a_val: %p, s_val: %p
    ", &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
    ",
    47                 getpid(), getppid(), pid);
    48         printf("g_val: %p, a_val: %p, s_val: %p
    ", &g_val, &a_val, &s_val);
    49     }
    50 
    51     //这里的代码是父子进程都要执行的代码,写入父子进程各自的缓存当中
    52     fprintf(fp, " pid: %d, g_val: %d, a_val: %d, s_val: %d
    ", 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
    ", 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
    ", getpid());
    53     sleep(1);
    54 
    55     //父子进程都要去关闭文件描述符
    56     close(fd);
    57 
    58     return 0;
    59 }

      编译运行:

      

      

  • 相关阅读:
    【转】很全的TeeChart for .NET中文教程
    对比两个flash金融图表Stock Chart vs AnyStock
    FusionCharts实例大全
    [译]金融图表AnyStock的9个使用技巧
    看懂SqlServer查询计划
    怎样修改MySQL的默认编码
    VS2003+自带水晶报表的打包部署(CS方式)
    ADO.NET 连接池理解
    临时表 & 表变量
    Eclipse 快捷键介绍
  • 原文地址:https://www.cnblogs.com/kele-dad/p/9135900.html
Copyright © 2011-2022 走看看