精华内容
下载资源
问答
  • 为使生产者进程与消费者进程能并发执行,两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将其所生产的产品放入一个缓冲区中;消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程和消费者进程...

    1.生产者-消费者问题

       有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将其所生产的产品放入一个缓冲区中;消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程和消费者进程都是以异步方式运行的,但它们之间必须保持同步,既不允许消费者进程到一个空缓冲区去取产品,也不允许生产者进程向一个已装满产品且尚未被取走的缓冲区投放产品。

        1.利用记录型信号量解决生产者-消费者问题

           这时可以 利用互斥信号量mutex实现对诸进程的缓冲互斥作用,利用信号量emptyfull分别表示缓冲池中的空缓冲区和满缓冲区的数量

    代码:

    int in = 0, out = 0;//in: 输入指针, out: 输出指针;
    item buffer[n];//n个缓冲区组成的数组;
    semaphore mutex = 1, full = 0, empty = n;
    //mutex: 互斥信号量, 生产者进程和消费者进程都只能互斥访问缓冲区;
    //full: 资源信号量, 满缓冲区的数量;
    //empty: 资源信号量, 空缓冲区的数量;//信号量不允许直接参与运算, 故都要定义;
    
    //生产者程序;
    void Producer() {
      do {
          生产者生产一个产品nextp;
          P(empty);//申请一个空缓冲区;
          P(mutex);//申请临界资源;
          buffer[in] = nextp;//将产品添加到缓冲区;
          in = (in + 1) % n;//类似于循环队列;
          V(mutex);//释放临界资源;
          V(full);//释放一个满缓冲区;
      } while (TRUE);
    }
    
    //消费者程序;
    void Producer() {
      do {
          P(full);//申请一个满缓冲区;
          P(mutex);//申请临界资源;
          nextc = buffer[out];//将产品从缓冲区取出;
          out = (out + 1) % n;//类似于循环队列;
          V(mutex);//释放临界资源;
          V(empty);//释放一个空缓冲区;
          消费者将一个产品nextc消费; 
     } while (TRUE);
    }
    

     需要注意的是:

    应先执行对资源信号量的申请,然后再对互斥信号量进行申请操作,否则会因起死锁。

    汪先生来仔细分析一下造成死锁的原因,首先缓冲区有n个,每个都有满(full)和和空(empty)的两种状态,l临界区只有一个,控制权为murex,值为1,如果生产者首先拿到的是缓冲区的控制权,那么其他的生产者和消费者就拿不到这个控制权了对不对?对,接着这个生产者去申请一个空的缓冲区,如果此时这些个缓冲区刚好是满的,那么这个申请必然失败对不对,对,那么消费者还能消费吗?他拿不到临界区的控制权,无法消费,也就没有空的缓冲区腾出来,生产者申请空缓冲区也必然不成功,此时就是一个死锁的状态了。

    2.利用AND型信号量解决生产者消费者问题

    int in = 0, out = 0;//in: 输入指针, out: 输出指针;
    item buffer[n];//n个缓冲区组成的数组;
    semaphore mutex = 1, full = 0, empty = n;
    //mutex: 互斥信号量, 生产者进程和消费者进程都只能互斥访问缓冲区;
    //full: 资源信号量, 满缓冲区的数量;
    //empty: 资源信号量, 空缓冲区的数量;//信号量不允许直接参与运算, 故都要定义;
    
    //生产者程序;
    void Producer() {
      do {
          Swait(empty,mutex);
          buffer[in] = nextp;//将产品添加到缓冲区;
          in = (in + 1) % n;//类似于循环队列;
          Ssignal(mutex,full);
      } while (TRUE);
    }
    
    //消费者程序;
    void Producer() {
      do {
          Swait(full,mutex);
          nextc = buffer[out];//将产品从缓冲区取出;
          out = (out + 1) % n;//类似于循环队列;
          Ssignal(mutex,empty);
          消费者将一个产品nextc消费; 
     } while (TRUE);
    }
    

    2.哲学家进餐问题

    一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭,如图2-10所示。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿的时候,才试图拿起左、 右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。

                                                            

    1.记录型信号量解决哲学家问题

    分析:筷子就是临界资源,在一段时间内只允许一位哲学家使用,为了让实现筷子的互斥使用,可以一个筷子用一个信号量表示

    semaphore chopstick[5]={1,1,1,1,1};

    第i位哲学家的活动可以描述为:

    do{
       wait(chopstick[i]);
       wait(chopstick[(i+1)%5]);
       //eat
       signal(chopstick[i]);
       signal(chopstick[(i+1)%5]);
       //think
    }while(TRUE);

     分析,哲学家饿了会去先拿起它左边的筷子,然后拿起它右边的筷子,然后进餐,虽然可以保障不会有两个哲学家同时进餐,但是却有可能引起死锁。比如五个哲学家同时饥饿,同时拿起了左边的筷子,则大家都进入无限期的等待。

    如何解决这个问题呢?这里说一种办法,其余可以参考博客:https://blog.csdn.net/thelostlamb/article/details/80741319

    就是使用AND信号量机制,意思就是如果想给某个哲学家筷子,就将他需要的所有资源都给他,然后让他进餐,否则就一个都不给他。

    2.利用AND信号量机制解决哲学家就餐问题

    semaphore chopstick[5]={1,1,1,1,1};
    do{
       //think;
       Swait(chopstick[(i+1)%5],chopstick[i]);
       //eat
       Ssignal(chopstick[(i+1)%5],chopstick[i]);
       //think
    }while(TRUE);

    3.读-写

        一个数据文件或记录文件可被多个进程共享,我们把只要求读该文件的进程称为“Reader进程”,其他进程则称为“Writer进程”。允许多个进程同时读一个共享对象,但是要读写互斥,或写写互斥。

     1.利用记录型信号量解决读-写问题

       为实现读写互斥,需要设置一个互斥信号量wmutex,设一个整型变量readcount表示正在读的进程数目。仅当Readcount=0,表示尚无Reader进程在读时,Reader进程才需要执行Wait(Wmutex)操作;仅当Reader进程执行了Readcount-1操作后其值为0时,才进行signal(Wmutex)操作。因为readcount是一个可以被多个进程访问的临界资源,所以还需为他设置一个互斥信号量rmutex..

    semaphore rmutex=1,wmutex=1;
        int readcount:=0;
        void reader(){
          do{
               wait(rmutex); 
               if readcount=0 then wait(wmutex);
               readcount++;
               signal(rmutex);
               ...
               perform read operation;
               wait(rmutex);
               readcount--;
               if readcount=0 then signal(wmutex);
               signal(rmutex);
           }
        }
       
        void Writer(){
          do{
               wait(wmutex);
               perform read operation;
               signal(wmutex);
          }
       }
    
                              

     汪先生来分析一下这段代码,首先,这个读的进程是不需要拿到什么控制权的,因为多个进程读是不会产生安全问题的,但是要读写互斥就要顺带拿到写的控制权,并且要保证只要有一个进程在读,写进程就不可能拿到写的控制权。因为要读写互斥。然后会有很多的读进程来操作,都是没有问题的,当这些读的进程读完一个个退出的时候,最后退出的那个读进程必须要释放写的控制权。然后每个写的进程就必须要先拿到写临界区的控制权,才能写,写完再释放控制权。

    4.最后一个例子

        父亲向一个盘子中放一个苹果或桔子,儿子取桔子,女儿取苹果,盘子只能放一个水果

       

    semaphore S=1,Sorange=1,Sapple=1;
       void Father{
            do{
               wait(S);//申请空盘子
               //放苹果
               if(放的是苹果){
                 Signal(Sorange);
               }
               //放的是桔子
               if(放的是桔子){
                  Signal(Sapple);
               }
            }
       }
    
       son(){
        while(1){
         P(So);
         从盘中取出桔子;
         V(S);
         吃桔子;
        }
    }
    
       daughter(){
        while(1){
          P(Sa);
          从盘中取出苹果;
          V(S);
          吃苹果;
        }
    }
       

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • Nginx学习——进程模型(master 进程)

    千次阅读 2014-07-27 09:36:21
    进程模型  Nginx分为Single和Master...Master模型即为一个master进程+N个worker进程的工作方式。生产环境都是用master-worker模型来工作。 master进程  我们知道main函数中完成了Nginx启动初始化过程,启动初始

    进程模型

            Nginx分为Single和Master两种进程模型,Single模型即为单进程方式工作,具有较差的容错能力,不适合生产之用。Master模型即为一个master进程+N个worker进程的工作方式。生产环境都是用master-worker模型来工作。


    master进程

            我们知道在main函数中完成了Nginx启动初始化过程,启动初始化过程中的一个重要环节就是解析配置文件,回调各个配置指令的回调函数,因此完成了各个模块的配置及相互关联。在所有的这些重要及不重要的初始化完成后,main函数就开始为我们打开进程的“大门”——调用ngx_master_process_cycle(cycle); 在剖析ngx_master_process_cycle(cycle)之前,我们先来看看该函数中用到的7个标志位,如下面的表所示:


    进程中接收到的信号对Nginx框架的意义:

    信号

    对应进程中的全局标志位变量

    意义

    QUIT

    ngx_quit

    优雅地关闭整个服务

    TERM或INT

    ngx_terminate

    强制关闭整个服务

    USR1

    ngx_reopen

    重新打开服务中的所有文件

    WINCH

    ngx_noaccept

    所有子进程不再接受处理新的连接,实际相当于对所有子进程发送QUIT信号

    USR2

    ngx_change_binary

    平滑升级到新版本的Nginx程序

    HUP

    ng_reconfigure

    重读配置文件并使服务对新配置项生效

    CHLD

    ngx_reap

    有子进程意外结束,这时需要监控所有子进程,也就是ngx_reap_children方法所做的工作

     

            还有一个标志位会用到:ngx_restart,它仅仅是在master工作流程中作为标志位使用,与信号无关。master进程就是根据这8个标志来决定工作流程的,流程图如下:

     

            以下是对ngx_master_process_cycle()函数源码的剖析,可参照流程图理解:

    void
    ngx_master_process_cycle(ngx_cycle_t *cycle)
    {
        char              *title;
        u_char            *p;
        size_t             size;
        ngx_int_t          i;
        ngx_uint_t         n, sigio;
        sigset_t           set;
        struct itimerval   itv;
        ngx_uint_t         live;
        ngx_msec_t         delay;
        ngx_listening_t   *ls;
        ngx_core_conf_t   *ccf;
    	/*屏蔽一些列信号*/
        sigemptyset(&set);
        sigaddset(&set, SIGCHLD);
        sigaddset(&set, SIGALRM);
        sigaddset(&set, SIGIO);
        sigaddset(&set, SIGINT);
        sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
    
        if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "sigprocmask() failed");
        }
    
        sigemptyset(&set);
    
    	
        size = sizeof(master_process);
    	
        for (i = 0; i < ngx_argc; i++) {
            size += ngx_strlen(ngx_argv[i]) + 1;
        }
    	
        title = ngx_pnalloc(cycle->pool, size);
    
        p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
        for (i = 0; i < ngx_argc; i++) {
            *p++ = ' ';
            p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);
        }
    	
        ngx_setproctitle(title);
    
    	
        ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
    	/*创建子进程,接受请求,完成响应*/
        ngx_start_worker_processes(cycle, ccf->worker_processes,
                                   NGX_PROCESS_RESPAWN);
    	/*创建有关cache的子进程*/
        ngx_start_cache_manager_processes(cycle, 0);
    
        ngx_new_binary = 0;
        delay = 0;
        sigio = 0;
        live = 1;
    
        for ( ;; ) {
            if (delay) {
                if (ngx_sigalrm) {
                    sigio = 0;
                    delay *= 2;
                    ngx_sigalrm = 0;
                }
    
                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                               "termination cycle: %d", delay);
    
                itv.it_interval.tv_sec = 0;
                itv.it_interval.tv_usec = 0;
                itv.it_value.tv_sec = delay / 1000;
                itv.it_value.tv_usec = (delay % 1000 ) * 1000;
    			//设置定时器
                if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                                  "setitimer() failed");
                }
            }
    
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");
    		
    		/*master进程挂起,等待信号的产生*/
            sigsuspend(&set);
    
            ngx_time_update();
    
            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "wake up, sigio %i", sigio);
    		/*ngx_reap若为1,则表示要监控所有子进程*/
            if (ngx_reap) {
                ngx_reap = 0;
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
    			/*ngx_reap_children(会遍历ngx_process数组,检查每个子进程的状态,对于非正常退出的子进程会重新拉起,
    			最后,返回一个live标志位,如果所有的子进程都已经正常退出,则live为0,初次之外live为1。*/
                live = ngx_reap_children(cycle);
            }
    		/*当live标志为0,并且ngx_terminate或ngx_quit中的一个标志位1时,都将调用ngx_master_process_exit方法退出master进程*/
            if (!live && (ngx_terminate || ngx_quit)) {
                ngx_master_process_exit(cycle);
            }
    		/*ngx_terminate标志为1,则向所有子进程发送信号TERM,通知子进程强制退出进程*/
            if (ngx_terminate) {
                if (delay == 0) {
                    delay = 50;
                }
    
                if (sigio) {
                    sigio--;
                    continue;
                }
    
                sigio = ccf->worker_processes + 2 /* cache processes */;
    			//强制退出进程
                if (delay > 1000) {
                    ngx_signal_worker_processes(cycle, SIGKILL);
                } else {
                    ngx_signal_worker_processes(cycle,
                                           ngx_signal_value(NGX_TERMINATE_SIGNAL));
                }
    
                continue;
            }
    		//优雅地退出服务,向所有子进程发送QUIT信号,通知它们退出进程
            if (ngx_quit) {
                ngx_signal_worker_processes(cycle,
                                            ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
    
                ls = cycle->listening.elts;
                for (n = 0; n < cycle->listening.nelts; n++) {
                    if (ngx_close_socket(ls[n].fd) == -1) {
                        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
                                      ngx_close_socket_n " %V failed",
                                      &ls[n].addr_text);
                    }
                }
                cycle->listening.nelts = 0;
    
                continue;
            }
    		/*
    		重新读取配置文件。Nginx不会让原先的worker等子进程在从新读取配置文件。
    		它的策略是重新初始化ngx_cycle_t结构体,用它来读取新的配置文件,再拉起
    		新的worker进程,销毁就的worker进程。
    		*/
            if (ngx_reconfigure) {
                ngx_reconfigure = 0;
    
                if (ngx_new_binary) {
                    ngx_start_worker_processes(cycle, ccf->worker_processes,
                                               NGX_PROCESS_RESPAWN);
                    ngx_start_cache_manager_processes(cycle, 0);
                    ngx_noaccepting = 0;
    
                    continue;
                }
    
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
    			/*调用ngx_init_cycle方法重新初始化ngx_cycle_t结构体*/
                cycle = ngx_init_cycle(cycle);
                if (cycle == NULL) {
                    cycle = (ngx_cycle_t *) ngx_cycle;
                    continue;
                }
    			
                ngx_cycle = cycle;
    			//重新读取配置文件
                ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
                                                       ngx_core_module);
    			//启动worker子进程
                ngx_start_worker_processes(cycle, ccf->worker_processes,
                                           NGX_PROCESS_JUST_RESPAWN);
    			//启动cache_manage子进程
                ngx_start_cache_manager_processes(cycle, 1);
    
                /* allow new processes to start */
                ngx_msleep(100);
    
                live = 1;
    			//向原先的所有子进程发送QUIT信号
                ngx_signal_worker_processes(cycle,
                                            ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
            }
    		/*
    		调用ngx_start_worker_processes拉起worker进程,调用ngx_start_cache_manager_processes
    		根据缓存模块的请款选择是否启动cache manage进程或者cache loader进程,
    		同时将live置为1,ngx_restart置为0
    		*/
            if (ngx_restart) {
                ngx_restart = 0;
                ngx_start_worker_processes(cycle, ccf->worker_processes,
                                           NGX_PROCESS_RESPAWN);
                ngx_start_cache_manager_processes(cycle, 0);
                live = 1;
            }
    		/*重新打开所有文件*/
            if (ngx_reopen) {
    			/*将ngx_reopen置为0*/
                ngx_reopen = 0;
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
    			/*调用ngx_reopen_files方法重新打开所有文件*/
                ngx_reopen_files(cycle, ccf->user);
    			/*向所有子进程发送USR1信号,要求子进程都重新打开所有文件*/
                ngx_signal_worker_processes(cycle,
                                            ngx_signal_value(NGX_REOPEN_SIGNAL));
            }
    		/*表示需要平滑升级Nginx*/
            if (ngx_change_binary) {
    			/*将ngx_change_binary标志置为0*/
                ngx_change_binary = 0;
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
    			/*调用ngx_exec_new_binary方法用新的子进程启动新版本的Nginx程序*/
                ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
            }
    		/*向所有子进程发送QUIT信号,要求他们优雅关闭服务*/
            if (ngx_noaccept) {
    			/*将ngx_noaccept置为0*/
                ngx_noaccept = 0;
    			/*将ngx_noaccepting置为1,表示正在停止接受新的连接*/
                ngx_noaccepting = 1;
    			/*向所有子进程发送QUIT信号*/
                ngx_signal_worker_processes(cycle,
                                            ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
            }
        }
    }
    


     

     转载注明出处,谢谢~~

    展开全文
  • 问题描述:有一群生产者进程在生产产品,并将这些产品提供给消费者进程进行消费,生产者进程和消费者进程可以并发执行,两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程需要将所生产的产品放到一个缓冲区中...

    引言

    本文为第四篇,进程管理之进程同步,本文主要介绍为什么需要进程间同步以及进程间同步的原则和线程同步

    一、为什么需要进程间同步

    通过两个例子了解一下什么是进程同步以及为什么需要进程同步

    (1)生产者-消费者问题

    问题描述:有一群生产者进程在生产产品,并将这些产品提供给消费者进程进行消费,生产者进程和消费者进程可以并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程需要将所生产的产品放到一个缓冲区中,消费者进程可以从缓冲区中取走产品消费

    生产和消费的过程

    当生产者生产了一个产品之后,缓冲区里的产品就会+1,同样,如果消费者从缓冲区里边消费一个产品,缓冲区里的产品就会-1,在生活中这种模型是没有问题的(比如生产手机的工厂,流水线上生产完一个手机,就会放在仓库里边,消费者从仓库中取出手机消费,这个生产者-消费者模型从宏观的角度上看没有问题)
    在这里插入图片描述

    上边的模型在宏观的角度上看没有问题,但是在计算机微观的角度去看就会有问题。
    在计算机中,这个缓冲区是位于高速缓存或主存上边的,如果说生产者或消费者要操作里边的数据时,就分为三个步骤:

    a、取出数据放到寄存器中 register=count

    b、在CPU的寄存器中将register+1register=register+1 表示说生产者完成了一个产品

    c、将register放回缓冲区 count=register

    这三步就是就是我们操作缓冲区必须的三个步骤。我们就可以将缓冲区看作是仓库,将register寄存器看作是生产者的地方或者消费者的地方,这个模型我们乍一看,好像也没什么问题
    在这里插入图片描述

    单从生产者程序或者消费者程序去看是没问题的,但是如果两者并发的去执行的时候就有可能出现差错

    下边红色部分为生产者生产的过程,蓝色的为消费者消费的过程

    将register和count看作是两个部分的值(假设为10), 假设此时执行生产者的第一步,也就是register=count,此时两者均为10,接着执行生产者的第二步,register=register+1,此时寄存器中的值+1了,那么此时register=11,count=10,假设生产者程序和消费者程序是并发的执行的,那么第三步就有可能轮到消费者去执行了,那么假设此时到了消费者的第一步register=count,那么这个时候消费者的进程里边的寄存器的值就是10,接着执行第四步,假设第四步执行到消费者的第二步,也就是register=register-1,此时消费者的进程的寄存器的值就变成9了,而缓存里边的值还是10,接着执行第五步,第五步假设执行到消费者的第三个步骤count=register,也就是把寄存器里边的值写回到缓冲区里边,此时缓冲区和消费者的寄存器的值都是9了,这里就完成了消费者的操作,接下来还有一个生产者的操作,将生产者的register写回到缓冲区里边,那么在刚才,生产者的register是等于11,那么执行完这一步,它会将register重新的写回到缓冲区中,那么缓冲区中得值就变成了11,count=register。那这个样子其实就有问题了,刚开始缓冲区的值是10,而在执行的过程中进行了+1和-1的操作,那么它的值应该还是10才对,但是租后却变成了11,说明这个数据是错误的,错误的原因就在于这两个进程在并发的执行了,他们轮流的在操作缓冲区,导致缓冲区中的数据不一致,这个就是生产者-消费者的问题

    下边看一个实际执行的例子,下边是一个简单的程序,使用了两个线程模拟生产者和消费者的过程:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <vector>
    
    pthread_ mutex t mutex = PTHREAD MUTEX INITIALIZER;
    int num = 0;//全局变量,初始值为0
    void *producer(void*){//生产者
          int times = 10000000 ;
          while(times -- ){//生产者将num循环+1很多次,表示生产过程,消费者是循环-1
          //pthread mutex_ lock (&mutex) ;
          num+=1;
          //pthread mutex unlock (&mutex) ;
      }
    }
    
    void *comsumer (void* ){//消费者
          int times = 10000000 ;while(times -- ){
          //pthread mutex lock (&mutex) ;
          num  -=  1;
          //pthread mutex unlock (&mutex) ;
      }
    }
    
    //在main函数中创建了两个线程来模拟两个进程,一个线程执行producer的逻辑,一个线程执行comsumer的逻辑
    int main()
    {
    	printf("Start a main function.");
    	pthread_t thread1,thread1;
    	pthread_create(&thread1, NULL, &producer, NULL);
    	pthread_create(&thread1, NULL, &comsumer, NULL);
    	pthread_join(thread1, NULL);
    	pthread_join(thread2, NULL);
    	printf("Print in main function: num = %d\n", num);
    	retuen 0
    }
    

    因为生产者和消费者循环的次数都是一样的,那么执行的结果应该是0才对,那实际的执行结果是不是呢?我们会发现不是,那么这个就是生产者和消费者的问题。上边例子中的缓冲区和num就是临界资源

    (2)哲学家进餐问题

    问题描述:有五个哲学家,他们的生活方式是交替的进行思考和进餐,哲学家们共同使用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗五支筷子。平时哲学家们只进行思考,饥饿时则试图取靠近他们左、右两支筷子,只有两支筷子都被他拿到的时候才能进餐,进餐完毕后,放下筷子继续思考
    在这里插入图片描述

    那么这个过程为什么也需要进程同步呢?可以想象一下哲学家进餐的时候会出现什么样的情况,假设在这个时候某一位哲学家饿了,他需要拿起左边的筷子和右边的筷子进行吃饭。这个时候,第一步,拿起左边的筷子,第二步,拿起右边的筷子,假设此时他发现右边的筷子被拿了,那么他就会等待右边的筷子释放,筷子释放后,他拿起右边的筷子,开始吃饭。这就是哲学家吃饭的时候可能会面临的问题,这样一看,好像没有什么问题
    在这里插入图片描述

    看一种极端的情况,假设这五个哲学家同时肚子饿了,并且同时拿起了左边的筷子,然后此时他们就会发现自己右边的筷子都被拿了(可以对照上边的圆桌图想象一下),那么此时,五个哲学家都会等待自己右边的筷子被释放,而这个时候所有的筷子都被他们自己拿起来了,所以他们都会相互等待而拿不到筷子,并且他们也都不会释放自己左边的筷子,因此这五个哲学家就会饿死,这个就是最极端的情况

    上边就是哲学家进餐问题,现在把筷子换成资源,把哲学家换成进程,这个就是计算机进程所面临的问题,筷子就是临界资源

    总结一下发生上边两个问题的根源是什么?

    • 根源问题是:彼此之间没有进行通信
    • 第一个生产者-消费者的问题,我们假设生产者通知消费者我已经完成了一件生产
    • 第二个哲学家进餐问题,假设哲学家对旁边的哲学家说我要进餐了,这个时候就不会出现问题了

    因此得出结论

    需要进程间的同步,那么进程间同步是为了解决什么问题呢?

    1、对竞争资源在多进程间进行使用次序的协调

    2、使得并发执行的多个进程之间可以有效使用资源和相互合作

    二、进程间同步的原则

    临界资源:临界资源指的是一些虽作为共享资源却又无法同时被多个进程或线程共同访问的共享资源。当有进程使用临街资源时,其它进程必须依据操作系统的同步机制等待占用进程释放该共享资源才可重新竞争使用共享资源

    为了对临界资源进行有效的约束,就提出了进程间同步的四个原则

    • 空闲让进:资源无占用,允许使用
    • 忙则等待:资源被占用,请求进程等待
    • 有限等待:保证有限等待时间能够使用资源,避免其它等待的进程僵死
    • 让权等待:等待时,进程需让出CPU,也就是进程由执行状态变为阻塞状态,这也是保证CPU可以高效使用的前提

    进程间同步的方法:
    消息队列、共享存储、信号量。会在后边的文章中详细介绍这些进程间同步的方法

    三、线程同步

    从之前的文章《进程管理之进程实体》中知道,一个进程可能会有一个或多个线程,并且线程是共享进程资源的。那么现在就有个问题,如果多个线程并发的使用进程资源时,会发生什么?其实也同样会出现上边提到的生产者-消费者问题和哲学家进餐问题,因此我们得出结论:进程内多线程也需要同步,因为进程里边的线程会并发的去使用进程中的共享资源

    线程同步的方法

    • 互斥量:这个是保证多线程可以互斥访问临界资源的一个锁
    • 读写锁:这个是应对多读少写或多写少读这种情况而发明出来的锁
    • 自旋锁
    • 条件变量

    这些方法也会在后边的文章详细介绍

    在快速变化的技术中寻找不变,才是一个技术人的核心竞争力。知行合一,理论结合实践
    在这里插入图片描述

    展开全文
  • 进程可以并发执行,两者之间设置了一个具有n可缓冲区的缓冲池,生产者进程需要将所生产 的产品放到一缓冲区中,消费者进程可以从缓冲区取走产品消费。 单两者并发执行时就可能出差错 有五哲学家,他们的生活...

    一、为什么需要进程间同步?

    有一群生产者进程在生产产品,并将这些产品提供给消费者进程进行消费,生产者进程和消费者
    进程可以并发执行,在两者之间设置了一个具有n可缓冲区的缓冲池,生产者进程需要将所生产
    的产品放到一个缓冲区中,消费者进程可以从缓冲区取走产品消费。
    在这里插入图片描述
    单两者并发执行时就可能出差错

    有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共同使用一张圆桌,分别坐
    在周围的五张椅子上,在圆桌上有五个碗和五支筷子。平时哲学家们只进行思考,饥饿时则试图
    取靠近他们的左、右两支筷子,只有两支筷子都被他拿到的时候就能进餐,进餐完毕之后,放下
    左右筷子继续思考。

    在这里插入图片描述
    需要进程间的同步

    ◆ 根源问题是:彼此相互之间没有通信
    ◆ “如果生产者通知消费者我已经完成一件生产”
    ◆ “哲学家向旁边哲学家说我要进餐了”

    二、进程间同步的原则

    对竞争资源在多进程间进行使用次序的协调

    使得并发执行的多个进程之间可以有效使用资源和相互合作

    ◆ 空闲让进:资源无占用,允许使用
    ◆ 忙则等待:资源有占用,请求进程等待
    ◆ 有限等待:保证有限等待时间能够使用资源
    ◆ 让权等待:等待时,进程需要让出CPU

    -----临界资源

    临界资源指的是一些虽作为共享资源却又无法同时被多个线程共同访问的共享资源。当有进程在使用临界资源时,其他进程必须依据操作系统的同步机制等待占用进程释放该共享资源才可重新竞争使用共享资源。

    -----方法

    ◆ 消息队列
    ◆ 共享存储
    ◆ 信号量

    三、线程同步

    当多个线程并发使用进程资源时,进程内多线程也需要同步

    -----方法

    ◆ 互斥量
    ◆ 读写锁
    ◆ 自旋锁
    ◆ 条件变量

    展开全文
  • Master模型即为一个master进程+N个worker进程的工作方式。生产环境都是用master-worker模型来工作。本文着重分析Nginx的master进程做了哪些事情,它是如何管理好各个worker进程的。具体分析代码之前,先附上一张...
  • OS之进程同步

    2018-12-29 18:07:11
    为了使这个过程更加高效,我们这两个之间设置具有n个缓冲区的缓冲池 生产者进程和消费者进程以异步方式运行,但又必须保持同步 消费者进程不能向空缓冲区去产品 生产者进城也不能向满缓冲区放产品  ...
  • 经典进程的同步问题

    2019-09-27 10:00:25
    为使生产者进程与消费者进程能并发执行,两者之间设置了一个具有N个缓冲区的缓冲池,生产者进程将它放入缓冲区中;消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程和消费者进程都是以异步的方式...
  • 操作系统中用c语言实现对N个进程采用动态优先权的进程调度,输入实例为: 假设调度前,系统有5个进程,具有如下状态 5 9 0 3 2 3 38 0 3 1 1 30 0 6 4 2 29 0 3 1 1 0 0 4 1 2 1 每个标示进程的进程控制看PCB用...
  • 经典进程同步问题

    2012-05-23 11:18:00
    为使生产者和消费者进程能并发执行,它们之间设置一个具有n个缓冲池,生产者进程可将它所生产的产品放入一个缓冲池中,消费者进程可从一个缓冲区取得一个产品消费。 问题分析: 设两个同步信号量:一个说明空缓冲...
  • 为使生产者进程与消费者进程能够并发执行,两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中;消费者进程可以从一个缓冲区中取走产品去消费。尽管所有的生产者和消费者进程...
  • 匿名管道是半双工的,并且只能在具有公共祖先的两个进程之间使用。通常一个管道由一个进程创建,在进程调用fork之后,这个管道就能在父进程和子进程之间使用了。管道创建时会创建两个文件描述符,其中fd[0]为读而...
  • 为使生产者进程与消费者进程能并发执行,两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将其所生产的产品放入一个缓冲区中;消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程和消费者进程...
  • 为使生产者进程与消费者进程能并发执行,两者之间设置了一个具有 n 缓冲区的缓冲池,生产者进程将它所生产的产品放入一缓冲区中;消费者进程可从一缓冲区中取走产品去消费。尽管所有的生产者进程和消费者...
  • 经典进程的同步问题1

    2018-11-21 23:33:32
    假设生产者和消费者之间的公用缓冲池中具有n个缓冲区,这时可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用;利用empty和full分别表示缓冲池中空缓冲区和满缓冲区的数量。 int in=0,out=0; item buffer[n]; ...
  • 经典进程同步问题之生产者消费者...为使生产者进程与消费者进程能并发执行,两者之间设置了一个具有n 缓冲区的缓冲池,生产者进程将它所生产的产品放入一缓冲区中;消费者进程可从一缓冲区中取走产品去消费...
  • 为使生产者进程与消费者进程能并发执行,两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区;消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程和消费者进程都...
  • 为使生产者进程与消费者进程能并发执行,两者间设置了一个具有n个缓冲区的缓冲区池,生产者进程将其生产的产品放入一个缓冲区中,消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程都是以异步方式...
  • 进程间的通信方式

    2015-08-26 15:36:08
    (2)只能在具有公共祖先的两个进程间通信。 创建 #include int pip(int fd[2]);参数fd返回两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。fd[1]写入,fd[0]读出。 3. 经由父进程向子进程传送数据的例子#...
  • 为使生产者和消费者进程能并发执行,它们之间设置一个具有n个缓冲池,生产者进程可将它所生产的产品放入一个缓冲池中,消费者进程可从一个缓冲区取得一个产品消费。semaphore mutex=1,empty=n,full=0; item buffer...
  • 有一群生产者进程在生产产品,并将这些产品提供给消费者进程进行消费,生产者进程和消费者进程可以并发执行,两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程需要将所生产的产品放到缓冲区中(+1操作),...
  • 为了使生产者进程与消费者进程能够并发进行,两者之间设置一个具有n个缓冲区的缓冲池,生产者进程将产品放入一个缓冲区中;消费者可以从一个缓冲区取走产品去消费。尽管所有的生产者进程和消费者进程是以异方式...
  • Nginx源码分析--master进程

    千次阅读 2011-12-01 22:28:39
    Master模型即为一个master进程+N个worker进程的工作方式。生产环境都是用master-worker模型来工作。本文着重分析Nginx的master进程做了哪些事情,它是如何管理好各个worker进程的。具体分析代码之前,先附上一张...
  • 环境打印机 打印Windows上运行的给定进程的所有环境变量。 建造 dotnet publish - c Release (可执行文件可以.\... 如果有多个具有相同名称的进程,请指定一数字: EnvironmentPrinter.exe < process> - n 1
  • 《unix环境高级编程》--- 进程间通信

    千次阅读 2018-05-31 21:34:21
    2、只能在具有公共祖先的进程间使用。 下图显示父进程关闭读端(fd[0]),子进程关闭写端(fd[1])后的管道流向。 创建一从父进程到子进程的管道,由父进程向子进程发送数据。 #include &quot;apue.h&...
  • 为使生产者进程与消费者进程能够并发执行,两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中;消费者进程可以从一个缓冲区中取走产品去消费。尽管所有的生产者和消费者进程...
  • 第二章 进程管理 2.1 进程的描述与控制 2.1.1 进程的顺序执行和并发执行 ...若两程序p1和p2满足下列条件,则他们就能并发执行,且具有可再现性: R(P1) n W(P2) U R(P2) n W(P1) U W(P1)...
  • ,某系统N个进程共享6台打印机,每个进程要2台,N的最大值不超过(C 数字答案是5);10个进程共享3台同类打印机,则与打印机对应的互斥信号量的初值是(A 数字答案是 3);P.V操作是(两条低级通信原语);消息缓冲队列中的临界...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 226
精华内容 90
关键字:

在具有n个进程