今天起,开始学习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. [LeetCode] 304. Range Sum Query 2D - Immutable 二维区域和检索 - 不可变

    Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...

  2. Java之Java程序的基本结构

    Java的基本结构是 /** * 可以用来自动创建文档的注释 */ public class Hello { public static void main(String[] args) { // 向 ...

  3. Apache JMeter 做接口并发测试

    获知来源:查找如何使用Postman进行接口并发测试时,在StackOverflow上看到,说postman只能做串行测试,而且postman并不是被设计做这种测试的:而jmeter就是为了测试而开发 ...

  4. 多生产者多消费者(第二种方式2.1)基于BlockingQueue

    public class Producer implements Runnable { //静态变量只初始化一次 private static AtomicInteger count = new At ...

  5. Django框架深入了解_04(DRF之url控制、解析器、响应器、版本控制、分页)

    一.url控制 基本路由写法:最常用 from django.conf.urls import url from django.contrib import admin from app01 impo ...

  6. PAT(B) 1065 单身狗(Java:17分,C:25分)

    题目链接:1065 单身狗 (25 point(s)) 题目描述 "单身狗"是中文对于单身人士的一种爱称.本题请你从上万人的大型派对中找出落单的客人,以便给予特殊关爱. 输入格式 ...

  7. 路由Routers

    路由Routers 对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息. REST framework提供 ...

  8. JAVA MyBybatis分页

    java: import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; impo ...

  9. CCF 2016-04-2 俄罗斯方块

    CCF 2016-04-2 俄罗斯方块 题目 问题描述 俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游戏. 游戏在一个15行10列的方格图上进行,方格图上的每一个格子可能已经放置了方块,或者 ...

  10. Java Service Wrapper将jar包安装成Windows服务

    刚接触java,第一次使用Java开发windows服务,也是刚不久看了SSM框架 简直也是一头雾水,不过只要用心理解,其实很简单,下面有详细的步骤,包学包会 在windows上运行jar包,需要在工 ...