美文网首页
进程相关概念

进程相关概念

作者: 飞翃荷兰人 | 来源:发表于2020-04-12 19:23 被阅读0次

    一 进程线程的区别

    官方概念:进程是资源分配的基本单位,线程的cpu调度的基本单位。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。

    1. PCB控制块

    为了描述控制进程的运行,系统中存放进程的管理和控制信息的数据结构称为进程控制块,它是进程实体的一部分,是操作系统中最重要的记录性数据结构。它是进程管理和控制的最重要的数据结构,每一个进程均有一个PCB,在创建进程时,建立PCB,伴随进程运行的全过程,直到进程撤消而撤消。

    cpu对pcb的控制达到对进程的控制,pcb控制块中包含着进程的基本信息。linux下pcb数据结构:

    struct task_struct{
    
        unsigned short uid;
        int pid;
        int processor;
    
        volatile long state;
        long prority;
        unsighed long rt_prority;
        long counter;
        unsigned long flags;
        unsigned long policy;
        
        Struct task_struct *next_task, *prev_task;
        Struct task_struct *next_run,*prev_run;
        Struct task_struct *p_opptr,*p_pptr,*p_cptr,*pysptr,*p_ptr;
    };
    
    (1)unsigned short pid 为用户标识
    (2)int pid 为进程标识
    (3)int processor标识用户正在使用的CPU,以支持对称多处理机方式;
    (4)volatile long state 标识进程的状态,可为下列六种状态之一:
         可运行状态(TASK-RUNING);
         可中断阻塞状态(TASK-UBERRUPTIBLE)
         不可中断阻塞状态(TASK-UNINTERRUPTIBLE)
         僵死状态(TASK-ZOMBLE)
         暂停态(TASK_STOPPED)
         交换态(TASK_SWAPPING)
    (5)long prority表示进程的优先级
    (6)unsigned long rt_prority 表示实时进程的优先级,对于普通进程无效
    (7)long counter 为进程动态优先级计数器,用于进程轮转调度算法
    (8)unsigned long policy 表示进程调度策略,其值为下列三种情况之一:
         SCHED_OTHER(值为0)对应普通进程优先级轮转法(round robin)
         SCHED_FIFO(值为1)对应实时进程先来先服务算法;
         SCHED_RR(值为2)对应实时进程优先级轮转法
    (9)struct task_struct *next_task,*prev_task为进程PCB双向链表的前后项指针
    (10)struct task_struct *next_run,*prev_run为就绪队列双向链表的前后项指针
    (11)struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_ptr指明进程家族间的关系,分别为指向祖父进程、父进程、子进程以及新老进程的指针。
    

    2. 进程和线程的区别

    线程不能脱离进程而存在,一个进程至少有一个线程。进程是程序在某个数据集合上的一次运行活动;线程是进程中的一个执行路径。 角色方面:在支持线程机制的系统中,进程是系统资源分配的单位,线程是系统调度的单位。 资源共享方面:进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。 独立性方面:进程有自己独立的地址空间,而线程没有,线程必须依赖于进程而存在。对于操作系统来说,进程是一个比较重的概念,线程是轻量级的概念。

    3. 进程同步

    临界区

    在同步的程序设计中,临界区段(Critical section)指的是一个访问共享资源(例如:共享设备或是共享存储器)的程序片段,而这些共享资源有无法同时被多个线程访问的特性。
    当有线程进入临界区段时,其他线程或是进程必须等待(例如:bounded waiting 等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共享资源是被异或的使用,例如:semaphore
    总结:所谓的临界区就是每次只能有一个线程访问。

    同步

    处理竞争就是同步,安排进程执行的先后顺序就是同步,每个进程都有一定的个先后执行顺序。

    互斥

    互斥访问不可共享的临界资源,同时会引发两个新的控制问题(互斥可以说是特殊的同步)。

    信号量

    信号量(Semaphore)是一个整型变量,可以对其执行 down 和 up 操作,也就是常见的 P 和 V 操作。

    • down : 如果信号量大于 0 ,执行 -1 操作;如果信号量等于 0,进程睡眠,等待信号量大于 0;
    • up :对信号量执行 +1 操作,唤醒睡眠的进程让其完成 down 操作。
      down 和 up 操作需要被设计成原语,不可分割,通常的做法是在执行这些操作的时候屏蔽中断。
      如果信号量的取值只能为 0 或者 1,那么就成为了 互斥量(Mutex) ,0 表示临界区已经加锁,1 表示临界区解锁。

    4 进程间通信

    匿名管道(pipe)

    半双工,只能在有亲缘关系的进程之间进行通信,一般就是父子进程。
    典型的匿名管道代码

    #include <sys/wait.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    int main(int argc, char *argv[])
    {
        int pipefd[2];
        pid_t cpid;
        char buf;
    
        if (argc != 2) {
            fprintf(stderr, "Usage: %s <string>\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    
        if (pipe(pipefd) == -1) {
            perror("pipe");
            exit(EXIT_FAILURE);
        }
    
        cpid = fork();
        if (cpid == -1) {
            perror("fork");
            exit(EXIT_FAILURE);
        }
    
        if (cpid == 0) {    /* Child reads from pipe */
            close(pipefd[1]);          /* Close unused write end */
            while (read(pipefd[0], &buf, 1) > 0)
                write(STDOUT_FILENO, &buf, 1);
    
            write(STDOUT_FILENO, "\n", 1);
            close(pipefd[0]);
            _exit(EXIT_SUCCESS);
    
        } else {            /* Parent writes argv[1] to pipe */
            close(pipefd[0]);          /* Close unused read end */
            write(pipefd[1], argv[1], strlen(argv[1]));
            close(pipefd[1]);          /* Reader will see EOF */
            wait(NULL);                /* Wait for child */
            exit(EXIT_SUCCESS);
        }
    }
    

    父进程创建管道,得到两个⽂件描述符,进程执行fork,⼦进程也有两个⽂件描述符指向同⼀管道。
    父进程关闭fd[0],子进程关闭fd[1],即⽗进程关闭管道读端,⼦进程关闭管道写端(因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信。

    具名管道

    与传统的无名的shell管道不同,命名管道利用了文件系统。使用mkfifo()mknod()创建命名管道。两个进程可以通过管道的名字打开、读写管道。

    消息队列

    消息队列本身是异步的,它允许接收者在消息发送很长时间后再取回消息,这和大多数通信协议是不同的。例如中使用的HTTP协议是同步的,因为客户端在发出请求后必须等待服务器回应。然而,很多情况下我们需要异步的通信协议。比如,一个进程通知另一个进程发生了一个事件,但不需要等待回应。但消息队列的异步特点,也造成了一个缺点,就是接收者必须轮询消息队列,才能收到最近的消息。
    重点:消息队列是一个异步的通信过程,接受者一般需要轮训监听。常用的rocketMQ就是一种消息队列机制。

    共享内存

    多个进程通过将同一块屋里内存绑定到自己的虚拟地址空间,实现共享内存。

    进程通过调用shmget(Shared Memory GET,获取共享内存)来分配一个共享内存块。
    该函数的第一个参数是一个用来标识共享内存块的键值。彼此无关的进程可以通过指定同一个键以获取对同一个共享内存块的访问。不幸的是,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突。用特殊常量IPC_PRIVATE作为键值可以保证系统建立一个全新的共享内存块。该函数的第二个参数指定了所申请的内存块的大小。因为这些内存块是以页面为单位进行分配的,实际分配的内存块大小将被扩大到页面大小的整数倍。第三个参数是一组标志,通过特定常量的按位或操作来shmget。

    #include <sys/types.h>
    #include <sys/shm.h>
    
    void *shmat(int shmid, const void *shmaddr, int shmflg);
    
    套接字

    这个用的太多了,所有的网络编程都是基于套接字的

    5 孤儿进程与僵尸进程

    孤儿进程

    孤儿进程指的是父进程执行完或终止后仍然存在的进程。unix从机制上尽量避免产生孤儿进程:

    • 为避免孤儿进程退出时无法释放所占用的资源而僵死,任何孤儿进程产生时都会立即为系统进程initsystemd自动接收为子进程,这一过程也被称为“收养”。
    • 因为父进程终止或崩溃都会导致对应子进程成为孤儿进程,所以也无法预料一个子进程执行期间是否会被“遗弃”。有鉴于此,多数类UNIX系统都引入了进程组以防止产生孤儿进程:在父进程终止后,用户的Shell会将父进程所在进程组标为“孤儿进程组”,并向终止的进程下属所有子进程发出SIGHUP信号,以试图结束其运行,如此避免子进程继续以“孤儿进程”的身份运行[2]
    僵尸进程

    僵尸进程是指完成执行系统调用,或运行时发生致命错误或收到终止信号但在操作系统的进程表中仍然有一个表项(进程控制块PCB),处于"终止状态"的进程。这发生于子进程需要保留表项以允许其父进程读取子进程的exit status:一旦退出态通过waitwaitpid读取,僵尸进程条目就从进程表中删除,称之为"回收(reaped)"。正常情况下,进程直接被其父进程waitwaitpid并由系统回收。进程长时间保持僵尸状态一般是错误的并导致资源泄漏
    注:waitwaitpid的一个特例。

    相关文章

      网友评论

          本文标题:进程相关概念

          本文链接:https://www.haomeiwen.com/subject/imkbmhtx.html