系统编程2

=============================================================

系统编程技术点

1、进程的概念、接口函数

2、进程间的通信方式

3、进程间的信号集

4、线程的概念,函数接口,意义

5、线程之间的通信

6、线程池

execl簇函数

我们调用fork函数主要目的是调用外部程序功能,在进行外部程序调用时,一般有两种方法:一种是使用system函数,一种是execl簇函数
1、system 函数
#include <stdlib.h>
int system(const char *command);
command :需要调用的shell命令
2、exec函数族
#include <unistd.h>

extern char **environ;

int execl(const char *path, const char arg, …
/
(char *) NULL */);
int execlp(const char *file, const char arg, …
/
(char *) NULL */);
int execle(const char *path, const char arg, …
/
, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    
    
	pid_t pid;
	pid = fork();
	if(pid < 0)
	{
    
    
		perror("fork failed");
		return -1;
	}
	if(pid > 0)//父进程
	{
    
    
		//空操作
	}
	if(pid == 0)//子进程
	{
    
    
		system("./test hello world ");
		//替换当前的子进程
		//execl("./test", "hello", "world", NULL);
		printf("child pid :%d\n", getpid());
	}
	return 0;
}
进程之间的通信

1, 无名管道
管道是linux系统中的一种特殊文件,数据从写端写入管道,并且只能从读端读出数据:让一个进程从管道的写端写入数据,另一个进程从读端读出数据,他们就实现一个单向通信。
缺点:无名管道可以实现不同进程之间的信息交换,但是只能工作于亲缘进程之间,不能实现任意两个不同进程之间的通信。

	头文件:
	#include <unistd.h>

	函数:
	int pipe(int pipefd[2]);
	
	参数: 2 个成员的int型数组
	在创建无名管道之后,返回两个文件描述符: 读端 :fd[0] ,  写端:fd[1]
	功能:创建一个无名管道,返回这个管道的读端和写端

注:在使用pipe函数时,父进程首先pipe创建无名管道,然后再创建子进程

例:使用无名管道实现父子进程之间的通信。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <strings.h>
int main()
{
    
    
	int pipefd[2];
	pid_t pid;
	//1,创建无名管道
	int ret = pipe(pipefd);
	if(ret == -1)
	{
    
    
		perror("pipe failed");
		return -1;
	}
	//2,创建子进程
	pid = fork();
	if(pid < 0)
	{
    
    
		perror("fork failed");
		return -1;
	}
	if(pid > 0)
	{
    
    
		char buf[20]={
    
    0};
		while(1)
		{
    
    
			bzero(buf, sizeof(buf));
			fgets(buf, sizeof(buf), stdin);
			write(pipefd[1], buf, sizeof(buf));
		}		
	}
	if(pid == 0)
	{
    
    
		while(1)
		{
    
    
			char buf[20] = {
    
    0};
			read(pipefd[0], buf, sizeof(buf));
			printf("[%d] readbuf:%s\n", getpid(), buf);
		}

	}
	return 0;
}

练习:使用无名管道,实现兄弟进程之间的通信。
即父进程创建两个子进程,两个子进程互相通信。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <strings.h>

int main()
{
    
    
	int pipefd[2];
	pid_t pid;
	//1,创建无名管道
	int ret = pipe(pipefd);
	if(ret == -1)
	{
    
    
		perror("pipe failed");
		return -1;
	}
	//2,创建子进程
	pid = fork();
	if(pid < 0)
	{
    
    
		perror("fork failed");
		return -1;
	}
	if(pid > 0)	//父进程执行部分
	{
    
    
		pid_t pid1;
		pid1 = fork();
		if(pid1 == 0)	//子进程2执行内容
		{
    
    
			char buf[20]={
    
    0};
			while(1)
			{
    
    
				bzero(buf, sizeof(buf));
				fgets(buf, sizeof(buf), stdin);
				write(pipefd[1], buf, sizeof(buf));
			}
		}
		if(pid1 > 0)
		{
    
    
			wait(NULL);
			wait(NULL);
		}	
	}
	if(pid == 0)//子进程1执行内容
	{
    
    
		char buf[20] = {
    
    0};
		while(1)
		{
    
    
			bzero(buf, sizeof(buf));
			read(pipefd[0], buf, sizeof(buf));
			printf("[%d] readbuf:%s\n", getpid(), buf);
		}

	}
	return 0;
}
有名管道:FIFO (first in first out)

优点:能实现任意两个不同进程之间的通信。
有名管道实实在在的存在于文件系统中。
ls -l
- :普通文件
d :目录文件
p :管道文件

函数一:
int mkfifo(const char *pathname, mode_t mode);

头文件:
#include <sys/types.h>
#include <sys/stat.h>
参数:
pathname : 文件路径名(需要创建的管道文件)
mode	 : 文件权限 八进制 0777

返回值:成功返回0,失败返回-1;


函数二:
int access(const char *pathname, int mode);(判断文件是否具有某种功能)

