[Linux C编程]有名管道实现守护进程

时间:2022-07-13 15:10:31

有名管道实现守护进程

fork()函数创建子进程时,当父进程在运行时,终端都会被阻塞(即不能对当前终端进行任何操作,除非结束父进程);然而,只要父进程结束,不管子进程是否结束,终端都会解除阻塞(即在子进程运行的同时,当前终端也可以进行其他操作)。

 

守护进程:是始于系统开始运行,结束于系统结束,不受终端和用户登录、登出的影响,它是在后台运行的进程。在Linux下使用ps -s命令查看时,守护进程的TTY的一项是“?”。

 

创建守护进程的步骤为:

(1)在后台运行。使用fork()函数创建子进程,使其父进程结束,子进程运行。

(2)脱离控制终端,登录会话和进程组。使用setsid()函数(man setsid查看更详细的细节),将子进程建立新的会话组,并担任会话组长。

(3)禁止进程重新打开控制终端。由于会话组长拥有打开控制终端的权限,所以再次使用fork()函数,结束第一子进程(现在的父进程),继续第二子进程(不是会话组长)。
(4)关闭打开的文件描述符。进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:for(i=0;i<sysconf(_SC_OPEN_MAX);i++) {close(i);} 。那么,什么是文件描述符呢?文件描述符是非负整数,打开显存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。习惯上,0表示标准输入,1表示标准输出,2表示标准错误。
(5)改变当前工作目录。进程活动时,其工作目录所在的文件系统是不能卸下的。一般需要将守护进程的工作目录改到根目录。使用chdir("/"),将工作目录改到了根目录。
(6)重设文件创建掩码。进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0); (参数0表示具有所有权限)

(7)处理SIGCHLD信号(软中断)。子进程结束时,会向其父进程发送SIGCHLD信号,父进程可以通过使用signal(SIGCHLD,SIG_IGN);函数来忽略这个信号,将它抛给init进程,init会回收子进程的资源,从而不会产生僵尸进程。

 有名管道:

(1)使两个不相关的进程彼此通信 a:通过路径名指出,在文件系统中可见

                                b:管道建立后,两进程可按普通文件一样对其操作

(2)遵循先进先出规则  a:对管道读从开始处返回数据

                       b:对管道写则把数据添加到末尾

 

 

下面我贴出我的用有名管道实现守护进程的代码,有误欢迎指正:

守护进程部分: 

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>

#define FIFO "/tmp/myfifo"

void init_daemon() //将程序转换成守护进程的函数
{
int pid;
int i;

pid = fork();

if(pid > 0)
{
exit(0); //在后台运行,结束父进程
}
else if(pid < 0)
{
exit(1);
}

setsid(); //脱离控制终端,但是使子进程成为了会话组长,它拥有重新打开终端的权限

pid = fork();

if(pid > 0)
{
exit(0); //禁止进程重新打开控制终端,结束第一子进程(现在的父进程)
}
else if(pid < 0)
{
exit(1);
}

for(i = 0; i < sysconf(_SC_OPEN_MAX); i++) //关闭打开的文件描述符,循环关闭了所有的文件标识符
{
close(i);
}

chdir("/"); //改变当前目录,使进程在根目录下运行

umask(0); //重设文件创建掩码,参数0表示所有权限

signal(SIGCHLD,SIG_IGN); //处理SIGGHLD信号,在本程序中可以不用处理,因为父进程已经关闭掉了

return;

}
int main(int argc,char ** argv)
{
char r_buf[100];

int count = 0;
int fd;
int nread;

init_daemon();

/* 创建管道 */
if((mkfifo(FIFO,O_CREAT|O_EXCL) < 0) && errno != EEXIST)
{
printf("cannot creat fifoserver\n");
}

printf("Preparing for reading bytes...\n");

memset(r_buf,0,sizeof(r_buf));

/* 打开管道 */
fd = open(FIFO,O_RDONLY|O_NONBLOCK,0);

if(fd == -1)
{
perror("open");

exit(1);
}

while(1)
{
memset(r_buf,0,sizeof(r_buf));

if((nread = read(fd,r_buf,100)) == -1)
{
if(errno == EAGAIN)
{
printf("no data yet\n");

}
}

if(nread == 0)
{
count++;
}
else if(nread > 0)
{
count = 0;
}

if(count > 6)
{
system("./write 123"); //重启被守护进程
}

printf("read %s from FIFO\n",r_buf);

sleep(1);
}

pause(); /*暂停,等待信号*/

unlink(FIFO); /*s删除文件*/

return 0;
}


被守护的进程:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>

#define FIFO_SERVER "/tmp/myfifo"

int main(int argc,char ** argv)
{
int fd;
int count = 0;
int nwrite;

char w_buf[100];

/*打开管道*/
fd = open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);

if(argc == 1)
{
printf("please send something!\n");

exit(-1);
}

while(count < 10)
{
memset(w_buf,0,sizeof(w_buf));
strcpy(w_buf,argv[1]);

/* 向管道写入数据 */
if((nwrite = write(fd,w_buf,100)) == -1)
{
if(errno == EAGAIN)
{
printf("The FIFO has not been read yet,Please try later!\n");
}
}
else
{
printf("write %s to the FIFO\n",w_buf);
}

sleep(1);

count++;
}

if(10 == count) //人为使进程挂起
{
exit(1);
}

pause(); /*暂停,等待信号*/

return 0;
}