-
Linux中的进程组会话守护进程
2020-05-15 11:26:51当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID==其进程ID 组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。...终端
在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal),进程中,控制终端是保存在PCB中的信息,而fork会复制PCB中的信息,因此由Shell进程启动的其它进程的控制终端也是这个终端。默认情况下(没有重定向),每个进程的标准输入、标准输出和标准错误输出都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。
Alt + Ctrl + F1、F2、F3、F4、F5、F6 字符终端 pts (pseudo terminal slave) 指伪终端。
Alt + F7 图形终端
SSH、Telnet… 网络终端进程组
概念特性
进程组,也称之为作业。代表一个或多个进程的集合。每个进程都属于一个进程组。
当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID==其进程ID
组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。
进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。
一个进程可以为自己或子进程设置进程组ID
可以使用kill -SIGKILL -进程组ID(负的)来将整个进程组内的进程全部杀死。 //kill -SIGKILL -19189进程组操作函数
getpgrp函数
获取当前进程的进程组ID
pid_t getpgrp(void); 总是返回调用者的进程组IDgetpgid函数
获取指定进程的进程组ID
pid_t getpgid(pid_t pid); 成功:0;失败:-1,设置errno
如果pid = 0,那么该函数作用和getpgrp一样。
练习:查看进程对应的进程组ID#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { pid_t pid; if ((pid = fork()) < 0) { perror("fork"); exit(1); } else if (pid == 0) { printf("child process PID == %d\n", getpid()); //子进程ID printf("child Group ID == %d\n", getpgrp()); //子进程所在组ID printf("child Group ID == %d\n", getpgid(0)); //传0表当前进程 printf("child Group ID == %d\n", getpgid(getpid())); exit(0); } sleep(3); printf("parent process PID == %d\n", getpid()); printf("parent Group ID is %d\n", getpgrp()); return 0; }
setpgid函数
变进程默认所属的进程组。通常可用来加入一个现有的进程组或创建一个新进程组。
int setpgid(pid_t pid, pid_t pgid); 成功:0;失败:-1,设置errno
将参1对应的进程,加入参2对应的进程组中。
注意:- 如改变子进程为新的组,应fork后,exec前。
- 权级问题。非root进程只能改变自己创建的子进程,或有权限操作的进程
会话
创建会话
创建一个会话需要注意以下6点注意事项:
- 调用进程不能是进程组组长,该进程变成新会话首进程(session header)
- 该进程成为一个新进程组的组长进程。
- 需有root权限(ubuntu不需要)
- 新会话丢弃原有的控制终端,该会话没有控制终端
- 该调用进程是组长进程,则出错返回
- 建立新会话时,先调用fork, 父进程终止,子进程调用setsid
getsid函数
获取进程所属的会话ID
pid_t getsid(pid_t pid); 成功:返回调用进程的会话ID;失败:-1,设置errno
pid为0表示察看当前进程session ID
ps ajx命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。
组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。setsid函数
创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID。
pid_t setsid(void); 成功:返回调用进程的会话ID;失败:-1,设置errno
调用了setsid函数的进程,既是新的会长,也是新的组长。练习:fork一个子进程,并使其创建一个新会话。查看进程组ID、会话ID前后变化
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { pid_t pid; if ((pid = fork())<0) { perror("fork"); exit(1); } else if (pid == 0) { printf("child process PID is %d\n", getpid()); printf("Group ID of child is %d\n", getpgid(0)); printf("Session ID of child is %d\n", getsid(0)); sleep(5); setsid(); //子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程 printf("Changed:\n"); printf("child process PID is %d\n", getpid()); printf("Group ID of child is %d\n", getpgid(0)); printf("Session ID of child is %d\n", getsid(0)); } sleep(10); // exit(0); return 0; }
守护进程
Daemon(精灵)进程,是Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互。不受用户登录、注销的影响,一直在运行着,他们都是守护进程。如:预读入缓输出机制的实现;ftp服务器;nfs服务器等。
创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。创建守护进程模型
- 创建子进程,父进程退出
所有工作在子进程中进行形式上脱离了控制终端 - 在子进程中创建新会话
setsid()函数
使子进程完全独立出来,脱离控制 - 改变当前目录为根目录
chdir()函数
防止占用可卸载的文件系统
也可以换成其它路径 - 重设文件权限掩码
umask()函数
防止继承的文件创建屏蔽字拒绝某些权限
增加守护进程灵活性 - 关闭文件描述符/重定向
继承的打开文件不会用到,浪费系统资源,无法卸载 - 开始执行守护进程核心工作
- 守护进程退出处理程序模型
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> void daemonize(void) { pid_t pid; /* 成为一个新会话的首进程,失去控制终端 */ if ((pid = fork()) < 0) { perror("fork"); exit(1); } else if (pid != 0) /* parent */ exit(0); setsid(); //创建新会话 /* 改变当前工作目录到/目录下.*/ if (chdir("/") < 0) { perror("chdir"); exit(1); } /* 设置umask为0 */ umask(0); /* 重定向0,1,2文件描述符到 /dev/null,因为已经失去控制终端,再操作0,1,2没有意义.*/ close(0); open("/dev/null", O_RDWR); dup2(0, 1); dup2(0, 2); } int main(void) { daemonize(); while(1); /* 在此循环中可以实现守护进程的核心工作 */ }
-
linux系统的进程组和守护进程
2020-02-17 14:36:29进程组 概念和特性 进程组,也称之为作业。BSD于1980年前后向Unix中增加...当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID==其...进程组
概念和特性
进程组,也称之为作业。BSD于1980年前后向Unix中增加的一个新特性。代表一个或多个进程的集合。每个进程都属于一个进程组。在waitpid函数和kill函数的参数中都曾使用到。操作系统设计的进程组的概念,是为了简化对多个进程的管理。 当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID==其进程ID 可以使用kill -SIGKILL -进程组ID(负的)来将整个进程组内的进程全部杀死。 【kill_multprocess.c】 组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。 进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。 一个进程可以为自己或子进程设置进程组ID
进程组操作函数
getpgrp函数
获取当前进程的进程组ID pid_t getpgrp(void); 总是返回调用者的进程组ID
getpgid函数
获取指定进程的进程组ID pid_t getpgid(pid_t pid); 成功:0;失败:-1,设置errno 如果pid = 0,那么该函数作用和getpgrp一样。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { pid_t pid; if ((pid = fork()) < 0) { perror("fork"); exit(1); } else if (pid == 0) { printf("child process PID == %d\n", getpid()); //子进程ID printf("child Group ID == %d\n", getpgrp()); //子进程所在组ID printf("child Group ID == %d\n", getpgid(0)); //传0表当前进程 printf("child Group ID == %d\n", getpgid(getpid())); exit(0); } sleep(3); printf("parent process PID == %d\n", getpid()); printf("parent Group ID is %d\n", getpgrp()); return 0; }
setpgid函数
改变进程默认所属的进程组。通常可用来加入一个现有的进程组或创建一个新进程组。 int setpgid(pid_t pid, pid_t pgid); 成功:0;失败:-1,设置errno 将参1对应的进程,加入参2对应的进程组中。 注意: 1. 如改变子进程为新的组,应fork后,exec前。 2. 权级问题。非root进程只能改变自己创建的子进程,或有权限操作的进程
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { pid_t pid; if ((pid = fork()) < 0) { perror("fork"); exit(1); } else if (pid == 0) { printf("child PID == %d\n",getpid()); printf("child Group ID == %d\n",getpgid(0)); // 返回组id //printf("child Group ID == %d\n",getpgrp()); // 返回组id sleep(7); printf("----Group ID of child is changed to %d\n",getpgid(0)); exit(0); } else if (pid > 0) { sleep(1); setpgid(pid,pid); //让子进程自立门户,成为进程组组长,以它的pid为进程组id sleep(13); printf("\n"); printf("parent PID == %d\n", getpid()); printf("parent's parent process PID == %d\n", getppid()); printf("parent Group ID == %d\n", getpgid(0)); sleep(5); setpgid(getpid(),getppid()); // 改变父进程的组id为父进程的父进程 printf("\n----Group ID of parent is changed to %d\n",getpgid(0)); while(1); } return 0; }
创建会话
创建一个会话需要注意以下6点注意事项: 1. 调用进程不能是进程组组长,该进程变成新会话首进程(session header) 2. 该进程成为一个新进程组的组长进程。 3. 需有root权限(ubuntu不需要) 4. 新会话丢弃原有的控制终端,该会话没有控制终端 5. 该调用进程是组长进程,则出错返回 6. 建立新会话时,先调用fork, 父进程终止,子进程调用setsid
getsid函数
获取进程所属的会话ID pid_t getsid(pid_t pid); 成功:返回调用进程的会话ID;失败:-1,设置errno pid为0表示察看当前进程session ID ps ajx命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。 组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。 setsid函数 创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID。 pid_t setsid(void); 成功:返回调用进程的会话ID;失败:-1,设置errno 调用了setsid函数的进程,既是新的会长,也是新的组长。
#include<stdio.h> #include<unistd.h> #include<stdlib.h> int main() { pid_t pid; if((pid = fork())<0){ perror(“fork”); exit(1); } else if(pid == 0) { printf(“child process PID is %d\n”,getpid()); printf(“Group id of child is %d\n”,getpgid(0)); printf(“session id of child is %d\n”,getsid(0)); sleep(10); setsid(); printf(“changed :\n”); printf(“child process PID is %d\n”,getpid()); printf(“Group id of child is %d\n”,getpgid(0)); printf(“session id of child is %d\n”,getsid(0)); sleep(20); exit(0); } return 0; }
守护进程
Daemon(精灵)进程,是Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互。不受用户登录、注销的影响,一直在运行着,他们都是守护进程。如:预读入缓输出机制的实现;ftp服务器;nfs服务器等。
创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。
创建守护进程模型1)fork()创建子进程,父进程exit()退出 这是创建守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离,在后台工作。 2)在子进程中调用 setsid() 函数创建新的会话 在调用了fork()函数后,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开来,而 setsid() 函数能够使进程完全独立出来。 3)再次 fork() 一个子进程并让父进程退出 现在,进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端,可以通过 fork() 一个子进程,该子进程不是会话首进程,该进程将不能重新打开控制终端。退出父进程。 4)在子进程中调用 chdir() 函数,让根目录 ”/” 成为子进程的工作目录 这一步也是必要的步骤。使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让"/"作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir。 5)在子进程中调用 umask() 函数,设置进程的文件权限掩码为0 文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。 6)在子进程中关闭任何不需要的文件描述符 同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。 在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。 7)守护进程退出处理 当用户需要外部停止守护进程运行时,往往会使用 kill 命令停止该守护进程。所以,守护进程中需要编码来实现 kill 发出的signal信号处理,达到进程的正常退出。
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <unistd.h> #include <string.h> #include <strings.h> #include <errno.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <fcntl.h> int main(int argc, const char *argv[]) { pid_t pid; pid = fork(); if( pid < 0 ) { printf(" Error fork\n"); exit(1); } else if (pid > 0) { //父进程退出 exit(0); } setsid(); chdir("/tmp"); umask(0); int i; for(i=0; i< getdtablesize(); i++) { close(i); } //这时创建完守护进程,下面开始进入守护进程工作 char buf[64] = "This is a Daemon1\n"; int fp; while(1) { if((fp = open("daemon.txt",O_WRONLY | O_CREAT | O_APPEND, 0600)) < 0 ) { printf(" fail open \n"); exit(1); } write(fp, buf, strlen(buf)); close(fp); sleep(20); } exit(0); }
-
Linux C/C++编程之(十九)进程组守护进程
2020-07-10 08:58:04BSD于1980年前后向Unix中增加的一个新特性,代表一个或多个进程的集合,每个进程都属于一个进程组。在waitpid函数和kill函数的参数中都曾使用到。操作系统设计的进程组的概念,是为了简化对多个进程的管理。 当父...一、概述
二、进程组与守护进程
1. 进程组
进程组,也称之为作业。BSD于1980年前后向Unix中增加的一个新特性,代表一个或多个进程的集合,每个进程都属于一个进程组。在waitpid函数和kill函数的参数中都曾使用到。操作系统设计的进程组的概念,是为了简化对多个进程的管理。
当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID第一个进程ID(组长进程)。所以,组长进程标识:**其进程组ID其进程ID**
组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。可以使用
kill -SIGKILL -进程组ID(负的)
来将整个进程组内的进程全部杀死。进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。
一个进程可以为自己或子进程设置进程组ID
2. 进程组操作函数
1)getpgrp函数- 获取当前进程的进程组ID
- 总是返回调用者的进程组ID
2)getpgid函数
- 获取指定进程的进程组ID
- 成功:返回进程组ID;失败:-1,设置errno
3)setpgid函数
- 改变进程默认所属的进程组。通常可用来加入一个现有的进程组或创建一个新进程组。
注意:
- 如改变子进程为新的组,应fork后,exec前。
- 权级问题。非root进程只能改变自己创建的子进程,或有权限操作的进程
3. 会话
1)创建会话
创建一个会话需要注意以下6点注意事项:
a. 调用进程不能是进程组组长,该进程变成新会话首进程(session header)
b. 该进程成为一个新进程组的组长进程。
c. 需有root权限(ubuntu不需要)
d. 新会话丢弃原有的控制终端,该会话没有控制终端
e. 该调用进程是组长进程,则出错返回
f. 建立新会话时,先调用fork, 父进程终止,子进程调用setsid2)getsid函数
获取进程所属的会话ID
- pid为0表示察看当前进程session ID
- ps ajx命令查看系统中的进程
- 参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,
- 参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,
- 参数j表示列出与作业控制相关的信息。
组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。
3)setsid函数
创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID。
调用了setsid函数的进程,既是新的会长,也是新的组长。
总结:
- 会话:进程组的更高一级,多个进程对应一个会话。
- 进程组:多个进程在同一个组,第一个进程默认是进程组的组长。
- 创建会话的时候,组长不可以创建,必须是组员创建。
- 创建会话的步骤:创建子进程,父进程去死,子进程自当会长。
4. 守护进程
Daemon(精灵)进程,是Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互。不受用户登录、注销的影响,一直在运行着,他们都是守护进程。如:预读入缓输出机制的实现;ftp服务器;nfs服务器等。
创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。
- 创建守护进程模型:
1)创建子进程,父进程退出
所有工作在子进程中进行,形式上脱离了控制终端
2)在子进程中创建新会话
setsid()函数 使子进程完全独立出来,脱离控制
3)改变当前目录为根目录
chdir()函数 防止占用可卸载的文件系统 也可以换成其它路径
4)重设文件权限掩码
umask()函数 防止继承的文件创建屏蔽字拒绝某些权限 增加守护进程灵活性
5)关闭文件描述符
继承的打开文件不会用到,浪费系统资源,无法卸载 开始执行守护进程核心工作 守护进程退出处理程序模型
总结:
守护进程的步骤:
a. 创建子进程fork b. 父进程退出 c. 子进程当会长 setid d. 切换工作目录 $HOME e. 设置掩码 umask f. 关闭文件描述符 0, 1, 2,为了避免浪费资源 g. 执行核心逻辑 h. 退出
没创建家目录下的log,所以改成了家目录
-
【进程】进程组
2019-03-27 20:51:34(1)进程组,也称之为作业,BSD与1980年前后向UNIX中增加的一个新特性,代表一个或多个进程的集合。每个进程都属于一个进程组,在waitpid函数和kill函数的参数中都曾经使用到,操作系统设计的进程组的概念,是为了...一、进程组
1. 进程组
(1)进程组,也称之为作业,BSD与1980年前后向UNIX中增加的一个新特性,代表一个或多个进程的集合。每个进程都属于一个进程组,在waitpid函数和kill函数的参数中都曾经使用到,操作系统设计的进程组的概念,是为了简化对多个进程的管理。
当父进程创建子进程的时候,默认子进程与父进程属于同一个进程组,进程组ID等于进程组第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID等于其进程ID.
组长进程可以创建一个进程组,创建该进程组的进程,然后终止,只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。
(2)kill发送给进程组
使用 kill -n -pgid 可以将信号 n 发送到进程组 pgid 中的所有进程。例如命令 kill -9 -4115 表示杀死进程组 4115 中的所有进程。
2. getpgid、getpgrp函数原型:pid_t getpgrp(void); pid_t getpgid(pid_t pid);
分析:
- 函数1:获取当前进程的进程组ID
- 函数2:如果pid = 0,那么该函数作用和getpgrp一样。
3. setpgid函数函数原型:改变进程默认所属的进程组,通常可用来加入一个现有的进程组或新进程组。
int setpgid(pid_t pid, pid_t pgid);
分析:将参数1对应的进程,加入参数2对应的进程组中。
注意:
- 如改变子进程为新进程组,用fork后,exec前。
- 权级问题:非root进程只能改变自己创建的子进程,或有权限操作的进程。
4. 测试代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { pid_t pid; if ((pid = fork()) < 0) { perror("fork"); exit(1); } else if (pid == 0) //子进程 { printf("child PID = %d\n", getpid()); printf("child Group ID = %d\n", getpgid(0)); //返回组id sleep(7); printf("-------Group ID of child id change to %d\n", getpgid(0)); exit(0); } else if (pid > 0) //父进程 { sleep(1); setpgid(pid, pid); //让子进程自立门户,成为进程组组长,以它的pid为进程组 id sleep(13); printf("\n"); printf("parent PID = %d\n", getpid()); printf("parent's parent PID = %d\n", getppid()); printf(" parent Group ID = %d\n", getpgid(0)); sleep(5); setpgid(getpid(), getppid()); //改变父进程组id为父进程的父进程 printf("\n-------Group ID of parent is change to %d\n", getpgid(0)); while (1); } return 0; }
输出结果:
二、进程组的应用
1. 实验一:
题目:利用进程扇完成一个小实验。该进程扇有 1 个父进程和 3 个子进程,我们希望达到图 1 中的效果,即将进程 0 (父进程)和进程 1 设置成一组,假设为组 1,将进程 2 和 进程 3 设置成另一个组,假设为组 2. 另外,我们希望进程 0 和进程 2 分别是这两个组的组长。
1. 测试代码:
#include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> int main() { int pid, i; int group1, group2; // 设置父进程(进程 0)为组长 setpgid(getpid(), getpid()); group1 = getpgid(getpid()); for (i = 1; i <= 3; ++i) { pid = fork(); if (pid == 0) child { if (i == 1) { // 如果 group1 根本不存在,就会出问题。 // 比如进程 0 已经运行结束。 setpgid(getpid(), group1); } else if (i == 2) { setpgid(getpid(), getpid()); group2 = getpgid(getpid()); } else if (i == 3) { // 试想如果进程 2 还没运行,进程 3 先运行了, // 这时候 group2 还未进行设置,这里就会有问题。 // 或者进程 2 已经结束,那进程 3 的设置也会失败 setpgid(getpid(), group2); } break; } else if (pid < 0) { perror("fork"); return -1; } } printf("进程 %d, pid: %d -> ppid: %d, pgid: [%d], (%s)\n", i % 4, getpid(), getppid(), getpgid(getpid()), strerror(errno)); while (1) sleep(1); return 0; }
输出结果:
测试代码:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/wait.h> int main(void) { setpgid(getpid(), getpid()); pid_t group1 = getpgid(getpid()); pid_t group2; int i = 0; for(; i < 3; ++i) { pid_t pid = fork(); if(pid < 0) { perror("fork error"); exit(1); } else if(pid > 0) { // parent process if(i == 0) setpgid(pid, group1); if(i == 1) { setpgid(pid, pid); group2 = getpgid(pid); } if(i == 2) setpgid(pid, group2); } else { // child process if(i == 0) setpgid(getpid(), group1); if(i == 1) { setpgid(getpid(), getpid()); group2 = getpgid(getpid()); } if(i == 2) setpgid(getpid(), group2); break; } } printf("pid:%d, ppid:%d, pgid:%d\n", getpid(), getppid(), getpgid(getpid())); for(int i = 0; i < 3; ++i) wait(0); return 0; }
输出结果:
2. 实验二:
题目:利用进程扇完成一个小实验。该进程扇有 1 个父进程和 3 个子进程,我们希望达到图 1 中的效果,即将进程 0 (父进程)和进程 1 设置成一组,假设为组 1,将进程 2 和 进程 3 设置成另一个组,假设为组 2. 另外,我们希望进程 0 和进程 2 分别是这两个组的组长。
测试代码:
#include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> int main() { int pid, i; int group1, group2; setpgid(getpid(), getpid()); group1 = getpgid(getpid()); for (i = 0; i < 3; ++i) { pid = fork(); if (pid > 0) //父进程 { if (i == 0) { setpgid(pid, pid); group2 = getpgid(pid); } else if (i == 1) { setpgid(pid, group1); } else if (i == 2) { setpgid(pid, group2); } break; } else if (pid == 0) //子进程 { if (i == 0) { setpgid(getpid(), getpid()); group2 = getpgid(getpid()); } else if (i == 1) { setpgid(getpid(), group1); } else if (i == 2) { setpgid(getpid(), group2); } } else if (pid < 0) { perror("fork"); return -1; } } printf("进程 %d, pid: %d -> ppid: %d, pgid: [%d]\n", i, getpid(), getppid(), getpgid(getpid())); while(1) sleep(1); return 0; }
-
关于Redis的特性
2021-01-24 19:34:12属于小白的一些对redis的理解 1、Redis是什么? 以我的理解:redis就是用C语言写的一种高速的缓存数据库,能够存储多种数据类型的数据库。 2、Redis 的特点: 使用C/S模型, 单进程单进程模型, 存储多种数据类型... -
进程组、会话、守护进程
2019-05-28 12:55:26进程组 概念和特性 进程组,也称之为作业。BSD于1980年前后向Unix中增加... 当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID... -
Linux之进程组、守护进程
2018-09-17 21:13:08进程组 概念和特性 进程组,也称之为作业。BSD于1980年前后向Unix中增加的一...当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组... -
进程、进程组、会话以及守护进程
2013-09-29 15:28:181.进程、进程组与会话关系及其特性。 (1) 每个进程都有一个进程ID唯一识别(pid_t pid=getpid()); (2) 每个进程都属于一个进程组(getpgrp()),进程组属于一个会话(getsid(getpid())),是子集关系,即包含与被... -
Linux系统编程 —— 进程组、会话、守护进程
2020-05-02 21:05:521 进程组 1.1 概念和特性 进程组,也称之为作业。BSD于1980年前后向Unix中增加的一个...当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:... -
Linux C编程之十三 进程组、守护进程
2019-05-14 12:17:00一、整体大纲 二、进程组与守护进程 1. 进程组 概念和特性 进程组,也称之为作业。BSD于1980年前后向Unix中增加的一个新特性。... 当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组I... -
【转】Linux查看进程的命名空间
2019-08-26 10:25:29机制介绍 Linux的命名空间机制提供了一种资源隔离的解决方案。PID,IPC,Network等系统资源不再是全局性的,而是属于特定的Namespace。...不同Container内的进程属于不同的Namespace,彼此透明,互不干扰。 Nam... -
Linux学习总结(十五)进程组,会话,守护进程
2021-01-15 12:26:00进程组,也称之为作业,BSD于1980年前后向unix中增加的一个新特性,代表一个或多个进程的集合,每个进程都属于一个进程组,在waitpid函数和kill函数的参数中都曾使用导,操作系统设计的进程组的概念,是为了简化对多... -
进程组(作业)
2019-04-17 00:31:30(1)概念和特性 进程组,也称之为作业。BSD于1980年前后向Unix中...当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组的ID就是该进程组组长的ID,也就是父进程的ID。一个进程只有一个进程组... -
操作系统——进程
2021-01-04 18:33:12在多道操作系统中,程序的执行属于并发执行,此时他们将失去其封闭性,并具有间断性,以及其运行结果不可再现的特性,为能使程序并发执行,并且可以对并发执行的程序加一描述和控制,就需要引入进程这个概念。... -
linux环境编程-【进程组与会话】
2020-10-14 10:59:25当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组ID==第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID==其进程ID【本人实测】 可以使用kill -SIGKILL -进程组ID(负的)来将整个进 -
Linux进程的管理与调度(二)-- Linux的命名空间详解
2017-11-09 22:33:56PID,IPC,Network等系统资源不再是全局性的,而是属于特定的Namespace。每个Namespace里面的资源对其他Namespace都是透明的。要创建新的Namespace,只需要在调用clone时指定相应的flag。Linux Namespaces机制为实现... -
操作系统的基本特性(学习整理)
2018-03-11 19:30:44操作系统的基本特性并发(Concurrence)并行性:两个或多个事件在同一时刻发生并发性:两个或多... 未引入进程的系统(顺序执行):IO程序—计算程序—IO程序即在属于同一个应用程序的计算程序和IO程序之间只能一个... -
进程间通信—匿名管道
2017-09-21 22:05:55真的是有管道的特性。 匿名管道,实际上实在内核中有一个固定大小的缓冲区,这个缓冲区的大小可以看头文件也可以自己测试。 特性: 匿名管道只适用与有血缘关系的两个进程。 匿名管道的通信方式属于半双工,... -
erlang杂记三 ---- 进程
2013-05-17 10:27:391.特性:在erlang里,进程属于语言特性而非操作系统。 创建和销毁进程非常迅速;在两个进程之间收发消息非常迅捷;进程在所有操作系统上行为相同,可以创建大量进程;进程之间不共享任何数据,彼此完全独立;进程间... -
多进程并发编程----进程间传递文件描述符基础~发送接收附属数据函数介绍
2016-04-15 16:59:34如果fork进程之后,子进程如果想要传递描述给父进程一般使用socketpair函数。 此篇文章不介绍socketpair函数,二是介绍sendmsg和recvmsg...从概念上说,sendmsg函数是所有写入函数的基础,而他是从属于套接口的。下面 -
Linux进程通信之管道和FIFO
2013-07-20 16:51:23Linux进程间的通信可以简称为IPC(Interprocess Communication),前面说过的 Linux的同步工具也是属于IPC的一部分,这里我想说的是通常意义的进程间的实际数据通。 1管道 管道是最早的UNIX IPC,所有的UNIX系统... -
操作系统:进程学习笔记
2020-04-02 11:01:17程序顺序执行的三大特性: 1. 顺序性:指处理机严格按照程序所规定的的顺序执行。 2. 封闭性:指程序在封闭的环境运行即程序运行时独占全机资源,资源状态只能有本程序才能够改变它,程序一旦执行,其运行结果不受... -
UNIX网络编程 第2卷 进程间通信
2013-12-30 13:22:02这些实现能够把许多不同的特性联结在一起(例如有一个Posix信号灯的实现同时用上了互斥锁、条件变量和内存映射I/O),并强调了在我们的应用程序中必须经常处理的条件 (例如竞争状态、出错处理、内存空间遗漏、可变... -
个人学习之使用Hexo & Github,搭建属于自己的博客
2016-08-29 15:18:59经过在网上各种找资料,踩过各种坑,终于搭建好了hexo,直接与gitbub一起使用,搭建起自己的免费的博客...Hexo基于Node.js,支持多进程,几百篇文章也可以秒生成。 流畅的撰写 支持GitHub Flavored Markdown和所有O... -
-
JavaSE复习之十二 高级特性:并发 (多线程)
2018-03-25 22:34:59线程和进程的概念线程:进程中的一个执行流程,线程属于某个进程,进程中的多个线程共享进程的内存。进程:内存中运行的应用程序,每个进程独享一片内存空间,一个进程可启动多个线程。 一个java应用总是从main()... -
Centos7新特性——systemd取代init管理服务
2019-10-07 12:38:46一、Systemd 简介 首先 systmed 是一个用户空间的程序,属于应用程序,不属于 Linux 内核范畴...Linux内核加载启动后,用户空间的第一个进程就是初始化进程,这个程序的物理文件约定位于/sbin/init,当然也可以通过... -
PgSQL · 应用案例 · 阿里云 RDS PostgreSQL 高并发特性 vs 社区版本
2018-05-22 09:00:19背景 进程模型数据库,需要为每个会话指派独立的进程与之服务,在连接数非常多,且大都是活跃...PostgreSQL与Oracle Dedicate Server一样,属于进程模型。在非常高并发的情况下,性能会下降比较厉害,通常社区版本可...