头文件:#include <unistd.h>

参数:
pathname :路径名
mode	:权限 F_OK :测试文件是否存在

返回值:
	文件存在,返回0
	文件不存在,返回 -1

练习:
使用access函数和mkfifo函数,在家目录下创建一个管道文件 fifo
如果文件不存在,则创建该文件,如果文件存在,则输出: file exist

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define FIFO_PATH "/home/gec/fifo"
int main()
{
    
    
	//1,判断家目录下的fifo文件是否存在
	int ret0 = access(FIFO_PATH, F_OK);
	if(ret0 == 0)
	{
    
    
		printf("file exist\n");
	}
	else
	{
    
    
		int ret = mkfifo(FIFO_PATH, 0777);
		if(ret == -1)
		{
    
    
			perror("mkfifo failed");
			return -1;
		}
	}
	return 0;
}


家目录: /home/gec 用户所独立拥有的一个目录 ~
(gec 为用户名)

例子:使用有名管道实现两个不同进程之间的数据通信。
需要同时打开两个终端同时运行程序,一个终端发送,另一个终端接收。

发送信息程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#define FIFO_PATH "/tmp/myfifo"
int main()
{
    
    
	//1,判断文件是否存在,先创建一个管道文件
	int ret0 = access(FIFO_PATH, F_OK);
	if(ret0 == 0)
	{
    
    
		printf("file exist\n");
	}
	else
	{
    
    
		int ret = mkfifo(FIFO_PATH, 0777);
		if(ret == -1)
		{
    
    
			perror("mkfifo failed");
			return -1;
		}
	}
	//2,打开管道文件 (读写权限打开)
	int fd = open(FIFO_PATH, O_RDWR);
	if(fd < 0)
	{
    
    
		perror("open fifo failed");
		return -1;
	}
	//3,循环从键盘获取数据,写入管道文件
	char buf[128];
	while(1)
	{
    
    
		bzero(buf, sizeof(buf));
		fgets(buf, sizeof(buf), stdin);
		write(fd, buf, sizeof(buf));
	}	
	return 0;
}

接收信息程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#define FIFO_PATH "/tmp/myfifo"
int main()
{
    
    
	//1,判断文件是否存在,先创建一个管道文件
	int ret0 = access(FIFO_PATH, F_OK);
	if(ret0 == 0)
	{
    
    
		printf("file exist\n");
	}
	else
	{
    
    
		int ret = mkfifo(FIFO_PATH, 0777);
		if(ret == -1)
		{
    
    
			perror("mkfifo failed");
			return -1;
		}
	}
	//2,打开管道文件 (读写权限打开)
	int fd = open(FIFO_PATH, O_RDWR);
	if(fd < 0)
	{
    
    
		perror("open fifo failed");
		return -1;
	}
	//3,循环从管道文件中读取数据,输出
	char buf[128];
	while(1)
	{
    
    
		bzero(buf, sizeof(buf));
		read(fd, buf, sizeof(buf));
		printf("recvbuf:%s", buf);
	}	
	return 0;
}


