Linux系统编程---进程(一)

本文详细介绍了进程的基本概念,包括程序与进程的区别,进程的状态模型,以及进程控制中的fork函数。讨论了父子进程的共享内容和gdb调试的跟踪模式。
摘要由CSDN通过智能技术生成

一、进程的相关概念

进程和程序:

我们平时写好的代码,通过编译后生成的可执行文件就是一个程序,不占用系统资源(cpu、内存、打开的文件、设备、锁....),当这个程序被运行起来后(没结束之前) 它就成为了一个进程,进程是活跃的程序,占用系统资源,在内存中执行

程序:静态的,即死的。只占用磁盘空间。 ——剧本

进程:动态的,即活的。运行起来的程序。占用内存、cpu等系统资源。 ——戏

进程的状态:

在三态模型中,进程状态分为三个基本状态,即:运行态、就绪态、阻塞态

在五态模型中,进程状态分为五个基本状态,即:初始态、就绪态、运行态、挂起态、终止态

其中初始态为进程准备阶段,常与就绪态结合来看。

PCB进程控制块: 

进程id

用户id和组id

进程状态: 初始态、就绪态、运行态、挂起态、终止态。

进程工作目录位置

文件描述符表

信号相关信息资源

ps aux 返回结果里,第二列是进程id 

环境变量:

echo $PATH 查看环境变量

path环境变量里记录了一系列的值,当运行一个可执行文件时,系统会去环境变量记录的位置里查找这个文件并执行。

echo $TERM 查看终端

echo $LANG 查看语言

env 查看所有环境变量

二、进程控制

1. fork创建子进程

系统允许一个进程创建新进程,新进程即为子进程,子进程还可以创建新的子进程,形成进程树结构模型。

fork函数原理:

fork之前的代码,父子进程都有,但是只有父进程执行了,子进程没有执行,fork之后的代码,父子进程都有机会执行。

pid_t fork(void);

功能:

        用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。子进程是父进程的一个几乎完全独立的副本,包括内存空间、文件描述符、环境变量等,子进程获得的是这些资源的复制品,所以原进程和新进程的资源是相互独立的

参数:

        无

返回值:

        成功:子进程中返回0,父进程中返回子进程ID(PID)

        失败:返回-1
失败原因:
        通常是因为系统级别错误(如可用进程数量达到限制)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc,char *argv[])
{
        printf("before fork-1-\n");
        printf("before fork-2-\n");
        printf("before fork-3-\n");
        printf("before fork-4-\n");
        pid_t pid = fork();
        if (pid == -1)
        {
                perror("fork error");
                exit(1);
        }else if(pid == 0){
                printf("---child is created\n");
        }else if(pid > 0){
                printf("---parent process:my child is %d\n",pid);
        }

        printf("============end of file\n");

        return 0;
}

输出如下:

before fork-1-
before fork-2-
before fork-3-
before fork-4-
---parent process:my child is 2332517
============end of file
---child is created
============end of file

循环创建多个子进程:

        通过命令行参数指定创建进程的个数,每个进程休眠1S打印自己是第几个被创建的进程。如:第1个子进程休眠0秒打印:“我是第1个子进程”;第2个进程休眠1秒打印:“我是第2个子进程”;第3个进程休眠2秒打印:“我是第3个子进程”。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc,char *argv[])
{
        int i;
        pid_t pid;

        for (i = 0;i < 5;i++){  
            if (fork() == 0)   //循环期间,子进程不fork
                break;
        }

        if (5 == i){    
            sleep(5);
            printf("I'm parent\n");
        }else{        
            sleep(i);
            printf("I'm %dth child\n",i+1);
        }

        return 0;
}

输出如下:

I'm 1th child
I'm 2th child
I'm 3th child
I'm 4th child
I'm 5th child
I'm parent

2. getpid和getppid 

每个进程都由一个进程号来标识,其类型为pid,进程号的范围:0~32767,进程号总是唯一的,但进程号可以重用,当一个进程终止后其进程号就可以再次使用;

进程号(PID):标识进程的一个非负整形数

父进程号(PPID):任何进程都是由另一个进程创建,该进程称为被创建进程的父进程,对应的进程号称为父进程号(PPID),如 A进程创建了B进程,A的进程号就是B进程的父进程号

进程组号(PGID):进程组是一个或多个进程的集合,它们之间相互关联,进程组可以接收同一终端的各种信号,关联的进程有一个进程组号(PGID),默认情况下 当前的进程号会当作当前的进程组号

