进程间关系

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lu_1079776757/article/details/79968036

    今天我们的目标是学习进程间的几个关系:它们分别是进程组/作业/会话。

    首先我们谈论第一个话题----进程组。我们先来了解一下它的概念,什么是进程组呢?显而,进程组就是一个或多个进程组成的集合。每个进程除了有一个进程ID之外,还属于一个进程组。

进程组(Process Group)

    进程组就是一个或多个进程组成的集合。每个进程除了有一个进程ID之外,还属于一个进程组。通常,它们与同一个作业相关联,可以接受来自同一终端的各种信号每个进程组有一个唯一的进程组ID。每个进程组都可以有一个组长进程。组长进程标识是,其进程组ID等于其进程ID。组长进程可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中一个进程存在,则该组就存在,这与其组长进程是否终止无关。

我们看一个例子:


其中 “&”表示将进程组放在后台运行

进程: 4088 4089 4090

组长: 4088 进程组中的第一个进程

kill -9 杀掉组长,进程组还在


ps选项:

    a:不仅列出当前用户的进程,也列出所有其他用户的进程

    x:表示不仅列有控制终端的进程,也列出所有无终端的进程

    j:表示列出与作业控制相关的信息

作业(Jobs)

    shell分前后台来控制的,不是进程,而是作业或者进程组,一个前台作业可以有多个进程组成,一个后台作业可以由多个进程组成,shell可以运行一个前台作业和多个后台作业,这成为作业控制

    作业在后台的时候不允许从前台读取数据,一旦试图读取数据,则该进程被终止。

作业和进程组的区别:

    如果作业中的某个进程创建了子进程,那么这个子进程是不属于作业的,一旦作业运行结束,shell就把自己提到前台,如果前台作业还存在,那么它将自动变成后台进程组,在前台新起的作业,shell是无法运行的,因为它被提到了后台,如果前台进程退出,shell就又提到了前台,所以可以继续接收用户的输入。

现在我们来看一个例子:

#include <stdio.h>  
#include <unistd.h>  
#include <sys/types.h>  
int main(){  
    if(fork() == 0){  
        while(1){  
            printf("i am child:%d\n",getpid());  
            sleep(1);  
        }  
    }else{  
        int i = 5;  
        while(i>0){  
            printf("i am father:%d,i will dead:%d\n",getppid(),i);  
            i--;  
            sleep(1);  
        }  
    }  
    return 0;  
}

    我们发现,程序运行了以后,在前台创建了一个作业,包含了父进程和子进程,5s之内shell无法接受任何命令,说明此时的前台作业不是shell。但是我们发现当父进程退出后,子进程还在运行,但是此时我们输入命令,shell可以处理,说明变成了前台作业。换句话说,我们刚建的作业退出了,但是子进程还在,那么他就会被动提到后台。我们可以发现,子进程所属的组还在,但是父进程已经退出了。

会话(Session)

    会话是指一个或者多个进程组集合,一个会话可以有一个控制终端,这通常是登录到其上的终端设备或者是伪终端设备,建立与控制会话的首进程,被称之为控制进程(会话首进程),一个会话中的几个进程组可被分为一个前台进程和任意多个后台进程,所以会话中包括控制进程,一个前台进程,和任意多个后台进程,和新打开的一个终端。



    我们发现SID是会话ID:4142三个进程都属于同一个进程组,同一个会话,它们的父进程ID也相同,现在我们来查看一个他们的父进程是谁?


我们可以看得出来父进程就是bash,也就是会话首进程,而且三个进程的父进程都是bash。

作业控制

    Session与进程组“shell可以同时运行一个前台进程和任意多个后台进程”,其实这是不全面的,现在我们来研究一下复杂的情况。事实上,Shell分前台和后台开控制的不是进程而是作业(Jobs)或者进程组(Process Group),一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程古组成,Shell可以同时运行一个前台作业和任意多个后台作业,这就成为作业控制。现在我们来看个例子:


Ctrl-C杀掉的不是进程,而是整个作业。

现在我们来看一下作业控制有关的信号

作业控制有关的信号

    我们通过实验来理解与作业控制有关的信号


    在cat指令的后面加上“&”,表示让指令在后台运行,但是cat指令需要从中断读取数据,此时内核发送SIGTTIN信号给cat,该信号的默认处理动作是使进程停止。

指令:jobs,可以查看当前有哪些作业,fg命令可以将某个命令提到前台来进行,如果该作业的进程组在后台进行,则提到前台运行,如果作业处于停止,则发送SIGCONT使其继续运行,如果输入:ctrl+z则向所有前台进程发送SIGTSTP信号,该信号默认使得所有前台进程停止,cat继续以后台作业的形式存在。

bg命令可以让某个停止的作业在后台继续运行,也需要给改该作业的每个进程组发SIGCONT信号。cat进程继续运行,又要读中断输入,然而它在后台不能读端输入,所以又收到SIGTTIN信号而停止。


    以上过程是:先把cat放在后台运行,此时cat被stop,然后向cat发送SIGTERM(15)信号,默认进程终止,但是此时cat已经被stop了,所以此时不会立即的处理信号,得等到我们把cat运行起来的时候,再去执行这个信号,此时我们使用fg 1命令,代表将第一个后台作业提到前台来。

    kill 命令给一个停止的进程发SIGTERM(15)信号,这个信号不会立马被处理,而要等进程准备继续运行时处理,默认动作是终止进程。但是如果给一个停止的进程发SIGKILL信号就不同了。






linux系统文件流、缓冲及描述符与进程间关系详解

03-09

linux系统文件流、缓冲及描述符与进程间关系详解rnrn rnrnlinux(unix)进程与文件的关系错综复杂,本教程试图详细的阐述这个问题。rnrn包括:rnrn 1、linux多/单进程与多/单文件对于文件流和描述符在使用时的关联情况及一些需要注意的问题。rnrn 2、fork,vfork流缓冲等对文件操作的影响。rnrn rnrn1、linux文件系统结构rnrn首先补充一点基础知识,了解一下linux文件系统。如下图所示:rnrnrnrn[img=http://p.blog.csdn.net/images/p_blog_csdn_net/cuibo1123/EntryImages/20090309/1.JPG][/img]rn rnrn 图1 磁盘,分区和文件系统rnrn 应该明白,上图所示结构是硬盘中文件存放方式的一种逻辑表现形式,与进程无关。对于其中一些术语,见下面的解释。rnrni节点:包含文件/目录的几乎全部-适用于放置在硬盘上的,需要长久保存的信息。rnrn例如:文件所有者,文件类型,i节点号(存放在目录块中),主次设备号,连接计数,访问/修改时间,IO块长,文件字节数等等。rnrn可以用stat函数(#include )获取相关i节点信息信息。rnrn rnrn rnrn2、简单的进程与文件关系rnrn rnrn下面,我们了解一下单进程多文件以及多进程单文件间的关系,在不考虑fork(父子进程)的情况下,除了赋值语句给我们带来一些小麻烦以外,这个问题还是相当容易的。rnrn rnrn2.1、一个进程同时打开多个文件:rnrnrnrnrnrn [img=http://p.blog.csdn.net/images/p_blog_csdn_net/cuibo1123/EntryImages/20090309/2.JPG][/img]rnrn 图2 一个进程同时打开2个不同文件时内核数据结构rnrn 其中,v节点我们几乎已经介绍过了,因为它除了包含i节点之外,自身的内容实在是不怎么多,重点看一下文件表吧。rnrn 对于文件表,要注意,它并不是从在于硬盘中的东西,可以说,他是进程的一部分(可能是由操作系统内核负责维护,本人未考证,因为它到底是进程的还是内核的,对于我们要探讨的问题无关紧要)。文件表包括:rnrn 文件状态标志:包含读,写,添写,同步和非阻塞等各种文件打开/当前状态。rnrn V节点:文件类型和对此文件进行各种操作的函数的指针,这些信息都是在打开文件时候由磁盘读入内存的。rnrn I节点:同上节所述。rnrn可用fcntl函数(#include )修改文件表内容。rnrn rnrn rnrn2.2、多个无关联进程同时打开一个文件:rnrnrn rnrn[img=http://p.blog.csdn.net/images/p_blog_csdn_net/cuibo1123/EntryImages/20090309/3.JPG][/img]rnrn 图3 两个进程同时打开同一个文件时内核数据结构rnrn 此时,2个文件分别使用不同的文件表,这说明不同进程间文件状态标志,文件偏移量等都是独立的。但他们共用同一个v节点表。对于这种结构的特性,理解起来因该是个轻松的事情。rnrn rnrn3、文件描述符或流的复制rnrn对于文件描述符或流的复制,很多情况我们会采用赋值语句,下面了解一个赋值和dup的不同之处,dup函数复制文件描述符后的内核数据结构:rnrnrn rnrn[img=http://p.blog.csdn.net/images/p_blog_csdn_net/cuibo1123/EntryImages/20090309/4.JPG][/img]rnrn 图4 执行dup函数(#include )复制文件描述符后,内核数据结构。rnrn 此时,2个fd文件标志同时使用同一文件表。rnrn3.1、dup与赋值语句用于文件描述符的区别rnrn 为了了解dup与赋值语句用于文件描述符的区别,请看如下程序。rnrn程序描述:rnrn 打开一个文件描述符,分别适用dup和赋值语句进行复制,复制之后,打印原始和被复制的文件描述符id,看看是否具有相同的值,然后关闭文件,测试关闭是否成功。rnrn程序示例:rnrn#include rnrn#include rnrn#include rnrn#include rnrn rnrnint sys_err(char *str)rnrnrnrn puts(str);rnrn exit(0);rnrnrnrn rnrnint main(void)rnrnrnrn int p,q;rnrn rnrn if((p=open("c_fid.c", O_RDONLY)) == -1)rnrn sys_err("open error");rnrn q = dup(p);rnrn puts("dup:");rnrn printf("file p,q fd is:%d %d\n", q, p);rnrn printf("close file p ok?: %d\n", close(p));rnrn printf("close file q ok?: %d\n", close(q));rnrn rnrn if((p=open("c_fid.c", O_RDONLY)) == -1)rnrn sys_err("open error");rnrn q = p;rnrn puts("=:");rnrn printf("file p,q fd is:%d %d\n", q, p);rnrn printf("close file p ok?: %d\n", close(p));rnrn printf("close file q ok?: %d\n", close(q));rnrn rnrn return 0;rnrnrnrn rnrn程序运行结果:rnrndup:rnrnfile p,q fd is:4 3 //文件p,q使用不同的文件描述符rnrnclose file p ok?: 0rnrnclose file q ok?: 0 //文件关闭成功rnrn=:rnrnfile p,q fd is:3 3 //简单复制rnrnclose file p ok?: 0rnrnclose file q ok?: -1//关闭失败,原因是此描述符已经被关闭了rnrn rnrn 由此证明,dup是产生一个新的文件描述符id和指针,但是他们共用文件表,效果如图4,这时,关闭一个文件描述符,另外一个仍旧可用,文件表并不会被释放。而赋值语句不同,它只是简单的在另外一个变量中记录原始文件指针等,2个变量的文件描述符相同,进程表项中并不产生新的项目。rnrn rnrn3.2、赋值语句复制标准流。rnrn例:rnrn#include rnrn#include rnrn rnrnint sys_err(char *str)rnrnrnrn puts(str);rnrn exit(0);rnrnrnrn rnrnint main(void)rnrnrnrn FILE *p,*q;rnrn rnrn if((p=fopen("c_fid.c", "r")) == NULL)rnrn sys_err("open error");rnrn q = p;rnrn printf("FILE p,q fd is:%d %d\n", fileno(q),fileno(p));rnrn printf("close file p ok?: %d\n", fclose(p));rnrn printf("close file q ok?: %d\n", fclose(q));rnrn rnrn return 0;rnrnrnrn rnrn程序执行结果:rnrnFILE p,q fd is:3 3 //2个流共用同一个文件描述符rnrnclose file p ok?: 0rnrn*** glibc detected ***//2次关闭引起错误,造成程序崩溃。rnrn…………rnrn rnrn rnrn4、 引入fork后进程与文件关系以及流缓冲对文件操作的影响rnrn4.1 forkrnrnrn rn[img=http://p.blog.csdn.net/images/p_blog_csdn_net/cuibo1123/EntryImages/20090309/5.JPG][/img] rnrn 图5 使用fork之后,父进程、子进程之间对打开文件的共享rnrn 使用fork后,子进程会继承父进程所打开的文件表,此时,父子进程使用同一个文件表(可见,上面猜测文件表由内核维护因该是正确的,因为文件表并没有被复制),这说明2个进程将共用文件状态,文件偏移量等信息。如果使用close关闭一个进程中的文件描述符,那么另一个进程中,此描述符仍然有效,相应文件表也不会被释放。rnrn 需要注意的是,在使用c标准io进行文件读写时,先结束的进程会将缓冲区内数据也计入文件偏移量的长度,对于相应文件缓冲区类型和长度,可以使用setbuf,setvbuf(#include )设置。rnrn程序示例:rnrn#include rnrn#include rnrn#include rnrn#include rnrn#include rnrn rnrnint main(void)rnrnrnrn int pid;rnrn FILE *p;rnrn char buff[20]=0;rnrn int test=-2;rnrn rnrn if((p=fopen("/media/lin_space/soft/netbeans-6.5-ml-cpp-linux.sh", "r")) == NULL)rnrn //文件大于4096字节rnrn puts("open error.");exit(0);rnrn rnrn rnrn if((pid=fork()) < 0)rnrn rnrn puts("fork error");rnrn exit(0);rnrn rnrn else if(pid == 0)rnrn rnrn sleep(2);rnrn test = ftell(p);//返回当前偏移量rnrn printf("\nchild - ftell is: %d\n", test);rnrn if((buff[0] = fgetc(p)) != EOF)rnrn printf("child - fgetc is: %c\n", buff[0]);rnrn elsernrn puts("child - fgetc error\n");rnrn test = ftell(p);rnrn printf("child - ftell is: %d\n", test);rnrn rnrn elsernrn rnrn test = ftell(p);rnrn printf("\nparent - ftell is: %d\n", test);rnrn if((buff[0] = fgetc(p)) != EOF)rnrn printf("parent - fgetc is: %c\n", buff[0]);rnrn elsernrn puts("parent - fgetc error\n");rnrn test = ftell(p);rnrn printf("parent - ftell is: %d\n", test);rnrn rnrn rnrn printf("parent and child - close file ok?: %d\n", fclose(p));rnrn return 0;rnrnrnrn rnrn程序执行结果:rnrnparent - ftell is: 0rnrnparent - fgetc is: #rnrnparent - ftell is: 1rnrnparent and child - close file ok?: 0 rnrnfreec@freec-laptop:/media/lin_space/summa/apue/unit8$ //父进程结束rnrnchild - ftell is: 4096 //子进程文件偏移量为4096,原因是文件缓冲类型为全缓冲,缓冲区大小为4096rnrnchild - fgetc is: arnrnchild - ftell is: 4097rnrnparent and child - close file ok?: 0 //文件关闭成功rnrn rnrn4.2 vforkrnrn 而对于vfork,虽然子进程运行于父进程的空间,但是子进程却拥有自己的进程表项(包含进程pid,fd标志,文件指针等),所以,在其中一个进程结束后,另外一个进程的文件描述符依旧有效,子进程得到的是父进程文件描述符的副本。rnrn 但是vfork对于标准流,情况则不同,一个进程结束后(如果不是使用_exit()结束进程),则此进程结束时可能会冲洗流缓冲区,并且关闭流,对于是否这样做,要取决于具体实现。rnrn

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试