守护进程
1.守护进程的特点:
(1).守护进程是Linux三种类型之一
(2).通常在系统启动时运行.系统关闭的结束,
(3).始终运行在后台
(4).独立于任何终端(和终端无关)
(5).周期性的执行某种任何或者等待处理特定事件
2.进程的管理
Linux以会话(session),进程组的形式管理进程
每个进程属于一个进程组,子进程属于该进程组
会话又是一个或者多个进程组的集合,通常(shell进程)用户打开一个终端,系统就会创建一个会话,所有通过该终端运行的进程都属于这个会话
shell进程---shell进程是该会话的首进程,也称为该会话组的组长
终端 -----控制终端
一个会话最多打开一个控制终端,当控制终端关闭时,所有的进程也会被结束
3.守护进程的创建
1.创建子进程,父进程退出
子进程称为孤儿进程,被init进程收养
子进程运行在后台
子进程仍然属于该终端的会话组
if(fork()>0)
{
exit(0);
}
2.子进程创建新的会话,
子进程称为新会话组的组长,子进程不在属于原来的会话,脱离原来的终端
#include <unistd.h>
返回值:若小于0,创建失败
pid_t setsid(void);
if(setsid()<0){
exit(-1)
}
3.更改当前目录
#include <unistd.h>
int chdir(const char *path);
chdir(“/”) -----根目录
chdir(“/tmp”) ----777
两种目录权限不一样,守护进程会一直在后台运行,其工作目录不能为删除
4.重设文件权限掩码
文件权限掩码设置为0,只影响当前进程创建的文件
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);
if(umask(0)<0)
{
exit(-1);
}
5.关闭从父进程继承的打开的文件描述符
#include <unistd.h>
int getdtablesize(void); -----获得打开进程的最大文件个数
----已脱离终端,stdin/stdout/stderr无法在使用
int i=0;
for(i=0;i<getdtablesize();i++)
{
close(i);
}
每个1秒将系统时间写入time.log
#include <stdio.h>
#include "guard.h"
int main(int argc, const char *argv[])
{
//1.创建子进程,退出父进程
if(fork()>0)
{
exit(0);
}
//2.让子进程创建新的会话,脱离原来的会话
if(setsid<0)
{
exit(-1);
}
//3.更改当前目录
chdir("/tmp");
//4.重设文件权限掩码
if(umask(0)<0)
{
exit(-1);
}
//5.关闭从父进程继承的打开的文件描述符
int i;
for(i=0;i<getdtablesize();i++)
{
close(i);
}
//6.打开文件
FILE *fa=fopen("time.log","a");
if(NULL==fa)
{
perror("open error");
return -1;
}
//7.写入文件
time_t mytime;
struct tm *pt=NULL;
while(1)
{
time(&mytime);
pt=localtime(&mytime);
fprintf(fa,"%04d/%02d/%02d %02d:%02d:%02d\n",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,pt->tm_hour,pt->tm_min,pt->tm_sec);
//刷新缓冲区
fflush(fa);
sleep(1);
}
//8.关闭文件
fclose(fa);
return 0;
}
进程间的通信
1.分类
早期unix进程间通信方式:POSIX提供的通信方式
无名管道:
有名管道:
信号:
systemV是当年非常流行的标准:
消息队列
信号量
共享内存
POSIX相对于systemV是比较新的标准,语法相对于简单
2.通信方式的各自特点
无名管道:速度慢,容量有限,只能用于父子间的通信,单工的,数据存在内存中
有名管道:任何进程间的通信,双工,有文件名,但速度慢
信号:唯一异步通信方式
消息队列:常用于cs模式中,按消息类型访问,
共享内存:效率最高,但是需要同步,互斥机制
信号量:不能传递复杂消息,一般和共享内存配合使用,使用同步
3.无名管道
1.特点:
- 只能用于具有亲缘关系的进程之间的通信
- 单工的通信方式,具有固定的读端和写端
- 无名管道创建时会返回两个文件描述符,分别用于读管道和写管道
- 当管道中无数据时,读操作会阻塞,当管道中无空间时,写操作会阻塞.
- 当读端不存在,写管道,会出现管道断裂
2.案例:实现父子进程间的通信
父进程:
- 创建管道 ------读端和写端
- 创建子进程 ------继承管道的读端和写端
父进程:关闭写端
读取数据
通信结束,关闭读端
子进程:关闭读端
写入数据
通信结束,关闭写端
使用的函数
#include <unistd.h>
pipefd[0]----读管道
pipefd[1]----写管道
返回值:成功返回0,失败返回EOF
int pipe(int pipefd[2]);
无名管道在内存中存在,在OS下不可见,容量有限
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
//实现父子进程间的通信
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
//1.创建管道
int fd[2];
if(pipe(fd)<0)
{
perror("pipe error");
return -1;
}
//2.创建子进程,继承管道的读端和写端
pid_t pid;
pid=fork();
if(pid<0)
{
perror("fork error");
return -1;
}
//3.父进程:关闭写端;读取数据;通信结束,关闭读端
//4.子进程:关闭读端;写入数据;通信结束,关闭写端
else if(0==pid)
{
close(fd[0]);
write(fd[1],"this is a son process",20);
close(fd[1]);
}
else
{
close(fd[1]);
char buf[20];
read(fd[0],buf,20);
printf("buf=%s\n",buf);
}
return 0;
}
3.获得无名管道的大小
//获得无名管道的大小
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
//1.创建无名管道
int fd[2];
if(pipe(fd)<0)
{
perror("pipe error");
return -1;
}
//2.重复写入,塞满管道,每次写入1024字节(1K)
char buf[1024]={'\0'};
int count=0;
int n=0;
while(1)
{
n+=write(fd[1],buf,1024);
count++;
printf("%d %dk\n",n,count);
}
return 0;
}
4.无名管道使用时需要注意的问题
只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号(通常Broken pipe错误)。
5.验证管道是否断裂:
//验证无名管道炸裂
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
//1.创建无名管道
int fd[2];
if(pipe(fd)<0)
{
perror("pipe error");
return -1;
}
//2.关闭读端
close(fd[0]);
//3.创建子进程,子进程写入数据
pid_t pid;
pid=fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(0==pid)
{
printf("this is a son process");
write(fd[1],"this a son process",20);
exit(0);
}
//4.父进程等待子进程结束,wait();----->通过宏判断是否信号结束,若结束,判断该信号是谁?
else
{
int status;
wait(&status);
if(WIFSIGNALED(status))
{
printf("%#x\n",WTERMSIG(status));
printf("%d\n",WTERMSIG(status));
}
}
return 0;
}
4.有名管道:
1.特点:
有名管道可以使互不相关的两个进程进行通信
打开管道时可指定读写方式
在文件系统可见,可以使用文件IO进行操作,严格遵循先入先出FIFO,无法使用lseek
当管道中无数据时,读操作阻塞,当管道中无空间时,写操作阻塞,通信结束,管道被清空
2.使用的函数:
#include <sys/types.h>
#include <sys/stat.h>
参数1:有名管道的管道名,在OS可见
参数2:创建方式 八进制
返回值:成功返回0,失败返回-1, 失败时若错误原因不是EEXIST,认为mkfifo(error)
int mkfifo(const char *pathname, mode_t mode);
3.案例:进程间的通信
//单工通信的有名管道
#include <stdio.h>
#include "fifo.h"
#include <errno.h>
int main(int argc, const char *argv[])
{
//1.创建有名管道
if(mkfifo(FIFO,0666)<0&&errno!=EEXIST)
{
perror("fifo error");
return -1;
}
//2.以只读方式打开管道文件
int fd=0;
fd=open(FIFO,O_RDONLY);
if(0>fd)
{
perror("opne error");
return -1;
}
//3.读取管道
char buf[20]={'\0'};
while(1)
{
memset(buf,'\0',sizeof(buf));
printf("read:\n");
read(fd,buf,sizeof(buf));
//4.显示输出
printf("%s\n",buf);
}
//5.关闭管道
close(fd);
return 0;
}
//单工通信的有名管道
#include <stdio.h>
#include "fifo.h"
#include <errno.h>
int main(int argc, const char *argv[])
{
//1.创建有名管道
if(mkfifo(FIFO,0666)<0&&errno!=EEXIST)
{
perror("fifo error");
return -1;
}
//2.以只写方式打开管道文件
int fd=0;
fd=open(FIFO,O_WRONLY);
if(0>fd)
{
perror("opne error");
return -1;
}
//3.写入管道
char buf[20]={'\0'};
while(1)
{
printf("write:");
scanf("%s",buf);
write(fd,buf,sizeof(buf));
}
//5.关闭管道
close(fd);
return 0;
}
编写一子双工通信
//进程双工通信
#include <stdio.h>
#include "fifo.h"
#include <errno.h>
int main(int argc, const char *argv[])
{
//进程A
//1.创建有名管道
if(mkfifo(FIFI,0666)<0 && errno!=EEXIST)
{
perror("mkfifo error");
return -1;
}
if(mkfifo(FIFO,0666)<0 && errno!=EEXIST)
{
perror("mkfifi error");
return -1;
}
//写入管道文件
int fw=0;
fw=open(FIFI,O_WRONLY);
if(fw<0)
{
perror("open error");
return -1;
}
//读入管道文件
int fr=0;
fr=open(FIFO,O_RDONLY);
if(fr<0)
{
perror("open error");
return -1;
}
char buf[20]={'\0'};
while(1)
{
memset(buf,'\0',sizeof(buf));
printf("A:");
scanf("%s",buf);
write(fw,buf,sizeof(buf));
printf("B:\n");
read(fr,buf,sizeof(buf));
printf("%s\n",buf);
}
close(fr);
close(fw);
return 0;
}
//进程双工通信
#include <stdio.h>
#include "fifo.h"
#include <errno.h>
int main(int argc, const char *argv[])
{
//进程B
//1.创建有名管道
if(mkfifo(FIFI,0666)<0 && errno!=EEXIST)
{
perror("mkfifo error");
return -1;
}
if(mkfifo(FIFO,0666)<0 && errno!=EEXIST)
{
perror("mkfifo error");
return -1;
}
//读入管道文件
int fr=0;
fr=open(FIFI,O_RDONLY);
if(fr<0)
{
perror("open error");
return -1;
}
//写入文件
int fw=0;
fw=open(FIFO,O_WRONLY);
if(fw<0)
{
perror("open error");
return -1;
}
char buf[20]={'\0'};
while(1)
{
printf("A:\n");
read(fr,buf,sizeof(buf));
printf("%s\n",buf);
printf("B:\n");
scanf("%s",buf);
write(fw,buf,sizeof(buf));
}
close(fr);
close(fw);
return 0;
}
信号:
同步:发送方发送数据,等待接受方响应之后才发下一个数据包的通讯方式
异步:发送方发送数据,不等待接送方发回相应,接着发送下一个数据包的通讯方式
1.特点:
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
2.基本概念:
发送信号:产生信号的方式有很多种,
(1).一个进程可以给一个进程发送一个特定信号, kill -9 进程ID号
(2).内核可以向用户进程发送信号 管道断裂
(3).组合键
ctrl + c--- SIGINT ------>终止进程
ctrl+z ----SIGSTOP----->停止进程
ctrl+\ ---SIFQUIT------->终止前台进程并生成 core 文件
(4).自己可以给自己发送信号 raise()
捕获信号:
安装信号:获得该信号之后,不让他执行默认的操作,可以去执行别的程序,
但有两个信号是不能被安装也不能被屏蔽,SIGSTOP SIGKILL(杀死给定的进程或程序)
3.使用的函数:
#include <signal.h>
int raise(int sig);
WIFEXITED(status) 判断子进程是否正常结束
WEXITSTATUS(status) 获得子进程返回值
WIFSIGNALED(status) 判断子进程是否被信号结束
WTERMSIG(status) 判读结束子进程的信号类型
//测试raise(自己给自己发信号)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{
//1.创建子进程
pid_t pid;
pid=fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(0==pid)
{
raise(SIGKILL);
printf("this is a son process");
exit(0);
}
else
{
//父进程
int status;
wait(&status);
if(WIFSIGNALED(status))
{
printf("%d\n",WTERMSIG(status));
}
}
return 0;
}
#include <unistd.h>
参数1:秒数
时间到了之后内核会给程序发送SIGALRM(终止当前进程)
unsigned int alarm(unsigned int seconds);
//闹钟函数alarm
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
alarm(2);
while(1)
{
printf("time is up");
sleep(1);
}
return 0;
}
#include <signal.h>
typedef void (*sighandler_t)(int);
参数1是要接收到信号的id
参数2:
(1).忽略该信号 SIG_IGN
(2). 使用默认处理方式 SIG_DFL
(3). 捕获处理(安装信号)
sighandler_t signal(int signum, sighandler_t handler);
捕获信号(安装信号)
接收到的信号之后,对信号进行处理:
//信号捕获处理函数signal
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
alarm(2);//闹钟函数
signal(SIGALRM,SIG_IGN);
while(1)
{
printf("%d\n",getpid());//获得当前进程号
printf("time is up\n");
sleep(1);
}
return 0;
}