pid_t getpid(void);

功能:
        获取当前进程号(PID)

参数:
        无

返回值:
        本进程号

pid_t getppid(void);        获取当前进程的父进程ID

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc,char *argv[])
{
        printf("before fork-1-\n");//在fork之前打印,父进程执行,只执行一次
        printf("before fork-2-\n");
        printf("before fork-3-\n");
        printf("before fork-4-\n");

        pid_t pid = fork();//创建子进程
        if (pid == -1)
        {
            perror("fork error");
            exit(1);
        }else if(pid == 0){     //子进程
            printf("---child is created,pid=%d,parent-pid:%d\n",getpid(),getppid());
        }else if(pid > 0){      //父进程
            printf("---parent process:my child is %d,my pid:%d,my parent pid:%d\n",pid,getpid(),getppid());
        }

        printf("============end of file\n");//父子进程各执行一次

        return 0;
}

 输出如下:

before fork-1-
before fork-2-
before fork-3-
before fork-4-
---parent process:my child is 2328447,my pid:2328446,my parent pid:2272084
============end of file
---child is created,pid=2328447,parent-pid:2328446
============end of file

父进程先结束,导致子进程成孤儿,于是回收到孤儿院,看起来合情合理。

修改一下代码,给父进程增加一个等待命令,这样能保证子进程完成时,父进程处于执行状态,子进程就不会成孤儿。同时,这里也解决了终端提示符和输出混在一起的问题。代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc,char *argv[])
{
        printf("before fork-1-\n");//在fork之前打印,父进程执行,只执行一次
        printf("before fork-2-\n");
        printf("before fork-3-\n");
        printf("before fork-4-\n");

        pid_t pid = fork();//创建子进程
        if (pid == -1)
        {
            perror("fork error");
            exit(1);
        }else if(pid == 0){     //子进程
            printf("---child is created,pid=%d,parent-pid:%d\n",getpid(),getppid());
        }else if(pid > 0){      //父进程
            sleep(1);
            printf("---parent process:my child is %d,my pid:%d,my parent pid:%d\n",pid,getpid(),getppid());
        }

        printf("============end of file\n");//父子进程各执行一次

        return 0;
}

输出如下:

before fork-1-
before fork-2-
before fork-3-
before fork-4-
---child is created,pid=2338015,parent-pid:2338014
============end of file
---parent process:my child is 2338015,my pid:2338014,my parent pid:2272084
============end of file

 三、进程共享

父子进程之间在fork后共享哪些内容:

父子进程相同:

全局变量、data段、text段、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式

父子进程不同:

进程ID、fork返回值、父进程ID、进程运行时间、闹钟(定时器)、未决信号集

父子进程共享:

读时共享、写时复制  ----------------- 全局变量

1.文件描述符(打开文件的结构体) 2. mmap映射区(进程间通信)

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int var = 100;

int main(int argc,char* argv[])
{
        pid_t pid;

        pid = fork();

        if (pid == -1)
        {
                perror("fork error");
                exit(1);
        }else if (pid > 0){
                var = 288;
                printf("parent, var = %d\n",var);
                printf("I'm parent pid = %d,getpid = %d\n",getpid(),getppid());
        }else if(pid == 0){
                var = 200;
                printf("I'm child pid = %d,ppid = %d\n",getpid(),getppid());
                printf("child,var = %d\n",var);
        }

        printf("------------finish-------------\n");

        return 0;
}

 输出如下:写时复制

parent, var = 288
I'm parent pid = 2348490,getpid = 2272084
------------finish-------------
I'm child pid = 2348491,ppid = 2348490
child,var = 200
------------finish-------------

将var = 200进行注释,输出如下: 读时共享

parent, var = 288
I'm parent pid = 2350816,getpid = 2272084
------------finish-------------
I'm child pid = 2350817,ppid = 2350816
child,var = 100
------------finish-------------

四、gdb调试 

使用gdb调试的时候,gdb只能跟踪一个进程。可以在fork函数调用之前,通过指令设置gdb调试工具跟踪父进程或者是跟踪子进程。默认跟踪父进程。

设置父进程调试路径:set follow-fork-mode parent (默认)

设置子进程调试路径:set follow-fork-mode child

注意:一定要在fork函数调用之前设置才有效   

  • 39
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值