前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux系统进程编程(二)

Linux系统进程编程(二)

作者头像
用户6280468
发布2022-03-18 20:48:35
1.6K0
发布2022-03-18 20:48:35
举报
文章被收录于专栏:txp玩Linuxtxp玩Linux

在上一篇文章中,我们已经对进程这个概念有了一个基本的认识,今天我们来继续学习进程的实战操作----父子进程对文件的操作,以及什么是僵尸进程和孤儿进程?下面我们就开始来揭开它们神秘的面纱!

一、父子进程对文件的操作:

1、子进程继承父进程中打开的文件:

父进程先open打开一个文件得到fd,然后再用fork函数来创建子进程。之后在父子进程中各自write向fd中写入内容,代码如下:

代码语言:javascript
复制
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{

// 首先打开一个文件
int fd = -1;
pid_t pid = -1;

fd = open("1.txt", O_RDWR | O_TRUNC);
if (fd < 0)
{
    perror("open");
    return -1;
}

// fork创建子进程
pid = fork();
if (pid > 0)
{
    // 父进程中
    printf("parent.\n");
    write(fd, "IOTNB", 5);
    sleep(1);
}
else if (pid == 0)
{
    // 子进程
    printf("child.\n");
    write(fd, "MCU", 3);
    sleep(1);
}
else
{
    perror("fork");
    exit(-1);
}
close(fd);
return 0;
  }

输出结果:

说明:这里我们可以看到,子进程继承父进程中打开的文件结果是接续(什么是接续写,简单理解就是对文件写操作完后,另外一个操作接着前面的那个操作继续往文件里面写东西)写的。而且还要注意的是,实际测试时有时候会看到只有一个,有点像分别写。但是实际不是。原因是父进程写完后直接把文件关闭了,关闭后子进程就写不进去内容了

2、父子进程各自独立打开同一文件实现共享:

现在我们来把文件打开操作分别放到父进程和子进程当中去,看看会有什么效果,代码如下:

代码语言:javascript
复制
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
  int main(void)
{
// 首先打开一个文件
int fd = -1;
pid_t pid = -1;

// fork创建子进程
pid = fork();
if (pid > 0)
{
    // 父进程中
    fd = open("1.txt", O_RDWR | O_TRUNC );
    if (fd < 0)
    {
        perror("open");
        return -1;
    }

    printf("parent.\n");
    write(fd, "hello", 5);
    sleep(1);
}
else if (pid == 0)
{
    // 子进程
    fd = open("1.txt", O_RDWR | O_TRUNC);
    if (fd < 0)
    {
        perror("open");
        return -1;
    }

    printf("child.\n");
    write(fd, "world", 5);
    sleep(1);
}
else
{
    perror("fork");
    exit(-1);
}
close(fd);
    return 0;
 }

输出结果:

说明:最终结果是分别写。原因是父子进程分离后才各自打开的1.txt,这时候这两个进程的PCB已经独立了,文件表也独立了,因此2次读写是完全独立的,当然我们在打开文件操作时,改变open函数里面的参数,使用O_APPEND,还是上面这个操作,最终它的结果把父子进程各自独立打开的fd的文件指针给关联起来,实现接续写,这里我就不举例了,很简单,只要把上面的参数O_TRUNC改成O_APPEND就可以了,这两个参数的意思,在我前面的文章里面有写过,非常详细。

3、小结:其实父进程在没有fork之前自己做的事情对子进程有很大影响,但是父进程fork之后在自己的if里做的事情就对子进程没有影响了。本质原因就是因为fork内部实际上已经复制父进程的PCB生成了一个新的子进程,并且fork返回时子进程已经完全和父进程脱离并且独立被OS调度执行。

二、僵尸进程和孤儿进程解析:

1、什么是僵尸进程?

哈哈哈,听到僵尸两个字是不是有点小害怕,言归正传,在Linux系统中,我们要明白:进程在运行时是需要消耗系统资源(内存、IO),进程终止时理应完全释放这些资源(如果进程消亡后仍然没有释放相应资源则这些资源就丢失了),所以linux系统设计时规定:每一个进程退出时,操作系统会自动回收这个进程涉及到的所有的资源(譬如malloc申请的内容没有free时,当前进程结束时这个内存会被释放,譬如open打开的文件没有close的在程序终止时也会被关闭)。但是操作系统只是回收了这个进程工作时消耗的内存和IO,而并没有回收这个进程本身占用的内存(8KB,主要是task_struct和栈内存),因为进程本身的8KB内存操作系统不能回收需要别人来辅助回收,因此我们每个进程都需要一个帮助它收尸的人,这个人就是这个进程的父进程。所以我们僵尸就是-----子进程先于父进程结束。子进程结束后父进程此时并不一定立即就能帮子进程“收尸”,在这一段(子进程已经结束且父进程尚未帮其收尸)子进程就被成为僵尸进程。下面我们来看演示:

代码语言:javascript
复制
 #include<stdio.h>
 #include<unistd.h>
 #include<stdlib.h>

 #define ERR_EXIT(m) \
     do \
     {  \
        perror(m); \
         exit(EXIT_FAILURE); \
    } while(0)

 int main()
 {
 pid_t  pid;

 if((pid = fork()) == -1)
    ERR_EXIT("fork");
else if (pid == 0)
 {
     printf("I am the kid,my pid : %d,my father's pid : %d!\n",getpid(),getppid());
 }
 else
{
      while(1)
     {
         printf("I am the father,my pid : %d!\n",getpid());
        sleep(2);
      }
   }

 return 0;
}

输出结果:

2、什么是孤儿进程?

子进程死亡需要父进程来处理,那么意味着正常的进程应该是子进程先于父进程死亡。当父进程先于子进程死亡时,子进程死亡时没父进程处理,这个死亡的子进程就是孤儿进程。同时,linux系统规定:所有的孤儿进程都自动成为一个特殊进程(进程1,也就是init进程)的子进程。下面我来演示一下:

代码语言:javascript
复制
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
pid_t p1 = -1;

p1 = fork();        // 返回2次

if (p1 == 0)
{
    // 这里一定是子进程

    // 先sleep一下让父进程先运行,先死
    sleep(1);

    printf("子进程, pid = %d.\n", getpid());       
    printf("hello world.\n");
    printf("子进程, 父进程ID = %d.\n", getppid());
}

if (p1 > 0)
{
    // 这里一定是父进程
    printf("父进程, pid = %d.\n", getpid());
    printf("父进程, p1 = %d.\n", p1);
}

if (p1 < 0)
{
    // 这里一定是fork出错了
}

// 在这里所做的操作
//printf("hello world, pid = %d.\n", getpid());

return 0;
  }

输出结果:

说明:这里父进程先运行死掉了,但是我们后面并没有发现特殊进程init为1,而是908,这其实是跟ubuntu系统有关系的,真实是为1的。

三、总结:

后面还会接着继续更新进程的文章,今天的学习加深了对进程的进一步理解,后面会写回收进程的具体实现方法

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 txp玩Linux 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档