络腮胡菲菲 发表于 2024-10-22 17:20:55

【linux 多进程并发】0202 Linux进程fork之后父子进程间的文件操纵有着相同

0202 Linux进程资源

   ​专栏内容:


[*]postgresql利用入门基础
[*]手写数据库toadb
[*]并发编程
    个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.


一、概述

进程启动之后,除了占用内存资源之外,在进程中还会打开文件,共享内存,网络套接字等与内核对象关联的资源,
这些资源在子进程中如何处理呢?
本节以文件为例,分析父子进程间对于与内核相关联的资源的处理情况,并利用代码实例举行演示。
二、资源创建场景

在利用fork创建子进程的时候,对资源的利用大致分为两类情况:

[*]也可能是运行了一段步伐,初始化很多资源;然后再创建多进程任务;
   此时,子进程会继承父进程创建的资源;
同时,要在子进程中关闭和释放不再利用的资源,否则会产生资源泄漏;

[*]也可能是刚开始运行,先创建多任务,然后根据不同任务的分工不同,再初始化各自的资源;
   此时,各子进程中没有继承的资源;
在第一类场景下,对于关联内核对象的资源,继承后的特性也会与单进程利用有一定区别,下面通过案例来分析一下。
三、资源在子进程中创建

资源在fork之后创建,也就是在子进程中创建。
这种情况与我们常规编写一个main函数中创建类似,也就是单进程步伐一样,各子进程间互相独立,没有接洽。
https://i-blog.csdnimg.cn/direct/a65cdb202b7d47409c935a9f09973cb5.png#pic_center
3.1 示例代码



[*]下面通过一段测试代码来演示一下:
/*
* ex020104_forkafter.c
*/
#include <stdio.h>

#include <sys/types.h>
#include <unistd.h>

#include <errno.h>
#include <string.h>

typedef struct Info
{
        int pid;
        int seg;
}Info;

FILE *fp = NULL;
int segno = 0;

void OpenFile(char *path);
void CloseFile();
void ReadFile();
void WriteFile();

int main(int argc ,char *argv[])
{
        int pid = -1;

        pid = fork();
        if(pid == 0)
        {
                OpenFile("test");

                // in child
                printf("here in child ,my pid is %d\n", getpid());

                WriteFile();
                sleep(5);

                WriteFile();
                sleep(5);
               
                WriteFile();
        }
        else if(pid > 0)
        {
                OpenFile("test");
                // in parent
                printf("here in parent, my pid %d, child pid is %d\n", getpid(), pid);

                sleep(3);
                ReadFile();

                sleep(5);
                ReadFile();

                sleep(5);
                ReadFile();
        }
        else
        {
                // error
                printf("fork error[%s]\n",strerror(errno));
        }

        CloseFile();
        return 0;
}
在创建子进程之后,父子进程中同时打开test文件,举行以下步骤:

[*]子进程从文件头写入结构体数据;
[*]父进程从文件头读一个结构体巨细的数据;
[*]然后子进程在上次偏移位置继续写入结构体巨细的数据;
[*]接着父进程从自己的偏移位置处读入结构体数据;
[*]继续一次步骤3和4;
[*]在进程结束时,关闭文件。
公共的文件操纵函数
为了方便多次测试,将打开文件,关闭文件,读写文件编写为公共函数。
void OpenFile(char *path)
{
        if(NULL == path)
                return;

        fp = fopen(path, "w+");
        if(NULL == fp)
        {
                printf("open file %s error!\n", path);
        }

        return;
}

void CloseFile()
{
        if(NULL != fp)
                fclose(fp);
}

void ReadFile()
{
        Info rinfo = {0};
        int pos = 0;

        if(NULL == fp)
                return ;

        /* data read from current position record by the fp. */
        pos = ftell(fp);
        fread(&rinfo, sizeof(rinfo), 1, fp);       
        printf("[%d] read from %d, context(%d, %d) \n", getpid(), pos, rinfo.pid, rinfo.seg);
}

