接上一篇:linux_共享存储映射区-mmap函数-munmap函数-进程通信-strace命令
本次来分享更多的进程知识相关学习,有ttyname函数、getpgrp函数、getpgid函数、setpgid函数、getsid函数、setsid函数和守护进程,主要就是进程组的学习和会话的学习,话不多说,上菜:
此博主在CSDN发布的文章目录:【我的CSDN目录,作为博主在CSDN上发布的文章类型导读】
目录
1.ttyname函数
函数作用:
由文件描述符查出对应的文件名
头文件:
#include <unistd.h>
函数原型:
char *ttyname(int fd);
函数参数:
fd:文件描述符
返回值:
成功:终端名;
失败:NULL,设置errno
下面我们借助ttyname函数,通过实验看一下各种不同的终端所对应的设备文件名。
#include <unistd.h>
#include <stdio.h>
int main(void)
{
printf("fd 0: %s\n", ttyname(0));
printf("fd 1: %s\n", ttyname(1));
printf("fd 2: %s\n", ttyname(2));
return 0;
}
2.进程组操作函数
2.1.getpgrp函数
函数作用:
获取当前进程的进程组ID
头文件:
#include <unistd.h>
函数原型:
pid_t getpgrp(void);
函数参数:
无
返回值:
返回调用者的进程组ID。
2.2.getpgid函数
函数作用:
获取指定进程的进程组ID。
头文件:
#include <unistd.h>
函数原型:
pid_t getpgid(pid_t pid);
函数参数:
pid:进程id,如果pid = 0,那么该函数作用和getpgrp一样。
返回值:
成功:0;
失败:-1,设置errno
2.3.例子-getpgrp函数-getpgid函数
#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("当前子进程id == %d\n", getpid()); //子进程ID
printf("当前子进程组-getpgrp-id ID == %d\n", getpgrp()); //子进程所在组ID,传0表当前进程
printf("当前子进程组-getpgid-id ID == %d\n", getpgid(0));
printf("当前子进程组-getpgid(getpid())- ID == %d\n", getpgid(getpid()));
exit(0);
}
sleep(3);
printf("父进程 ID == %d\n", getpid());
printf("父进程组 ID == %d\n", getpgrp());
return 0;
}
2.4.setpgid函数
函数作用:
改变进程默认所属的进程组。通常可用来加入一个现有的进程组或创建一个新进程组。
头文件:
#include <unistd.h>
函数原型:
int setpgid(pid_t pid, pid_t pgid);
函数参数:
pid:进程id。
pgid:进程组id。
即将参1对应的进程,加入参2对应的进程组中。
返回值:
成功:0;
失败:-1,设置errno
注意:
1、如改变子进程为新的组,应fork后,exec前。
2、权级问题。非root进程只能改变自己创建的子进程,或有权限操作的进程
2.5.例子-setpgid函数
#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("子进程 ID == %d\n",getpid());
printf("子进程组 ID == %d\n",getpgid(0)); // 返回组id
//printf("child Group ID == %d\n",getpgrp()); // 返回组id
sleep(5);
printf("----子进程组ID被改变后的组 ID == %d\n",getpgid(0));
exit(0);
}
else if (pid > 0) {
sleep(1);
setpgid(pid,pid); //让子进程自立门户,成为进程组组长,以它的pid为进程组id
sleep(6);
printf("\n");
printf("父进程 ID == %d\n", getpid());
printf("父进程的父进程 ID == %d\n", getppid());
printf("父进程组 ID == %d\n", getpgid(0));
sleep(5);
setpgid(getpid(),getppid()); // 改变父进程的组id为父进程的父进程
printf("\n----父进程组ID被改变后的组 ID == %d\n",getpgid(0));
//若是用ctrl-c杀不死,就用kill命令杀死 kill -9 进程ID
while(1);
}
return 0;
}
3.会话
Linux session 一般是指 shell session。Shell session 是终端中当前的状态,在终端中只能有一个 session。当我们打开一个新的终端时,总会创建一个新的 shell session。这表明会话是我们和shell交互的一个过程。 就进程间的关系来说,session 由一个或多个进程组组成。
3.1.创建会话
创建一个会话需要注意以下6点注意事项:
1、调用进程不能是进程组组长,该进程变成新会话首进程(session header)
2、该进程成为一个新进程组的组长进程。
3、需有root权限(ubuntu不需要)
4、新会话丢弃原有的控制终端,该会话没有控制终端
5、该调用进程是组长进程,则出错返回
6、建立新会话时,先调用fork, 父进程终止,子进程调用setsid
3.2.getsid函数
函数作用:
获取进程所属的会话ID
头文件:
#include <unistd.h>
函数原型:
pid_t getsid(pid_t pid);
函数参数:
pid:进程id,为0表示察看当前进程session ID。
返回值:
成功:返回调用进程的会话ID;
失败:-1,设置errno
ps ajx
命令查看系统中的进程。
参数a表示不仅列当前用户的进程,也列出所有其他用户的进程;
参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程;
参数j表示列出与作业控制相关的信息。
组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。
3.3.setsid函数
函数作用:
创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID。
头文件:
#include <unistd.h>
函数原型:
pid_t setsid(void);
函数参数:
无。
返回值:
成功:返回调用进程的会话ID;
失败:-1,设置errno
注意:
调用了setsid函数的进程,既是新的会长,也是新的组长。
3.4.例子:
#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("子进程ID == %d\n", getpid());
printf("子进程组ID == %d\n", getpgid(0));
printf("子进程会话ID == %d\n", getsid(0));
sleep(8);
setsid(); //子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程
printf("改变后:\n");
printf("子进程ID == %d\n", getpid());
printf("子进程组ID == %d\n", getpgid(0));
printf("子进程会话ID == %d\n", getsid(0));
sleep(10);
exit(0);
}
while(1);
return 0;
}
4.守护进程
Daemon(精灵)进程,是Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互。不受用户登录、注销的影响,一直在运行着,他们都是守护进程。如:预读入缓输出机制的实现;ftp服务器;nfs服务器等。
创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。
4.1.创建守护进程模型
1、创建子进程,父进程退出
所有工作在子进程中进行形式上脱离了控制终端
2、在子进程中创建新会话
setsid()函数
使子进程完全独立出来,脱离控制
3、改变当前目录为根目录
chdir()函数
防止占用可卸载的文件系统
也可以换成其它路径
4、重设文件权限掩码
umask()函数
防止继承的文件创建屏蔽字拒绝某些权限
增加守护进程灵活性
5、关闭文件描述符
继承的打开文件不会用到,浪费系统资源,无法卸载
6、开始执行守护进程核心工作
7、守护进程退出处理程序模型
4.2.例子:
#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); /* 在此循环中可以实现守护进程的核心工作 */
}
以上就是本次的分享了,希望对大家有所帮助,欢迎关注博主一起学习更多的新知识!