今天起,开始学习linux系统编程中的另一个新的知识点----进程,在学习进程之前,有很多关于进程的概念需要了解,但是,概念是很枯燥的,也是让人很容易迷糊的,所以,先抛开这些抽象的概念,以实际编码来熟悉进程到底是个什么东东,这样学习起来要有兴趣一些,在学习一门技术时,找一种能提高自己兴趣的方法是很重要的,这也是我自己学习的一个比较"典型"的学习流程吧,关于不太清楚的概念会在实验中一一阐述(当然不可能理解得很透,但是没关系,先把实践做出来,随时间的推移再慢慢摸透它),理论当然也是非常非常重要的,等到进程学得差不多了,我会再回过头来去系统地分析理论,实践支配理论,这是非常重要的学习方法,好了,话不多说,进入正题!

复制进程映像:
 
说明:利用fork复制的新进程,并未全部都复制了父进程的东东,这个在结尾时还会说明,先有个了解】
关于fork进程,可以用下面这种的通俗方式来理解:
首先我们先来理解一个概念,这个之后在最后还会进行总结的:
其中PCB是Process Control Block(进程控制块),对于这些理论,先有个大至的了解,之后会进程中所有的概念进行一个总结的。
理解上面的东东之后,fork新创建的进程,会复制父进程的所有信息(代码段+数据段+堆栈段+PCB),但是“所有”并非绝对,还是有少部分信息是不一样的,另外还可以理解,每一个进程都有自己独立的4GB的地址空间(对于32位系统来说)
 
子进程与父进程的区别在于:
1、父进程设置的锁,子进程不继承
 对于排它锁(关于什么是排它锁可以参见博文:http://www.cnblogs.com/webor2006/p/3500354.html),如果说子进程会共享父进程的锁的话,那就有矛盾了。
2、各自的进程ID和父进程ID不同
3、子进程的未决告警被清除【了解】
4、子进程的未决信号集设置为空集【了解】
 
fork系统调用:
 

说明:关于 fork函数,它有一个特征:调用一次,返回两次,就如上面返回值的说明】 

如上图所说:fork()出来的子进程成功,对于子进程来说则返回0;而对于父进程来说返回子进程ID,这是有原因的,对于进程中的PCB保存了pid和ppid,所以对于子进程来说,有办法知道pid和ppid,而对于父进程来说,如果不返回子进程的id,则就无法知道新创建的进程的号码,因为PCB中并不会保存子进程的ID列表,这样就会让PCB膨胀,所以这也是有原因的。
有了上面的理论之后,下面就以实际代码来进行说明:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int main(int argc, char *argv[])
{
printf("before fork pid = %d\n", getpid());//打印当前进程ID,也就是原始父进程
pid_t pid;
pid = fork();//产生一个新的进程,注意:它里会有两个进程执行
if (pid == -)
ERR_EXIT("fork error"); if (pid > )
{//证明是父进程
printf("this is parent pid=%d childpid=%d\n", getpid(), pid);//getpid()为当前父进程的ID,而pid则为新创建的进程id,也就是子进程
}
else if (pid == )
{//证明是子进程
printf("this is child pid=%d parentpid=%d\n", getpid(), getppid());//getpid()为当前子进程的ID,getppid()为当前子进程所属父进程的ID
}
return ;
}

编译运行:

原因是由于当执行父进程之后,它就退出了,这时子进程执行时,这时子进程的父进程就变为init进程,所以就变成了1,如果我们让父进程输出延时一下,保证子进程执行时父进程没退出,就如我们的预期了:

这次再看效果:

对于fork函数,可能有一点比较难以理解,为啥它一次调用会有两次返回呢?这里再来用文字来解释一下:fork成功意味着创建了一个进程副本,意味着也就有两个进程了,两个进程都要执行各自相应的动作,所以两个进程都得要返回,实际上在内核中,是在各自的进程地址空间中返回的:
fork 系统调用注意点:
关于上面的第二个注意点,,如果父进程退出了,子进程还没有退出,我们将子进程称为孤儿进程,这时会将子进程托孤给init进程。
关于第三点,其中提到了“僵尸进程”,用程序来看下现象:
运行:
这时,查看一下当前的进程状态:
 
僵尸状态,我们尽量得避免它,避免它的方法之一,可以采用一个系统调用----signal(信号,关于它,之后会详述,这里只是先了解一下):
 
这时,编译运行,再看效果:
 
 
下面再来理解一来系统是如何实现fork()的:
写时复制copy on write:
 

实际系统实现时,并未真正把所有的数据(代码段+数据段+堆栈段+PCB)都复制一份,只是为了方便理解,我们可以认为是数据(代码段+数据段+堆栈段+PCB)都复制了一份,实际上代码段是只读的,是可以被共享的,每个进程只要保存一个指向这个资源的指针既可,这可以加快进程的创建速度,大大提高了效率
 
而对于需要修改的进程才会复制那份资源,对于linux而言,它是基于页的的方式进行复制的,并没将所有数据都进行复制,只是复制需要页,其它页是不会复制的,所以我们得正确理解“每个进程有自己独立的4GB(对于32位系统来说)的地址空间”,实际上不被修改的数据是共享的,对于这个理论,大致了解下,也是为了加深对fork()函数的理解。
好了,今天就学到这,下回见!

linux系统编程之进程(一)的更多相关文章

  1. Linux系统编程——Daemon进程

    目录 Daemon进程介绍 前提知识 Daemon进程的编程规则 Daemon进程介绍 Daemon运行在后台也称作"后台服务进程". 它是没有控制终端与之相连的进程.它独立与控制 ...

  2. linux系统编程之进程(六):父进程查询子进程的退出,wait,waitpid

    本节目标: 僵进程 SIGCHLD wait waitpid 一,僵尸进程 当一个子进程先于父进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行,或者父进程调用了wait才告终止. ...

  3. linux系统编程之进程(五)

    今天继续学习系统编程,学习的主题还是进程,今天主要讨论的是守护进程相关的概念,开始进入正题: 什么是守护进程:       守护进程的创建步骤: 在描述它之前,首先得先了解两个概念:进程组.会话期: ...

  4. linux系统编程之进程(二):进程生命周期与PCB(进程控制块)

    本节目标: 进程状态变迁 进程控制块 进程创建 进程撤消 终止进程的五种方法 一,进程状态变迁 进程的三种基本状态 就绪(Ready)状态 当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便 ...

  5. linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

  6. linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程

    本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生)      使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...

  7. linux系统编程--守护进程,会话,进程组,终端

    终端: 在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal), 进程中,控制终端是保存在PCB中的信息,而f ...

  8. Linux系统编程之进程概念

    注:本文部分图片来源于网络,如有侵权,请告知删除 1. 什么是进程? 在了解进程概念之前,我们需要先知道程序的概念. 程序,是指编译好的二进制文件,这些文件在磁盘上,并不占用系统资源. 进程,指的是一 ...

  9. linux系统编程之进程(七):system()函数使用

    一,system()理解 功能:system()函数调用"/bin/sh -c command"执行特定的命令,阻塞当前进程直到command命令执行完毕 原型: int syst ...

