0202 Linux进程资源
专栏内容:
- postgresql利用入门基础
- 手写数据库toadb
- 并发编程
个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
一、概述
进程启动之后,除了占用内存资源之外,在进程中还会打开文件,共享内存,网络套接字等与内核对象关联的资源,
这些资源在子进程中如何处理呢?
本节以文件为例,分析父子进程间对于与内核相关联的资源的处理情况,并利用代码实例举行演示。
二、资源创建场景
在利用fork创建子进程的时候,对资源的利用大致分为两类情况:
- 也可能是运行了一段步伐,初始化很多资源;然后再创建多进程任务;
此时,子进程会继承父进程创建的资源;
同时,要在子进程中关闭和释放不再利用的资源,否则会产生资源泄漏;
- 也可能是刚开始运行,先创建多任务,然后根据不同任务的分工不同,再初始化各自的资源;
此时,各子进程中没有继承的资源;
在第一类场景下,对于关联内核对象的资源,继承后的特性也会与单进程利用有一定区别,下面通过案例来分析一下。
三、资源在子进程中创建
资源在fork之后创建,也就是在子进程中创建。
这种情况与我们常规编写一个main函数中创建类似,也就是单进程步伐一样,各子进程间互相独立,没有接洽。
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 进程独立验证
- [senllang@hatch ex_0201]$ gcc ex020104_forkafter.c -o extest
- [senllang@hatch ex_0201]$ ./extest
- here in parent, my pid 802470, child pid is 802471
- here in child ,my pid is 802471
- [802471] write to 0, context(802471, 0)
- [802470] read from 0, context(802471, 0)
- [802471] write to 8, context(802471, 1)
- [802470] read from 8, context(802471, 1)
- [802471] write to 16, context(802471, 2)
- [802470] read from 16, context(802471, 2)
复制代码
- 可以看到进程802471从文件偏移为0处开始写入数据;
- 而父进程802470也是从偏移为0处读数据;
- 两个进程的偏移都是各自计数,互相独立;文件偏移记录在内核文件表项中;
四、步伐启动时资源创建
如果在步伐启动时创建文件,也就是在fork之前创建,子进程会继承之前创建的文件句柄。
在这种情况下,会出现什么样的情况呢?
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 共用内核对象
- [senllang@hatch ex_0201]$ gcc ex020103_forkresource.c -o extest1
- [senllang@hatch ex_0201]$ ./extest1
- here in parent, my pid 803877, child pid is 803878
- here in child ,my pid is 803878
- [803878] write to 0, context(803878, 0)
- [803877] read from 8, context(0, 0)
- [803878] write to 8, context(803878, 1)
- [803877] read from 16, context(0, 0)
- [803878] write to 16, context(803878, 2)
- [803877] read from 24, context(0, 0)
复制代码 可以看到很有意思的征象:
- 在子进程803878中,在文件偏移0处写入,之后偏移变为8;
- 而之后父进程803877开始读时,文件偏移为8,并没有从0开始;
- 接着子进程803878又从偏移8处写入数据,之后偏移变为16;
- 而父进程803877读偏移也酿成了16;
- 后面一次,也是父进程中文件偏移,与子进程中文件偏移相互接洽;
总结
好了,到这里,子进程是父进程的拷贝有了更加深入的理解,这里像编程语言中的深拷贝与浅拷贝的关系。
而子进程其实是做了一些浅拷贝,引用的内核文件表项照旧一份,这就会引起两个进程共同操纵的问题。
在这种情况下,每次操纵需要加锁,同时要指定操纵的位置和巨细。
末端
非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的批评,如果觉得值得鼓励,请点赞,收藏,我会更加积极!
作者邮箱:study@senllang.onaliyun.com
如有错误大概疏漏欢迎指出,互相学习。
注:未经同意,不得转载!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |