进程间关系

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

一、进程组:一个或多个进程的集合。通常,它们与一作业相关联,可以接收来自同一终端的信号。每个进程组有唯一的进程组ID。每个进程有一个组长进程,其进程ID就是进程组ID。
组长进程可以创建一个进程组,创建该组中的进程,然后终止。只要进程组中有一个进程存在,进程组就一直存在,与其组长是否存在无关。
用代码创建一个进程组:

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

#define ERR_EXIT(m) \
    do\
    {\
        printf(m);\
        exit(EXIT_FAILURE);\
    }while(0)

int main()
{
    pid_t pid;
    if((pid = fork()) < 0)
        ERR_EXIT("fork");

    else if(pid == 0)
    {
        if(fork() < 0)
            ERR_EXIT("fork");
        while(1);
    }
    else
    {
        sleep(10);
        printf("process group quit\n");
        exit(EXIT_SUCCESS);
    }

    return 0;
}

最先存在的进程为进程组组长,创建子进程后,父进程10秒后退出,子进程继续创建一个子进程后一直执行死循环操作。当进程组组长退出后,这个进程组依然存在:
这里写图片描述

二、作业:一个作业也可以说是一个任务,可以由一个或多个进程组成,可以理解为相当于一个进程组所执行的功能,但是又与进程组有区别。
作业与进程组区别:如果作业中某个进程有创建了子进程,则子进程不属于此作业。
我们知道shell有前台进程和后台进程,实质上正真有前后台区分的是作业(或进程组)。shell可以同时运行一个前台作业和任意多个后台作业,这称为作业控制
在前台作业运行时,shell自动转到后台执行,成为后台进程组。前台作业运行结束后,shell就把自己提到前台运行(如果有子进程则子进程还在,子进程不属于作业)。
用代码创建作业观察:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define ERR_EXIT(m) \
    do\
    {\
        printf(m);\
        exit(EXIT_FAILURE);\
    }while(0)
int main()
{
    pid_t pid;
    if((pid = fork()) < 0)
        ERR_EXIT("fork");
    else if(pid == 0)
    {
        while(1)
        {
            printf("Child run, pid :%d\n", getpid());
            sleep(1);
        }
    }
    else
    {
        int i = 1;
        while(i <= 5)
        {
            printf("%dsecond, father :%d\n", i, getpid());
            i++;
            sleep(1);
        }
    }

    return 0;
}

父进程在5秒后退出,shell返回前台。而子进程依然在后台运行,用Ctrl+C不能结束子进程:
这里写图片描述

三、会话:会话(Session)是一个或多个进程组的集合。一个会话可以有一个控制终端。这通常是登录到其上的终端设备(终端登录的情况下)或伪终端设备(网络登录的情况下)。
建立与控制终端连接的会话首进程称为控制进程。一个会话中的几个进程组可被分为一个前台进程组和任意多个后台进程组。所以一个会话中应包含一个控制进程(会话首进程)、一个前台进程组合任意多个后台进程。
在shell创建3个后台进程:
这里写图片描述
这里bash为控制进程,同时也是前台作业,而三个sleep进程属于同一个进程组,是一个后台作业。这样bash进程和三个sleep进程构成了一个会话。

作业控制有关信号:
jobs:可查看当前有哪些作业。
选项:
-l:显示进程号;
-p:仅任务对应的显示进程号;
-n:显示任务状态的变化;
-r:仅输出运行状态(running)的任务;
-s:仅输出停止状态(stoped)的任务。

fg:将指定(如果后台作业只有一个不用指定)作业提至前台运行,如果作业处于停止状态,则给这个作业的每个进程发送SIGCONT信号使之运行。

bg:用于将指定作业放到后台运行,其运行效果与在指令后面加&一样。如果指定作业处于停止状态,则向作业中每个进程发送SIGCONT信号使之运行。

Ctrl+Z:向前台作业进程发送SIGTSTP信号,使前台作业停止,并以后台作业方式存在。

Ctr+C:给前台作业中的每个进程发送SIGINT信号,结束前台作业。对后台作业无效。

SIGTERM(15):给一个停止的进程发送这个信号后,这个信号并不会立即处理,而要等进程准备运行之前处理,其默认处理动作是结束进程。

创建三个后台作业:
这里写图片描述
分别用jobs的-l和-p选项列出三个作业:
这里写图片描述
将一号作业转到前台运行,然后使之停止,并转到后台:
这里写图片描述
用bg是一号作业继续运行:
这里写图片描述
用同样的方法是作业2停止,然后分别列出处于运行状态和停止状态的作业:
这里写图片描述
这里写图片描述
给作业2的某个进程发送SIGTERM信号,这个进程和作业还存在:
这里写图片描述
再次是这个作业运行后,被发送SIGTERM信号的进程被杀死了,而同一个作业中的其他进程还继续运行:
这里写图片描述

阅读更多

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

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