随机推荐

  1. 应用程序正常初始化(0xc0000135)失败。请单击“确定”,终止应用程序。

    应用程序正常初始化(0xc0000135)失败.请单击“确定”,终止应用程序. 没有安装对应版本的.NET FRAMEWORK.

  2. VS 2015main函数带参数的调试

    最近学习pcl,学习C++,今天让main的参数接收数据,想起没用过这样的,不知道怎么在vs里面调试 因此找了下方法,并记录下来 代码 #include<iostream> int mai ...

  3. (转)面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题

    背景:redis问题在面试过程中经常被问到,对于常见问题一定不能放过. 面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题 一.缓存雪崩 1.1什么是缓存雪崩? 如果缓存数据设置的过 ...

  4. html5+springboot+websocket的简单实现

    环境 window7,IntelliJ IDEA 2019.2 x64 背景:利用IntelliJ来搭建springboot框架,之后来实现websocket的功能.websocket只是实现了画面上 ...

  5. linux虚拟机IP发生变化之后上面Oracle数据库的处理

    1. 首先说明一下 centos 和 rhel 的关系 redhat是最大的开源软件公司(现在已经被IBM收购) 作为开源最大的受益者,  redhat 自己的 rhel(redhat enterpr ...

  6. python学习-69 包装和授权

    包装 1.二次加工标准类型(包装) class List(list): def append(self, a_objcet): if type(a_objcet) is str: super().ap ...

  7. css之多行居中

    需求: 单行到多行文字居中. <div> <p>应该为数组中的每个子代分配一个唯一的键.表格dataSource和中的值columns应遵循此规则.默认情况下</p> ...

  8. git比较本地仓库和远程仓库的差异(转)

    转自:https://www.jianshu.com/p/6078a49900a4

  9. golang ---调用window api函数执行程序

    package main import "syscall" import "unsafe" func main() { var hand uintptr = u ...

  10. gensim快速上手教程

    1 gensim是什么?        gensim是一个Python常用的的自然语言处理开发包, 主要用于词向量训练和加载词向量,以下解释其正确使用姿势. 2 正确使用姿势 from gensim. ...