精华内容
下载资源
问答
  • 试验一~设计一N个进程共行的进程调度程序
  • 假如有多个进程争夺一种资源,这资源共有n个,每个进程需要这种资源m,并且每个进程当得到某一资源之后不会直到执行完成都不会释放这占有的资源,只有这个进程的需求得到满足之后他才会执行完成,那么问最多...

    假如有多个进程争夺一种资源,这个资源共有n个,每个进程需要这种资源m个,并且每个进程当得到某一个资源之后不会直到执行完成都不会释放这个占有的资源,只有这个进程的需求得到满足之后他才会执行完成,那么问最多有多少个这样的进程争夺这m个资源,一定不会发生死锁?

    其实这个问题的简化版本是哲学家问题,哲学家问题是说有n个餐具,每个哲学家需要2个餐具才能用餐,问最多可以有多少个哲学家,才能保证每个哲学家能够用餐完成。

    我们可以考虑有m个哲学家围成一圈,按顺序给每个哲学家分一个餐具,假如每个哲学家都分到了一个餐具,这个时候餐具还有剩余,那么对于m个哲学家而言,他们一定能够用餐完成。

    也就是说只要n > m * (2 – 1) ,那么对于这m个哲学家,他们一定能够用餐完成,当n < m * 1,的时候,有可能出现哲学家不能用餐完成,循环等待的情况,也就是说最多可以有n – 1个哲学家用餐。

    再回到题目的问题,假如餐具有n个,每个哲学家需要m个餐具,有t个哲学家,那么当n > t * (m - 1)的时候,不会出现循环等待,那么同理,当n % (m – 1) !=0的时候,最多有n / (m – 1)个哲学家,当n % (m – 1) == 0的时候,最多有n / (m – 1) – 1个哲学家。

    展开全文
  • 入门学习Linux常用必会60命令实例详解doc/txt

    千次下载 热门讨论 2011-06-09 00:08:45
    n:一般而言,mount挂上后会/etc/mtab中写入一笔资料,系统中没有可写入文件系统的情况下,可以用这选项取消这动作。 4.应用技巧 Linux 和Unix系统上,所有文件都是作为一大型树(以/为根)的一部分...
  • 今有三并发进程R,M,P,它们共享了一可循环使用的缓冲区B,缓冲区B共有N个单元。进程R负责从输入设备读信息,每读一字符后,把它存放缓冲区B的一单元中;进程M负责处理读入的字符,若发现读入的字符中有...

    今有三个并发进程R,M,P,它们共享了一个可循环使用的缓冲区B,缓冲区B共有N个单元。进程R负责从输入设备读信息,每读一个字符后,把它存放在缓冲区B的一个单元中;进程M负责处理读入的字符,若发现读入的字符中有空格符,则把它改成“,”;进程P负责把处理后的字符取出并打印输出。当缓冲区单元中的字符被进程P取出后,则又可用来存放下一次读入的字符。请用PV操作为同步机制写出它们能正确并发执行的程序。

    semaphore S1=1;
    semaphore S2=0;
    semaphore S3=0;
    semaphore S4=N;//缓冲区B有N个单元
    main()
    {
    	cobegin
    	R();
    	M();
    	P();
    	coend
    }
    R()
    {
    	P(S1);
    	P(S4)
    	char x=read();
    	V(S2);
    }
    M()
    {
    	P(S2);
    	if(x==' ')
    	x=',';
    	V(S1);
    	V(S3);
    }
    P()
    {
    	P(S3);
    	print(x);
    	V(S4)
    }
    
    展开全文
  • 【Linux】Linux进程的创建与管理

    万次阅读 多人点赞 2018-07-27 19:21:29
    Linux系统中,除了系统启动之后的第一个进程由系统来创建,其余的进程都必须由已存在的进程来创建,新创建的进程叫做子进程,而创建子进程进程叫做父进程。那个系统启动及完成初始化之后,Linux自动创建的进程...

    在Linux系统中,除了系统启动之后的第一个进程由系统来创建,其余的进程都必须由已存在的进程来创建,新创建的进程叫做子进程,而创建子进程的进程叫做父进程。那个在系统启动及完成初始化之后,Linux自动创建的进程叫做根进程。根进程是Linux中所有进程的祖宗,其余进程都是根进程的子孙。具有同一个父进程的进程叫做兄弟进程。

    Linux进程创建的过程示意图如下所示:

     

    子进程的创建

    在Linux中,父进程以分裂的方式来创建子进程,创建一个子进程的系统调用叫做fork()。

    系统调用fork()

    为了在一个进程中分裂出子进程,Linux提供了一个系统调用fork()。这里所说的分裂,实际上是一种复制。因为在系统中表示一个进程的实体是进程控制块,创建新进程的主要工作就是要创建一个新控制块,而创建一个新控制块最简单的方法就是复制。

    当然,这里的复制并不是完全复制,因为父进程控制块中某些项的内容必须按照子进程的特性来修改,例如进程的标识、状态等。另外,子进程控制块还必须要有表示自己父进程的域和私有空间,例如数据空间、用户堆栈等。下面的两张图就表示父进程和相对应的子进程的内存映射:

    稍微介绍一下进程地址空间:

    进程的数据区也就是未初始化的数据(.bss)、已初始化的数据(.data);进程的栈区也就是进程的用户栈和堆;进程程序代码就是进程的程序文件(.text);除此之外还有进程的系统堆栈区。可见下面的这张更详细的介绍图:

    例子:

    #include <stdio.h>
    
    int count1=0;
    int main(void)
    {
        int pid;
        int count2=0;
        count1++;
        count2++;
        printf("count1=%d,count2=%d\n",count1,count2);
    
        pid=fork();
        count1++;
        count2++;
        printf("count1=%d,count2=%d\n",count1,count2);
        printf("pid=%d\n"pid);
    
        return 0;
    }

    程序运行结果为:

    由此可知:

    • 函数fork()却是分裂出了两个进程:因为自函数fork()之后执行了两遍之后的代码(先子进程一次,后父进程一次)。同时,这也证明了父进程和子进程运行的是同一个程序,也正是这个理由,系统并未在内存中给子进程配置独立的程序运行空间,而只是简单地将程序指针指向父进程的代码;
    • 两个进程具有各自的数据区和用户堆栈,在函数fork()生成子进程时,将父进程数据区和用户堆栈的内容分别复制给了子进程。同时,接下来的内容,父进程和子进程都是对自己的数据区和堆栈中的内容进行修改运算了。

    其实,在父进程中调用fork()之后会产生两种结果:一种为分裂子进程失败,另一种就是分裂子进程成功。如果fork()失败,则返回-1,;否则会出现父进程和子进程两个进程,在子进程中fork()返回0,在父进程中fork()返回子进程的ID。系统调用fork()工作流程示意图如下:

    也就是说,在fork()函数之前需要确认内核中有足够的资源来完成。如果资源满足要求,则内核slab分配器在相应的缓冲区中构造子进程的进程控制块,并将父进程控制块中的全部成员都复制到子进程的控制块,然后再把子进程控制块必须的私有数据改成子进程的数据。当fork()返回到用户空间之前,向子进程的内核栈中压入返回值0,而向父进程内核堆栈压入子进程的pid。最后进行一次进程调度,决定是运行子进程还是父进程。

    显然,为了能够使子程序和父程序执行不同的代码,在fork()之后应该根据fork()的返回值使用分支结构来组成程序代码。例如:

    #include <stdio.h>
    #include <sys/types.h>
    
    int main(void)
    {
        pid_t pid;
    
        pid = fork();
        if(pid < 0){
            ...                //打印fork()失败信息
        }else if(pid == 0){
            ...                //子进程代码
        }else{
            ...                //父进程代码
        }
    
        return 0;
    }

    在代码中获得当前进程pid的函数为:getpid();

    在代码中获得当前进程父进程pid的函数为:getppid()。

    这里需要注明一点:父子进程的调度的顺序是由调度器决定的,与进程的创建顺序无关。

    关于子进程的时间片

    与UCOSIII不同,Linux在大多数情况下是按时间片来进行进程调度的。当某一个进程所拥有的时间片用完之后,内核会立即剥夺它的运行权,停止它的执行。那么当子进程被创建出来之后,这个子进程的初始时间片应该是多大呢?

    Linux规定,如果父进程只是如之前那样简单地创建了一个子进程,那么系统会将父进程剩余的时间片分成两份,一份留给父进程,另一份作为子进程的时间片。因为之前的这种简单方式下,父子进程使用的是同一个代码,还没有成为两个真正的各自独立的进程,所以没有资格享用两份时间片。

     

    与进程相关的系统调用

    函数execv()

    为了在程序运行中能够加载并运行一个可执行文件,Linux提供了系统调用execv()。其原型为:

    int execv(const char* path, char* const argv[]);

    其中,参数path为可执行文件路径,argv[]为命令行参数。

    如果一个进程调用了execv(),那么该函数便会把函数参数path所指定的可执行文件加载到进程的用户内存空间,并覆盖掉原文件,然后便运行这个新加载的可执行文件。

    在实际应用中,通常调用execv()的都是子进程。人们之所以创建一个子进程,其目的就是执行一个与父进程代码不同的程序,而系统调用execv()就是子进程执行一个新程序的手段之一。子进程调用execv()之后,系统会立即为子进程加载可执行文件分配私有程序内存空间,从此子进程也成为一个真正的进程。

    如果说子进程是父进程的“儿子”,那么子进程在调用execv()之前,它所具有的单独用户堆栈和数据区也仅相当于它的私有“房间”;但因它还没有自己的“住房”,因此也只能寄住在“父亲”家,而不能“自立门户”,尽管它有自己的“户口”(进程控制块)。

    调用execv()后,父进程与子进程存储结构的示意图如下:

    与上文刚fork()的内存映像图相比,刚调用fork()之后,父子共同使用同一块程序代码;而调用execv()之后,子程序拥有了自己的程序代码区。

    例子:

    #include <stdio.h>
    #include <sys/types.h>
    
    int main(void)
    {
        pid_t pid;
    
        if(!(pid=fork())){
            execv("./hello.o",NULL);
        }else {
            printf("my pif is %d\n", getpid());
        }
    
        return 0;
    }

    而可执行文件./hello.o可以编写一个.c程序再进行编译获得。

    函数execv()其实是Linux的exec函数族成员之一,该函数族一共有5个函数和1个系统调用,分别是:

    int execl(const char* path, const char* arg, ...);
    int execlp(const char* file, const char* arg, ...);
    int execle(const char* path, const char* arg, ..., char* const envp[]);
    int execv(const char* path, const char* argv[]);
    int execvp(const char* file, const char* argv[]);
    int execve(const char* path, const char* argv[], char* const envp[]);

    其中,只有execv()是真正意义上的系统调用,其他的都是在此基础上经过包装的库函数。

    execv函数族的作用是,根据指定的文件名找到可执行文件,并将其关联到调用exec族函数的进程,从而使进程执行该可执行文件。简单地说,就是用execv族函数加载的程序文件代替该进程原来的程序文件。

    与一般的函数不同,exec族函数执行成功后一般不会返回调用点,因为它运行了一个新的程序,进程的代码段、数据段和堆栈等都已经被新的数据所取代,只留下进程ID等一些表面信息仍保持原样,虽然还是旧的躯壳,但是其实质内容已经全部变化了。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

    系统调用wait()

    虽然子进程调用函数execv()之后拥有自己的内存空间,称为一个真正的进程,但由于子进程毕竟由父进程所创建,所以按照计算机技术中谁创建谁负责销毁的惯例,父进程需要在子进程结束之后释放子进程所占用的系统资源。

    为实现上述目标,当子进程运行结束后,系统会向该子进程的父进程发出一个信息,请求父进程释放子进程所占用的系统资源。但是,父进程并没有准确的把握一定结束于子进程结束之后,那么为了保证完成为子进程释放资源的任务,父进程应该调用系统调用wait()。

    如果一个进程调用了系统调用wait(),那么进程就立即进入等待状态(也叫阻塞状态),一直等到系统为本进程发送一个消息。在处理父进程与子进程的关系上,那就是在等待某个子进程已经退出的信息;如果父进程得到了这个信息,父进程就会在处理子进程的“后事”之后才会继续运行。

    也就是说,wait()函数功能是:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

    如果父进程先于子进程结束进程,则子进程会因为失去父进程而成为“孤儿进程”。在Linux中,如果一个进程变成了“孤儿进程”,那么这个进程将以系统在初始化时创建的init进程为父进程。也就是说,Linux中的所有“孤儿进程”以init进程为“养父”,init进程负责将“孤儿进程”结束后的资源释放任务。

    这里区分一下僵尸进程和孤儿进程:

    • 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作;
    • 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程(也就是进程为中止状态,僵死状态)。

    Linix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。 但这样就导致了问题,如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

    参考文章:孤儿进程与僵尸进程[总结]

    系统调用exit()

    系统调用exit()用来终结一个进程,通常这个系统调用会在一些与进程控制有关的系统调用中被调用。

    如果一个进程调用exit(),那么这个进程会立即退出运行,并负责释放被中止进程除了进程控制块之外的各种内核数据结构。这种只剩下“身份证”的进程叫做“僵尸进程”,其进程控制块域state的值为TASK_ZOMBLE。

    一个因调用exec函数族函数而执行新的可执行文件的子进程,当进程执行结束时,便会执行系统调用exit()而使自己成为一个“僵尸进程”,这也正是父进程要负责销毁子进程的进程控制块的原因。

    另外,exit()执行到最后,将调用进程调度函数schedule()进行一次进程调度。

    这里区分一下exit()和_exit()函数:

    exit()定义在stdlib.h文件,_exit()定义在unistd.h文件中。同时两者的步骤也是不一样的:

    具体体现在哪里呢?

    在Linux标准库中,有一套称为高级I/O函数,例如我们所熟知的printf、fopen、fread、fwrite都在此列,它们也被称为缓冲I/O。其特征是对应每一个打开的文件,都存在一个缓冲区,在每次读文件时会多读若干条记录,这样下次读文件时就可以直接从内存的缓冲区去读。在每次写文件时也会先写入缓冲区,当缓冲区写满,或者我们fflush()手动的刷新缓冲区,或者遇到\n、EOF这样的结束符,才会把对应的数据从缓冲区写入到文件中。这样的好处是大大增加的文件的读写的速度,因为我们都知道磁盘上的读写速度是很慢的,但这也为我们编程带来了一点麻烦,例如有些数据,我们认为已经写入了文件,但实际上它们很可能存在在缓冲区中。

    也就是说,_exit()函数的作用是:直接使进程停止运行,清除其使用的内存空间,并清除其在内核的各种数据结构;exit()函数则在这些基础上做了一些小动作,在执行退出之前还加了若干道工序。exit()函数与_exit()函数的最大区别在于exit()函数在调用exit  系统调用前要检查文件的打开情况,把文件缓冲区中的内容写回文件。也就是图中的“清理I/O缓冲”。

    参考文章:exit函数和_exit函数的区别

    系统调用vfork()

    vfork()是Linux提供的另一个用来生成一个子进程的系统调用。

    与fork()不同,vfork()并不把父进程全部复制到子进程中,而只是用用赋值指针的方法使子进程与父进程的资源实现共享。由于函数vfork()生成的子进程与父进程共享同一块内存空间,因此实质上vfork()创建的是一个线程,但习惯上人们还是叫它子进程。

    用vfork()创建子进程且调用execve()之后,子进程的内存映像如下所示:

    这里区分一下fork()与vfork():

    • fork():子进程拷贝父进程的数据段,代码段 。vfork():子进程与父进程共享数据段 ;
    • fork():父子进程的执行次序不确定 。vfork():保证子进程先运行,在调用execve()或exit()之前,与父进程数据是共享的,在它调用execve()或exit()之后,父进程才可能被调度运行。

    注意:由于vfork()保证子进程先运行,在它调用execve()或exit()之后,父进程才可能被调度运行。如果在调用这两个函数之前,子进程依赖于父进程的进一步动作,则会导致死锁。 

     

    内核中的进程和线程

    操作系统是一个先于其他程序运行的程序,那么当系统中除了操作系统之外没有其他的进程时怎么办?同时,系统中的所有用户进程必须由父进程来创建,那么“祖宗进程”是什么呢?

    前一个问题由Linux的进程0来解决,后一个问题由Linux的进程1来解决。其实,前者是一个内核进程,而后者先是一个内核进程,后来又变为用户进程。

    内核线程及其创建

    Linux实现线程的机制非常独特,它没有另外定义线程的数据结构和调度算法,只不过在创建时不为其分配单独的内存空间。如果线程运行在用户空间,那么它就是用户线程;如果运行在内核空间,那么它就是内核线程。并且,无论是线程还是进程,Linux都用同一个调度器对它们进行调度。

    但是Linux却单独提供了一个用于创建内核线程的函数kernel_thread()。该函数的原型如下:

    int kernel_thread(int (* fn)(void *), void* arg, unsigned long flags);

    其中,fn指向线程代码;arg为线程代码所需的入口参数。

    内核线程周期性执行,通常用来完成一些需要对内核幕后完成的任务,例如磁盘高速缓存的刷新、网络连接的维护和页面的交换等,所以它们也叫做内核任务。

    进程0

    计算机启动之后,首先进行Linux的引导和加载,在Linux内核加载之后,初始化函数start_kernel()会立即创建一个内核线程。因为Linux对线程和进程没有太严格的区分,所以就把这个由初始化函数创建的线程叫做进程0。

    进程0的执行代码时内核函数cpu_idel(),该函数中只有一条hlt(暂停)指令;也就是说,这个进程什么工作也没做,所以也叫做空闲进程。该空闲进程的PCB叫做init_task(),当系统没有可运行的其它进程时,调度器才会选择进程0来运行。

    进程1

    进程1也叫做init进程,它是内核初始化时创建的第2个内核线程,其运行代码为内核函数init()。

    该函数首先创建kswapd()等4个与内存管理有关的内核线程。接下来,在内核的init()中调用execve()系统调用装入另一个需要在用户空间运行的init函数代码,于是进程1就变成一个普通的进程。它是用户空间的第一个进程,所以它就成了其他用户进程的根进程。

    只要系统,init进程就永不中止,它负责创建和监控操作系统外层所有进程的活动。

    守护进程

    守护进程是不受终端控制并在后台运行的进程。Linux使用了很多守护进程,在后台做一些经常性的注入定期进行页交换之类的例行工作。

    守护进程一般可以通过以下方式启动:

    • 在系统启动时由启动脚本启动,这些启动脚本通常放在/etc/rc.d目录下;
    • 利用inetd超级服务器启动,大部分网络服务都是这样启动的,如ftp、telnet等;
    • 由cron定时启动以及在终端用nohup启动的进程也是守护进程。

     

    展开全文
  • 转载:http://blog.csdn.net/sunvince/article/details/6533016 这是CU上的一问题...   from: ...SMP上,想把所有的用户 态进程运行CPU上,腾出其它CPU干其它事。Linux能通过

    转载:http://blog.csdn.net/sunvince/article/details/6533016

    这是CU上的一个问题...

     

    from: http://bbs.chinaunix.net/viewthread.php?tid=2330394

     

     

    在SMP上,想把所有的用户 态进程运行在一个CPU上,腾出其它CPU干其它事。Linux能通过简单的配置实现吗?而不是去修改内核代码。

     

    回复:

    Linux 内核 API 提供了一些方法,让用户可以修改位掩码或查看当前的位掩码:
    sched_set_affinity() (用来修改位掩码)

    sched_get_affinity() (用来查看当前的位掩码)

     

    回复:

    不改内核的话,直接用 taskset  命令就可以啦,兄弟。
    不过我喜欢直接在内核里面修改 sched_set_affinity,这样用户空间就省事了。

     

     

    下面转载... taskset使用方法 

    from: http://www.blogkid.net/archives/2670.html

     

    我的Linode 十分繁忙,在跑一些密集操作数据库的Rake任务时尤其如此。但我观察发现,Linode服务器的4核CPU,只有第1个核心(CPU#0)非常忙,其他都处于idle状态。

    不了解Linux是如何调度的,但目前显然有优化的余地。除了处理正常任务,CPU#0还需要处理每秒 网卡中断。因此,若能将CPU#0分担的任务摊派到其他CPU核心上,可以预见,系统的处理能力将有更大的提升。

    两个名词

    SMP (Symmetrical Multi-Processing):指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。 [更多... ]

    CPU affinity :中文唤作“CPU亲和力”,是指在CMP架构下,能够将一个或多个进程绑定到一个或多个处理器上运行。[更多... ]

    一、在Linux上修改进程的“CPU亲和力”

    在Linux上,可以通过 taskset 命令进行修改。以Ubuntu为例,运行如下命令可以安装taskset 工具。

    # apt-get install schedutils
    

    对运行中的进程,文档上说可以用下面的命令,把CPU#1 #2 #3分配给PID为2345的进程:

    # taskset -cp 1,2,3 2345
    
    
    

    但我尝试没奏效 ,于是我关掉了MySQL,并用taskset将它启动:

    # taskset -c 1,2,3 /etc/init.d/mysql start
    
    

    对于其他进程,也可如此处理(nginx除外,详见下文)。之后用top查看CPU的使用情况,原来空闲的#1 #2 #3,已经在辛勤工作了。

    二、配置nginx绑定CPU

    刚才说nginx除外,是因为nginx提供了更精确的控制。

    conf/nginx.conf 中,有如下一行:

    worker_processes  1;
    

    这是用来配置nginx启动几个工作进程的,默认为1。而nginx还支持一个名为worker_cpu_affinity的配置项,也就是说,nginx可以为每个工作进程绑定CPU 。我做了如下配置:

    worker_processes  3;
    worker_cpu_affinity 0010 0100 1000;

    这里0010 0100 1000是掩码,分别代表第2、3、4颗cpu核心。

    重启nginx后,3个工作进程就可以各自用各自的CPU了。

    三、刨根问底

    1. 如果自己写代码,要把进程绑定到CPU,该怎么做?可以用sched_setaffinity 函数。在Linux上,这会触发一次系统调用 。
    2. 如果父进程设置了affinity,之后其创建的子进程是否会有同样的属性?我发现子进程确实继承了父进程的affinity属性。

    四、Windows?

    在Windows上修改“CPU亲和力”,可以通过任务管理器搞定。

    * 个人感觉,Windows系统中翻译的“处理器关系”比“CPU亲和力”容易理解点儿

    —————–

    进行了这样的修改后,即使系统负载达到3以上,不带缓存打开blogkid.net首页(有40多次查询)依然顺畅;以前一旦负载超过了1.5,响应就很慢了。效果很明显。

     

     


     

     

    另外还有一篇文章:详细的用程序例子分析了CPU affinity (亲和力)

     

    from:http://fuzhong1983.blog.163.com/blog/static/16847052009112413613231/

     

    觉得人为控制一下cpu的绑定还是有用处的
    1、linux的SMP负载均衡是基于进程数的,每个cpu都有一个可执行进程队列,只有当其中一个cpu的可执行队列里进程数比其他cpu队列进程数多25%时,才会将进程移动到另外空闲cpu上,也就是说cpu0上的进程数应该是比其他cpu上多,但是会在25%以内


    2、我们的业务中耗费cpu的分四种类型,(1)网卡中断(2)1个处理网络收发包进程(3)耗费cpu的n个worker进程(4)其他不太耗费cpu的进程

        基于1中的 负载均衡是针对进程数,那么(1)(2)大部分时间会出现在cpu0上,(3)的n个进程会随着调度,平均到其他多个cpu上,(4)里的进程也是随着调度分配到各个cpu上;

    当发生网卡中断的时候,cpu被打断了,处理网卡中断,那么分配到cpu0上的worker进程肯定是运行不了的

    其他cpu上不是太耗费cpu的进程获得cpu时,就算它的时间片很短,它也是要执行的,那么这个时候,你的worker进程还是被影响到了;按照 调度逻辑,一种非常恶劣的情况是:(1)(2)(3)的进程全部分配到cpu0上,其他不太耗费cpu的进程数很多,全部分配到 cpu1,cpu2,cpu3上。。那么网卡中断发生的时候,你的业务进程就得不到cpu了

    如果从业务的角度来说,worker进程运行越多,肯定业务处理越快,人为的将它捆绑到其他负载低的cpu上,肯定能提高worker进程使用cpu的时间

    找了个例子:

    现在多CPU的趋势越来越大了. 有时候为了更好地操作机器, 需要将某个进程绑定到具体的CPU上去. 下面给出了一个进程绑定到具体的CPU上去的一个例子.

    view plaincopy to clipboardprint?
    ·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
    #include<stdlib.h>   
    #include<stdio.h>   
    #include<sys/types.h>   
    #include<sys/sysinfo.h>   
    #include<unistd.h>   
      
    #define __USE_GNU   
    #include<sched.h>   
    #include<ctype.h>   
    #include<string.h>   
      
    int main(int argc, char* argv[])   
    {   
            int num = sysconf(_SC_NPROCESSORS_CONF);   
            int created_thread = 0;   
            int myid;   
            int i;   
            int j = 0;   
      
            cpu_set_t mask;   
            cpu_set_t get;   
      
            if (argc != 2)   
            {   
                    printf("usage : ./cpu num/n");   
                    exit(1);   
            }   
      
            myid = atoi(argv[1]);   
      
            printf("system has %i processor(s). /n", num);   
      
            CPU_ZERO(&mask);   
            CPU_SET(myid, &mask);   
      
            if (sched_setaffinity(0, sizeof(mask), &mask) == -1)   
            {   
                    printf("warning: could not set CPU affinity, continuing.../n");   
            }   
            while (1)   
            {   
      
                    CPU_ZERO(&get);   
                    if (sched_getaffinity(0, sizeof(get), &get) == -1)   
                    {   
                            printf("warning: cound not get cpu affinity, continuing.../n");   
                    }   
                    for (i = 0; i < num; i++)   
                    {   
                            if (CPU_ISSET(i, &get))   
                            {   
                                    printf("this process %d is running processor : %d/n",getpid(), i);   
                            }   
                    }   
            }   
            return 0;   
    }  
    #include<stdlib.h>
    #include<stdio.h>
    #include<sys/types.h>
    #include<sys/sysinfo.h>
    #include<unistd.h>

    #define __USE_GNU
    #include<sched.h>
    #include<ctype.h>
    #include<string.h>

    int main(int argc, char* argv[])
    {
            int num = sysconf(_SC_NPROCESSORS_CONF);
            int created_thread = 0;
            int myid;
            int i;
            int j = 0;

            cpu_set_t mask;
            cpu_set_t get;

            if (argc != 2)
            {
                    printf("usage : ./cpu num/n");
                    exit(1);
            }

            myid = atoi(argv[1]);

            printf("system has %i processor(s). /n", num);

            CPU_ZERO(&mask);
            CPU_SET(myid, &mask);

            if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
            {
                    printf("warning: could not set CPU affinity, continuing.../n");
            }
            while (1)
            {

                    CPU_ZERO(&get);
                    if (sched_getaffinity(0, sizeof(get), &get) == -1)
                    {
                            printf("warning: cound not get cpu affinity, continuing.../n");
                    }
                    for (i = 0; i < num; i++)
                    {
                            if (CPU_ISSET(i, &get))
                            {
                                    printf("this process %d is running processor : %d/n",getpid(), i);
                            }
                    }
            }
            return 0;
    }

    下面是在两个终端分别执行了./cpu 0  ./cpu 2 后得到的结果. 效果比较明显.

    QUOTE:
    Cpu0  :  5.3%us,  5.3%sy,  0.0%ni, 87.4%id,  0.0%wa,  0.0%hi,  2.0%si,  0.0%st
    Cpu1  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Cpu2  :  5.0%us, 12.2%sy,  0.0%ni, 82.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Cpu3  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Cpu4  :  0.0%us,  0.0%sy,  0.0%ni, 99.7%id,  0.3%wa,  0.0%hi,  0.0%si,  0.0%st
    Cpu5  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Cpu6  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Cpu7  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    ///
    CPU Affinity (CPU亲合力)

    CPU亲合力就是指在Linux系统中能够将一个或多个进程绑定到一个或多个处理器上运行.
    一个进程的CPU亲合力掩码决定了该进程将在哪个或哪几个CPU上运行.在一个多处理器系统中,设置CPU亲合力的掩码可能会获得更好的性能.
    一个CPU的亲合力掩码用一个cpu_set_t结构体来表示一个CPU集合,下面的几个宏分别对这个掩码集进行操作:
    CPU_ZERO() 清空一个集合
    CPU_SET()与CPU_CLR()分别对将一个给定的CPU号加到一个集合或者从一个集合中去掉.
    CPU_ISSET()检查一个CPU号是否在这个集合中.
    其实这几个的用法与select()函数那几个调用差不多.
    下面两个函数就是最主要的了:
    sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
    该函数设置进程为pid的这个进程,让它运行在mask所设定的CPU上.如果pid的值为0,则表示指定的是当前进程,使当前进程运行在mask所设定的那些CPU上.第二个参数cpusetsize是

    mask所指定的数的长度.通常设定为sizeof(cpu_set_t).如果当前pid所指定的CPU此时没有运行在mask所指定的任意一个CPU上,则该指定的进程会从其它CPU上迁移到mask的指定的

    一个CPU上运行.
    sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
    该函数获得pid所指示的进程的CPU位掩码,并将该掩码返回到mask所指向的结构中.即获得指定pid当前可以运行在哪些CPU上.同样,如果pid的值为0.也表示的是当前进程.

    这几个宏与函数的具体用法前面已经有讲解.

    关于cpu_set_t的定义

    # define __CPU_SETSIZE  1024
    # define __NCPUBITS     (8 * sizeof (__cpu_mask))

    typedef unsigned long int __cpu_mask;

    # define __CPUELT(cpu)  ((cpu) / __NCPUBITS)
    # define __CPUMASK(cpu) ((__cpu_mask) 1 << ((cpu) % __NCPUBITS))

    typedef struct
    {
      __cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
    } cpu_set_t;


    # define __CPU_ZERO(cpusetp) /
      do {                                                                        /
        unsigned int __i;                                                         /
        cpu_set_t *__arr = (cpusetp);                                             /
        for (__i = 0; __i < sizeof (cpu_set_t) / sizeof (__cpu_mask); ++__i)      /
          __arr->__bits[__i] = 0;                                                 /
      } while (0)
    # define __CPU_SET(cpu, cpusetp) /
      ((cpusetp)->__bits[__CPUELT (cpu)] |= __CPUMASK (cpu))
    # define __CPU_CLR(cpu, cpusetp) /
      ((cpusetp)->__bits[__CPUELT (cpu)] &= ~__CPUMASK (cpu))
    # define __CPU_ISSET(cpu, cpusetp) /
      (((cpusetp)->__bits[__CPUELT (cpu)] & __CPUMASK (cpu)) != 0)

    在我的机器上sizeof(cpu_set_t)的大小为128,即一共有1024位.第一位代表一个CPU号.某一位为1则表示某进程可以运行在该位所代表的cpu上.例如
    CPU_SET(1, &mask);
    则mask所对应的第2位被设置为1.
    此时如果printf("%d/n", mask.__bits[0]);就打印出2.表示第2位被置为1了.


    具体我是参考man sched_setaffinity文档中的函数的. 
    然后再参考了一下IBM的 developerWorks上的一个讲解. 
    http://www.ibm.com/developerworks/cn/linux/l-affinity.html


    其中的概念:

    CPU亲和性(affinity)sched_setaffinity() 和 sched_getaffinity()

    分类: Linux基础编程   949人阅读  评论(0)  收藏  举报

    简单地说,CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。

    2.6 版本的 Linux 内核还包含了一种机制,它让开发人员可以编程实现 硬 CPU 亲和性(affinity)。这意味着应用程序可以显式地指定进程在哪个(或哪些)处理器上运行。

    什么是 Linux 内核硬亲和性(affinity)?

    在 Linux 内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是 cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。

    如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态。

    Linux 内核 API 提供了一些方法,让用户可以修改位掩码或查看当前的位掩码:

    • sched_set_affinity() (用来修改位掩码)
    • sched_get_affinity() (用来查看当前的位掩码)

    注意,cpu_affinity 会被传递给子线程,因此应该适当地调用 sched_set_affinity

     

    通常 Linux 内核都可以很好地对进程进行调度,在应该运行的地方运行进程(这就是说,在可用的处理器上运行并获得很好的整体性能)。内核包含了一些用来检测 CPU 之间任务负载迁移的算法,可以启用进程迁移来降低繁忙的处理器的压力。

    一般情况下,在应用程序中只需使用缺省的调度器行为。然而,您可能会希望修改这些缺省行为以实现性能的优化。让我们来看一下使用硬亲和性(affinity) 的 3 个原因。

    原因 1. 有大量计算要做

    基于大量计算的情形通常出现在科学和理论计算中,但是通用领域的计算也可能出现这种情况。一个常见的标志是您发现自己的应用程序要在多处理器的机器上花费大量的计算时间。

    原因 2. 您在测试复杂的应用程序

    测试复杂软件是我们对内核的亲和性(affinity)技术感兴趣的另外一个原因。考虑一个需要进行线性可伸缩性测试的应用程序。有些产品声明可以在 使用更多硬件 时执行得更好。

    我们不用购买多台机器(为每种处理器配置都购买一台机器),而是可以:

    • 购买一台多处理器的机器
    • 不断增加分配的处理器
    • 测量每秒的事务数
    • 评估结果的可伸缩性

    如果应用程序随着 CPU 的增加可以线性地伸缩,那么每秒事务数和 CPU 个数之间应该会是线性的关系(例如斜线图 —— 请参阅下一节的内容)。这样建模可以确定应用程序是否可以有效地使用底层硬件。

    Amdahl 法则

    Amdahl 法则是有关使用并行处理器来解决问题相对于只使用一个串行处理器来解决问题的加速比的法则。加速比(Speedup) 等于串行执行(只使用一个处理器)的时间除以程序并行执行(使用多个处理器)的时间:

          T(1)
    S = ------
          T(j)

    其中 T(j) 是在使用 j 个处理器执行程序时所花费的时间。

    Amdahl 法则说明这种加速比在现实中可能并不会发生,但是可以非常接近于该值。对于通常情况来说,我们可以推论出每个程序都有一些串行的组件。随着问题集不断变大,串行组件最终会在优化解决方案时间方面达到一个上限。

    Amdahl 法则在希望保持高 CPU 缓存命中率时尤其重要。如果一个给定的进程迁移到其他地方去了,那么它就失去了利用 CPU 缓存的优势。实际上,如果正在使用的 CPU 需要为自己缓存一些特殊的数据,那么所有其他 CPU 都会使这些数据在自己的缓存中失效。

    因此,如果有多个线程都需要相同的数据,那么将这些线程绑定到一个特定的 CPU 上是非常有意义的,这样就确保它们可以访问相同的缓存数据(或者至少可以提高缓存的命中率)。否则,这些线程可能会在不同的 CPU 上执行,这样会频繁地使其他缓存项失效。

    原因 3. 您正在运行时间敏感的、决定性的进程

    我们对 CPU 亲和性(affinity)感兴趣的最后一个原因是实时(对时间敏感的)进程。例如,您可能会希望使用硬亲和性(affinity)来指定一个 8 路主机上的某个处理器,而同时允许其他 7 个处理器处理所有普通的系统调度。这种做法确保长时间运行、对时间敏感的应用程序可以得到运行,同时可以允许其他应用程序独占其余的计算资源。

    下面的样例应用程序显示了这是如何工作的。

     

    现在让我们来设计一个程序,它可以让 Linux 系统非常繁忙。可以使用前面介绍的系统调用和另外一些用来说明系统中有多少处理器的 API 来构建这个应用程序。实际上,我们的目标是编写这样一个程序:它可以让系统中的每个处理器都繁忙几秒钟。


    清单 1. 让处理器繁忙

                    
    /* This method will create threads, then bind each to its own cpu. */
    bool do_cpu_stress(int numthreads)
    {
       int ret = TRUE;
       int created_thread = 0;
       /* We need a thread for each cpu we have... */
       while ( created_thread < numthreads - 1 )
       {
          int mypid = fork();
          if (mypid == 0) /* Child process */
           {
              printf("\tCreating Child Thread: #%i\n", created_thread);
              break;
          }
          else /* Only parent executes this */
          {
              /* Continue looping until we spawned enough threads! */ ;
              created_thread++;
          }
       }
       /* NOTE: All threads execute code from here down! */
    

     

    正如您可以看到的一样,这段代码只是通过 fork 调用简单地创建一组线程。每个线程都执行这个方法中后面的代码。现在我们让每个线程都将亲和性(affinity)设置为自己的 CPU。


    清单 2. 为每个线程设置 CPU 亲和性(affinity)

                    
       cpu_set_t mask;
       /* CPU_ZERO initializes all the bits in the mask to zero. */
            CPU_ZERO( &mask );
       /* CPU_SET sets only the bit corresponding to cpu. */
            CPU_SET( created_thread, &mask );
       /* sched_setaffinity returns 0 in success */
            if( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 )
       {
          printf("WARNING: Could not set CPU Affinity, continuing...\n");
       }
    

     

    如果程序可以执行到这儿,那么我们的线程就已经设置了自己的亲和性(affinity)。调用 sched_setaffinity 会设置由 pid 所引用的进程的 CPU 亲和性(affinity)掩码。如果 pid 为 0,那么就使用当前进程。

    亲和性(affinity)掩码是使用在 mask 中存储的位掩码来表示的。最低位对应于系统中的第一个逻辑处理器,而最高位则对应于系统中最后一个逻辑处理器。

    每个设置的位都对应一个可以合法调度的 CPU,而未设置的位则对应一个不可调度的 CPU。换而言之,进程都被绑定了,只能在那些对应位被设置了的处理器上运行。通常,掩码中的所有位都被置位了。这些线程的亲和性(affinity)都会传递给从它们派生的子进程中。

    注意不应该直接修改位掩码。应该使用下面的宏。虽然在我们的例子中并没有全部使用这些宏,但是在本文中还是详细列出了这些宏,您在自己的程序中可能需要这些宏。


    清单 3. 间接修改位掩码的宏

                    
    void CPU_ZERO (cpu_set_t *set)
    这个宏对 CPU 集 set 进行初始化,将其设置为空集。
    void CPU_SET (int cpu, cpu_set_t *set)
    这个宏将 cpu 加入 CPU 集 set 中。
    void CPU_CLR (int cpu, cpu_set_t *set)
    这个宏将 cpu 从 CPU 集 set 中删除。
    int CPU_ISSET (int cpu, const cpu_set_t *set)
    如果 cpu 是 CPU 集 set 的一员,这个宏就返回一个非零值(true),否则就返回零(false)。
    

     

    对于本文来说,样例代码会继续让每个线程都执行某些计算量较大的操作。


    清单 4. 每个线程都执行一个计算敏感的操作

                    
        /* Now we have a single thread bound to each cpu on the system */
        int computation_res = do_cpu_expensive_op(41);
        cpu_set_t mycpuid;
        sched_getaffinity(0, sizeof(mycpuid), &mycpuid);
        if ( check_cpu_expensive_op(computation_res) )
        {
          printf("SUCCESS: Thread completed, and PASSED integrity check!\n",
             mycpuid);
          ret = TRUE;
        }
        else
        {
          printf("FAILURE: Thread failed integrity check!\n",
             mycpuid);
          ret = FALSE;
        }
       return ret;
    }
    

     

    现在您已经了解了在 Linux 2.6 版本的内核中设置 CPU 亲和性(affinity)的基本知识。接下来,我们使用一个 main 程序来封装这些方法,它使用一个用户指定的参数来说明要让多少个 CPU 繁忙。我们可以使用另外一个方法来确定系统中有多少个处理器:

    int NUM_PROCS = sysconf(_SC_NPROCESSORS_CONF);

    这个方法让程序能够自己确定要让多少个处理器保持繁忙,例如缺省让所有的处理器都处于繁忙状态,并允许用户指定系统中实际处理器范围的一个子集。

     

    当运行前面介绍的样例程序时,可以使用很多工具来查看 CPU 是否是繁忙的。如果只是简单地进行测试,可以使用 Linux 命令top。在运行 top 命令时按下 “1” 键,可以看到每个 CPU 执行进程所占用的百分比。

     

    这个样例程序虽然非常简单,但是它却展示了使用 Linux 内核中实现的硬亲和性(affinity)的基本知识。(任何使用这段代码的应用程序都无疑会做一些更有意义的事情。)了解了 CPU 亲和性(affinity)内核 API 的基本知识,您就可以从复杂的应用程序中榨取出最后一点儿性能了。

    展开全文
  • 引 一Node.js应用程序只能使用线程中。...cluster模块中,可以使用fork方法开启多个子进程每个子进程中创建一Node.js应用程序的实例,并且该应用程序中运行一模块文件 cluster.fork(...
  • 它是一开源操作系统,可以不同的硬件平台上运行。它为用户提供了免费的低成本操作系统。这是一用户友好的环境,他们可以其中轻松修改和创建源代码的变体。 2.谁发明了Linux?解释Linux的历史? 回答:...
  • 进程与线程概念

    千次阅读 多人点赞 2018-09-18 15:34:48
    进程是指一个具有一定独立功能的程序,数据集合上的一次动态执行过程。 如下图所示,源代码文件经过编译链接形成可执行文件,将可执行文件加载进内存进行执行。为什么要加载进内存呢?这是因为负责解析和...
  • 基于C语言的进程调度模拟程序设计

    千次阅读 2019-07-23 21:49:19
    用C语言设计一n个并发进程进行调度的程序,每个进程由一个进程控制块(PCB)结构表示,调度程序应当包含2种不同的调度算法,运行时可以任选一种,以利于各种方法的分析和比较,程序应能显示或打印各种进程状态和...
  • 【数据库学习】数据库总结

    万次阅读 多人点赞 2018-07-26 13:26:41
    数据库是长期存储计算机内、有组织的、可共享的大量数据的集合。 常见数据库管理系统有:Access、mysql、sql server 2)特点 ①数据库数据特点 永久存储、有组织、可共享。 (数据的最小存取...
  • 进程

    千次阅读 2021-04-10 21:41:24
    1.进程的概念 程序: (1)存放磁盘上的指令和数据的有序集合(文件) ...一程序执行的时候会创建多个进程 通过系统数据段,可以使得操作系统有效的管理进程,系统数据段中主要包含:进程控制块
  • 第五章-Linux实操篇

    千次阅读 多人点赞 2019-10-24 18:45:20
    显示大型文件具有较高的效率 。 操作 功能说明 空白键 向下移动一页 [pagedown] 向下翻动一页 [pageup] 向上翻动一页 /字串 向下搜寻[字串],n向下查询,N向上查询 ...
  • OS中引入进程后,一方面使系统的吞吐量和资源的利用率得到提升,另一方面也使得系统变得复杂,如果没有合理的方式对进程进行妥善的管理,必然会引起进程对系统资源的无序竞争,使系统变得混乱;为了实现对并发进程...
  • 让每一个进程在屏幕上显示一字符:父进程显示字符“a”; 两子进程分别显示字符“b”和字符“c”。 步骤 1:使用 vi 或 gedit 新建一 fork_demo.c 程序,然后拷贝清单 2-1 中的程序,使用 ...
  • 用C语言设计一n个并发进程进行调度的程序,每个进程由一个进程控制块(PCB)结构表示,该进程控制块应包括下述信息:进程标识ID、进程优先数PRIORITY(并规定优先数与优先权成正比)、时间片数CHIP、进程已经...
  • 进程基础

    千次阅读 多人点赞 2019-11-03 20:07:32
    进程的基本概念 程序顺序执行的特征: 1)顺序性:处理机严格按照程序所规定的顺序执行,每一步操作必须在下一步操作开始前执行 2)封闭性:程序封闭的环境下运行,程序独占资源,资源的状态由程序决定,...
  • 整型信号量是一整数变量,除初始化外,对其只能执行两操作,即wait(s)和signal(s),也叫p(s)和v(s)操作,均是原语操作,用来实现进程的同步,互斥.  2.记录型信号量 type semaphore = rec
  • 计算机操作系统实验-进程调度实验

    千次阅读 2020-10-15 14:15:51
    二、实验内容:任务:设计一N个进程并行的进程调度程序进程调度算法:采用最高优先数优先的调度算法(即把处理机分配给优先数最高的进程)和同优先级条件下先来先服务算法。 每个进程有一个进程控制块( PCB)...
  • 进程调度算法设计

    千次阅读 2019-08-06 19:10:29
    进程管理中,进程调度是核心,因为采用多道程序设计的系统中,往往有若干个进程同时处于就绪状态,当就绪进程个数大于处理器数目时,就必须依照某种策略决定哪些进程优先占用处理器。本实验模拟单处理器情况下...
  • 下面对进程的描述中,错误的是 。 A.进程是动态的概念 B. 进程执行需要处理机 C.进程是有生命周期的 D. 进程是指令的集合 【答案】D 【解析】程序是指令的集合。而进程是程序的一次执行,是动态的,有生命周期的。 ...
  • Linux进程概念(精讲)

    千次阅读 多人点赞 2021-09-09 08:54:04
    文章目录基本概念描述进程-PCB组织进程查看进程通过系统调用获取进程的PID和PPID通过系统调用创建进程- fork初始进程状态运行状态-R浅度睡眠状态-S深度睡眠状态-D暂停状态-T僵尸状态-Z死亡状态-X僵尸进程僵尸进程的...
  • Linux编程入门四进程

    千次阅读 2019-06-10 16:26:39
    系统允许一个进程创建新进程(即为子进程),子进程还可以创建新的子进程,形成进程树结构。整个Linux系统的所有进程也是一树形结构。树根是系统自动构造的,即内核态下执行的0号进程,它是所有进程的祖先。由0...
  • 实验四Linux 进程之间的通信

    千次阅读 多人点赞 2019-11-24 15:42:43
    实验4 进程之间的通信 实验性质:验证性 实验学时:4学时 一、实验目的 1.掌握管道、信号、共享内存、消息队列等进程间通信机制; 2.能够利进程间通信机制求解一些常见问题。 二、实验预备知识 1.阅读并掌握...
  • Linux进程间通信—— 内存映射

    千次阅读 2017-07-29 10:26:08
    不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁...
  • ulimit -n 102400 #修改当前进程的最大文件数 网友共享的经验值: #修改文件 /etc/sysctl.conf ,末尾追加这些文字 fs.file-max = 2097152  fs.nr_open = 2097152  net.core.s
  • 进程调度

    千次阅读 2019-07-03 14:43:54
    当一个进程从运行状态切换到等待状态。 当一个进程终止。 当一个进程从运行状态切换到就绪状态。 当一个进程从等待状态切换到就绪状态。 1,2两种情形下,操作系统必须选择一新的进程去执行。当调度只出现1,2两...
  • Linux 进程基本概念 什么是进程

    千次阅读 2019-03-15 20:20:04
    操作系统主要由内核(进程管理、内存管理、文件管理、驱动管理)和其他程序(如函数库、shell等组成)。OS的目的是为了让计算机与硬件交互,管理所有的软硬件资源来为用户(应用程序)提供一良好的执行环境。通俗...
  • 进程间的关系

    千次阅读 2018-04-16 21:16:46
    进程间有什么关系呢?我们都知道使用fork函数创建进程是一次调用两次返回。父进程返回子进程进程ID(非0),子进程返回0...介绍进程间关系之前先介绍两重要的概念:前台进程与后台进程 我们输入的命令由bash...
  • 文章目录零、前言一、实验内容二、实验步骤三、实验数据及源代码四、实验结果分析五、思考题1、进程...当此程序运行时,系统中有一进程和两个子进程并发执行,观察实验结果并分析原因。 2、用fork( )创建一

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 224,621
精华内容 89,848
关键字:

在具有n个进程