void WriteFile()
{
        Info winfo = {0};
        int pos = 0;

        if(NULL == fp)
                return ;

        winfo.seg = segno++;
        winfo.pid = getpid();

        /* data read from current position record by the fp. */
        pos = ftell(fp);
        fwrite(&winfo, sizeof(winfo), 1, fp);       
        fflush(fp);

        printf("[%d] write to %d, context(%d, %d) \n", getpid(), pos, winfo.pid, winfo.seg);
}

3.2 进程独立验证



[*]编译运行:
$ gcc ex020104_forkafter.c -o extest
$ ./extest
here in parent, my pid 802470, child pid is 802471
here in child ,my pid is 802471
write to 0, context(802471, 0)
read from 0, context(802471, 0)
write to 8, context(802471, 1)
read from 8, context(802471, 1)
write to 16, context(802471, 2)
read from 16, context(802471, 2)


[*]结果分析
   

[*]可以看到进程802471从文件偏移为0处开始写入数据;
[*]而父进程802470也是从偏移为0处读数据;
[*]两个进程的偏移都是各自计数,互相独立;文件偏移记录在内核文件表项中;
四、步伐启动时资源创建

如果在步伐启动时创建文件,也就是在fork之前创建,子进程会继承之前创建的文件句柄。
https://i-blog.csdnimg.cn/direct/c5288054278a4052a8bf9c96daddb05f.png#pic_center
在这种情况下,会出现什么样的情况呢?
4.1 示例代码

/*
* ex020103_forkresource.c
*/
#include <stdio.h>

#include <sys/types.h>
#include <unistd.h>

#include <errno.h>
#include <string.h>

typedef struct Info
{
        int pid;
        int seg;
}Info;

FILE *fp = NULL;
int segno = 0;

void OpenFile(char *path);
void CloseFile();
void ReadFile();
void WriteFile();

int main(int argc ,char *argv[])
{
        int pid = -1;

        OpenFile("test");

        pid = fork();
        if(pid == 0)
        {
                // in child
                printf("here in child ,my pid is %d\n", getpid());

                WriteFile();
                sleep(5);


                WriteFile();
      sleep(5);

                WriteFile();
        }
        else if(pid > 0)
        {
                // in parent
                printf("here in parent, my pid %d, child pid is %d\n", getpid(), pid);

                sleep(3);
                ReadFile();

                sleep(5);
                ReadFile();

                sleep(5);
                ReadFile();
        }
        else
        {
                // error
                printf("fork error[%s]\n",strerror(errno));
        }

        CloseFile();
        return 0;
}
这段代码与前例类似,只是将创建文件放在了fork之前,
也就是FILE *fp 在主步伐中先被初始化了,然后创建了子进程,在子进程中引用了一模一样的内容,类似于拷备了一份。
4.2 共用内核对象



[*]编译运行
$ gcc ex020103_forkresource.c -o extest1
$ ./extest1
here in parent, my pid 803877, child pid is 803878
here in child ,my pid is 803878
write to 0, context(803878, 0)
read from 8, context(0, 0)
write to 8, context(803878, 1)
read from 16, context(0, 0)
write to 16, context(803878, 2)
read from 24, context(0, 0)
可以看到很有意思的征象:


[*]在子进程803878中,在文件偏移0处写入,之后偏移变为8;
[*]而之后父进程803877开始读时,文件偏移为8,并没有从0开始;
[*]接着子进程803878又从偏移8处写入数据,之后偏移变为16;
[*]而父进程803877读偏移也酿成了16;
[*]后面一次,也是父进程中文件偏移,与子进程中文件偏移相互接洽;
总结

好了,到这里,子进程是父进程的拷贝有了更加深入的理解,这里像编程语言中的深拷贝与浅拷贝的关系。
而子进程其实是做了一些浅拷贝,引用的内核文件表项照旧一份,这就会引起两个进程共同操纵的问题。
在这种情况下,每次操纵需要加锁,同时要指定操纵的位置和巨细。
末端

   非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的批评,如果觉得值得鼓励,请点赞,收藏,我会更加积极!
作者邮箱:study@senllang.onaliyun.com
如有错误大概疏漏欢迎指出,互相学习。
注:未经同意,不得转载!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【linux 多进程并发】0202 Linux进程fork之后父子进程间的文件操纵有着相同