进程-------->第二天,守护进程、POSIX的unix间通信(无名管道、有名管道、信号)

守护进程

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.特点:

  1. 只能用于具有亲缘关系的进程之间的通信
  2. 单工的通信方式,具有固定的读端和写端
  3. 无名管道创建时会返回两个文件描述符,分别用于读管道和写管道
  4. 当管道中无数据时,读操作会阻塞,当管道中无空间时,写操作会阻塞.
  5. 当读端不存在,写管道,会出现管道断裂

2.案例:实现父子进程间的通信

父进程:

  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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值