思考:如何实现双向通信。
提示:使用两个管道
同样需要同时运行程序1和程序2。
程序1:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/wait.h>
#define FIFO_PATH1 "/tmp/myfifo1"
#define FIFO_PATH "/tmp/myfifo"
int main()
{
    
    

	//1,判断文件是否存在,先创建一个管道文件
	int ret0 = access(FIFO_PATH, F_OK);
	if(ret0 == 0)
	{
    
    
		printf("file exist\n");
	}
	else
	{
    
    
		int ret = mkfifo(FIFO_PATH, 0777);
		if(ret == -1)
		{
    
    
			perror("mkfifo failed");
			return -1;
		}
	}
	
	int ret1 = access(FIFO_PATH1, F_OK);
	if(ret1 == 0)
	{
    
    
		printf("file exist\n");
	}
	else
	{
    
    
		int ret1 = mkfifo(FIFO_PATH1, 0777);
		if(ret1 == -1)
		{
    
    
			perror("mkfifo1 failed");
			return -1;
		}
	}
	
	//2,打开管道文件 (读写权限打开)
	int fd = open(FIFO_PATH, O_RDWR);
	int fd1 = open(FIFO_PATH1, O_RDWR);
	if(fd < 0 || fd1 < 0)
	{
    
    
		perror("open fifo failed");
		return -1;
	}
	
	//创建子进程
	pid_t pid = fork();
	if(pid < 0){
    
    
		perror("fork failed \n");
		return -1;
	}
	if(pid > 0){
    
    
		//3,循环从管道文件中写入数据
		char buf[128];
		while(1)
		{
    
    
			bzero(buf, sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			write(fd, buf, sizeof(buf));    //阻塞
		printf("Terminal 2 sendinfo:%s", buf);
		}
		wait(NULL);
	}
	if(pid == 0){
    
    
		//3,循环从管道文件中读取数据
		char buf[128];
		while(1)
		{
    
    
			bzero(buf, sizeof(buf));
			read(fd1, buf, sizeof(buf));   //阻塞
			printf("Terminal 2 recvinfo:%s", buf);
		}
	}
	return 0;
}

程序2:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/wait.h>
#define FIFO_PATH1 "/tmp/myfifo1"
#define FIFO_PATH "/tmp/myfifo"
int main()
{
    
    
	//1,判断文件是否存在,先创建一个管道文件
	int ret0 = access(FIFO_PATH, F_OK);
	if(ret0 == 0)
	{
    
    
		printf("file exist\n");
	}
	else
	{
    
    
		int ret = mkfifo(FIFO_PATH, 0777);
		if(ret == -1)
		{
    
    
			perror("mkfifo failed");
			return -1;
		}
	}
	
	int ret1 = access(FIFO_PATH1, F_OK);
	if(ret1 == 0)
	{
    
    
		printf("file exist\n");
	}
	else
	{
    
    
		int ret1 = mkfifo(FIFO_PATH1, 0777);
		if(ret1 == -1)
		{
    
    
			perror("mkfifo1 failed");
			return -1;
		}
	}
	
	//2,打开管道文件 (读写权限打开)
	int fd = open(FIFO_PATH, O_RDWR);
	int fd1 = open(FIFO_PATH1, O_RDWR);
	if(fd < 0 || fd1 < 0)
	{
    
    
		perror("open fifo failed");
		return -1;
	}
	
	//创建子进程
	pid_t pid = fork();
	if(pid < 0){
    
    
		perror("fork failed \n");
		return -1;
	}
	if(pid > 0){
    
    
		//3,循环从管道文件中读取数据,输出
		char buf[128];
		
		while(1)
		{
    
    
			bzero(buf, sizeof(buf));
			read(fd, buf, sizeof(buf));   //阻塞
			printf("Terminal 1 recvinfo:%s", buf);
		}
		wait(NULL);
	}
	if(pid == 0){
    
    
		char buf[128];
		while(1)
		{
    
    
			//3,循环从管道文件中写入数据
			bzero(buf, sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			write(fd1, buf, sizeof(buf));   //阻塞
			printf("Terminal 1 sendinfo:%s", buf);
		}
	}
	return 0;
}


有名管道的特点:
1) 有名管道是以文件形式存在于操作系统中,可以通过文件IO访问管道
2) 管道文件的读端和写端必须要同时存在
3) 有名管道可以实现单方向的数据通信:数据通信可以是1对1,多对一的形式
4) 数据写入具有原子性 (数据写入要么成功,要么失败)

信号

1、信号有哪些?
在这里插入图片描述
2、如何发出信号?
(1)一些特定的操作会发送信号给进程 。例如 ctrl + c 发送 SIGINT
(2)使用 kill 命令进行发送
格式: kill -信号值 进程ID  例如: kill -2 7289 / kill -SIGINT 7289
格式: killall -信号值 进程名字 例如: killall -9 ./double_fifo1
(3)在程序中使用kill函数发送信号

	函数一:
	int kill(pid_t pid, int sig);
	头文件:
	#include <sys/types.h>
	#include <signal.h>
	参数:
	pid : 进程ID
	sig : 信号值

例子:创建一个子进程,子进程每隔1s输出hello, 父进程5S后发送信号杀死子进程

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    
    
	pid_t pid;
	pid = fork();
	if(pid < 0)
	{
    
    
		perror("fork failed");
		return -1;
	}
	if(pid > 0)
	{
    
    
		while(1)
		{
    
    
			printf("hello\n");
			sleep(1);
		}
	}
	if(pid == 0)
	{
    
    
		pid_t pid1 = getppid();
		sleep(5);
		kill(pid1, 9);	
	}
	return 0;
}
	函数二:
	typedef void (*sighandler_t)(int);  //函数指针
	sighandler_t signal(int signum, sighandler_t handler);
	
	头文件:#include <signal.h>
	
	参数:
	signum : 要捕捉的信号 ( 9) SIGKILL	19) SIGSTOP不能捕捉)
	handler : 注册执行函数 (接收到捕捉信号之后进程执行的函数)

例子:设置一个进程,捕捉信号SIGINT, 接收到信号之后,输出hello

#include <stdio.h>
#include <signal.h>

void func(int arg)
{
    
    
	printf("hello\n");
}

int main()
{
    
    
	//1,注册捕捉信号SIGINT,捕捉到SIGINT信号后运行func()函数
	signal(SIGINT, func);
	while(1);
	return 0;
}
 其余函数:
 #include <signal.h>
 int raise(int sig);
 sig : 发送的信号值
 
 #include <unistd.h>
  int pause(void);  //挂起当前进程 (进程睡眠)等待外部发送一个信号

猜你喜欢

转载自blog.csdn.net/weixin_42518229/article/details/103847982