Linux系统编程——进程

进程的概念

存储在存储器中的程序文件不是进程,只有当程序加载到内存中运行才能成为进程。进程是动态活动的实体。每个进程都有自己的虚拟内存空间。一个进程虚拟地址空间分布:

在这里插入图片描述

进程通过进程控制块**(PCB——Process Control Block)**管理,进程从生老到病死的一切信息进程控制块都有记录

进程是系统资源分配的基本单位。

进程标识

​ 同人有唯一的身份证号一样,进程也有ID,称为pid。每个进程还包含ppid——父进程号。每个进程都有父亲。除了init进程,它是所有进程祖先。

特殊的进程

  • PID为0的进程

    调度进程,内核的一部分,也被称为系统进程
    
  • PID为1的init进程

    所有用户进程的祖先,负责引导系统、启动守护进程(后台进程),以超级用户权限运行
    

进程的状态

  • 可执行态(TASK_RUNNING)

    只有处于该状态的进程才可能占有CPU运行。

  • 可中断睡眠态(TASK_INTERRUPTIBLE)

    进程因等待某个事件发生而处于睡眠态,被挂起一直等待的事件发生,事件发生进程就被唤醒。 可以被中断。

    扫描二维码关注公众号,回复: 11363349 查看本文章
  • 不可中断睡眠态(TASK_UNINTERRUPTIBLE)

​ 进程因等待某个事件发生而处于睡眠态,一旦等待的事件发生,进程就被唤醒。 不可以中断——不是不能响应外部中断,而是不能响应异步信号。

  • 暂停状态or跟踪状态(TASK_STOPPED、TASK_TRACED)

    进程收到SIGTOP就会进入TASK_STOPPED状态,除非该进程设置不响应该此信号。

    程序被跟踪处于TASK_TRACE状态,例如使用GDB调试程序在进程中设置了断点执行到断点处进程就会停下来。TASK_TRACED状态的进程不能响应SIGCONT被唤醒。

  • 进程死亡僵尸态(TASK_DEAD-EXIT_ZOMBIE)

    不管进程是正常退出(return或者调用exit())还是异常终止,内核都会将进程设置为死亡僵尸状态。

    进程占有所有资源,除了task_struct结构体。

    父进程可以通过wait()/waitpid()查看子进程的死亡信息以及将其状态设置为EXIT_DEAD状态。

    父进程先子进程死了,则由init进程负责收尸

    父进程没空给子进程收尸,可以使用信号异步通知父进程收尸。

  • 死亡退出态(TASK_DEAD-EXIT_DEAD)

    进程彻底死亡的状态,进程所有一切被彻底释放。

创建进程

通过fork()系列系统调用创建。

fork()/clone()/vfork()

fork()创建子进程

man fork 查看fork()函数用法

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

int main(int argc, char **argv)
{
	pid_t pid;
	
	printf("before fork\r\n");
	
	pid = fork();              //创建子进程,子进程从fork()后面的第一条语句开始执行,所以after fork会执行两次
	
	printf("after fork\r\n");   
	
	return 0;
}

根据pid区别父子进程

#include <unistd.h>
#include <sys/types.h>
#include "stdio.h"

int main(int argc, char **argv)
{
	pid_t pid;
	
	printf("before fork\r\n");
	
	pid = fork();
	
	if(pid == 0)          //子进程
	{
		while(1)
		{
			printf("pid : %d, I am child\r\n", getpid());
			printf("\r\n");
			sleep(1);
		}
	}
	else               //父进程                 
	{
		while(1)
		{
			printf("my child pid : %d\r\n", pid);
			printf("pid : %d, I am father\r\n", getpid());
			sleep(2);
		}	
	}
	
	return 0;
}

子进程中fork函数返回值为0,父进程中fork返回值为子进程的pid


一般让子进程做与父进程不同的事情

子进程去执行准备好的elf文件或者脚本,不然子进程和父进程做一样的事情没什么意义。

涉及的函数接口:

exce()函数族
man exce可以查看有哪些函数

在这里插入图片描述

#include <unistd.h>
#include <sys/types.h>
#include "stdio.h"


int main(int argc, char **argv)
{
	pid_t pid;
	
	printf("before fork\r\n");
	
	pid = fork();                             //创建子进程
	
	if(pid == 0)                             //子进程
	{
		execl("child_work", "child_work", NULL);  //执行准备好的elf程序,原来子进程和父进程一样的程序就会被覆盖
        printf("diu\r\n");               //exec函数族函数执行后不会返回,所以后面这条语句不会执行
	}
	else                                  //父进程
	{
		while(1)
		{
			printf("my child pid : %d\r\n", pid);
			printf("pid : %d, I am father\r\n", getpid());
			sleep(1);
		}
	}
    
	return 0;
}

child_work.c:

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

int main(int argc, char **argv)
{
    while(1)
	{
		printf("hello world\r\n");
		sleep(1);
	}

	return 0;
}

子进程是从父进程中复制过来,类似细胞分裂。

类似基因的遗传性,子进程以下属性在创建之初和父进程一样:

  • 实际UID和GID,以及有效UID和GID
  • 所有环境变量
  • 进程组ID和会话ID、
  • 当前工作路径,可用chdir()修改
  • 打开的文件
  • 信号响应函数
  • 内存空间(栈、堆、bss段、标准IO缓冲区等)

类似基因的多样性,子进程以下属性和父进程不同:

  • 进程号PID
  • 记录锁
  • 挂起的信号

父进程给子进程收尸相关函数(wait()/waitpid())

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_36413982/article/details/105926490