一、无名管道
? ? ? ? 无名管道是早期的进程通信,主要用于父子进程或带有血缘关系的进程(进程组)间的通信,比较简单,主要用到的函数有一个:
#include <unistd.h>
int pipe(int pipefd[2]); // 创建无名管道
// pipefd是存放文件描述符的数组
// pipefd[0]代表读
// pipefd[1]代表写
? ? ? ? ? 以下是测试代码,没有加入线程,只实现了单向通信,父进程往管道里写数据,子进程读取父进程写入的数据
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
//创建无名管道
int pipefd[2];
if(pipe(pipefd)){
perror("pipe error");
exit(-1);
}
printf("[0] %d \n",pipefd[0]); //pipefd[0]:读
printf("[1] %d \n",pipefd[1]); //pipefd[1]: 写
//创建进程
pid_t pid = fork();
if(pid < 0){
perror("fork error");
exit(1);
}else if(pid == 0){
//子进程读取父进程写入的数据
close(pipefd[1]); //子进程只读取数据,所以把写关闭
char buf[50];
while(1){
bzero(buf,sizeof(buf));//清空数组
read(pipefd[0],buf,sizeof(buf));//读取数据
printf("父进程:%s\n",buf);
}
}else{
//父进程写入数据
close(pipefd[0]);//父进程只写数据,所以把读关闭
char buf[50];
while(1){
bzero(buf,sizeof(buf));//清空数组
gets(buf);
write(pipefd[1],buf,strlen(buf));//父进程写数据到pipefd[1]里
}
}
return 0;
}
? ? ? ? 运行结果:
iot@ubuntu:~/shared/process/day3/temp$ ./a.out
[0] 3
[1] 4
ni gan ma
父进程: ni gan ma
ai u
父进程: ai u
buyaozaidale
父进程: buyaozaidale
^C
iot@ubuntu:~/shared/process/day3/temp$
二、有名管道
? ? ? ? 有名管道也属于早期进程通信的一种,可以用于任意进程间的通信,比无名管道灵活,主要用到的函数有:
#include <sys/types.h>
#include <sys/stat.h>
/**
***********************************
*@brief 创建有名管道
*@param pathname:带文件名的路径
mode:文件的八进制权限
*@retval int
成功返回0
失败返回-1,并返回错误码EOF
***********************************
*/
int mkfifo(const char *pathname, mode_t mode);
? ? ? ? 测试代码如下,单向通信,进程1往管道里写数据,进程2从管道里读取数据,这两个进程可以是任意进程。
进程1:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc != 2){
printf("User :%s <name>\n",argv[0]);
exit(1);
}
if(mkfifo(argv[1],0666)){ //创建管道文件,文件名需要从外部传进来
perror("mkfifo error");
exit(-1);
}
//往管道文件里写数据,文件IO
int fd = open("./ikun",O_RDWR); //以读写方式打开文件,打开管道
if(fd < 0){
perror("open error");
exit(1);
}
char buf[50]; //数据缓冲区大小
while(1){
bzero(buf,sizeof(buf)); //初始化空间清零
gets(buf); //从键盘获取数据,把数据写入buf
write(fd,buf,sizeof(buf)); //把buf里的数据写给fd
}
close(fd);
return 0;
}
进程2:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc != 2){
printf("User :%s <name>\n",argv[0]);
exit(1);
}
mkfifo(argv[1],0664);
//往管道文件里写数据
int fd = open(argv[1],O_RDWR);//以读写方式打开文件,打开管道
if(fd < 0){
perror("open error");
exit(1);
}
char buf[50];
while(1){
bzero(buf,sizeof(buf));
read(fd,buf,sizeof(buf));//从fd中读取buf里的数据
printf("%s\n",buf);//把buf里的数据打印出屏幕
}
close(fd);
return 0;
}
? ? ? ? 进程1创建一个名为ikun的有名管道,运行进程1就可以在当前目录下生成管道文件ikun,我们用 ls -l 查看一下,ikun的文件类型为p,说明我们创建成功。注:管道文件由内核区域映射,文件大小永远为0。
iot@ubuntu:~/shared/process/day3/temp$ ./mkfifo1 ikun
^C
iot@ubuntu:~/shared/process/day3/temp$ ls -l
total 48
-rwxrwxr-x 1 iot iot 8600 Sep 11 04:32 a.out
prw-rw-r-- 1 iot iot 0 Sep 11 04:44 ikun
-rwxrwxr-x 1 iot iot 8560 Sep 11 04:36 mkfifo1
-rw-rw-r-- 1 iot iot 724 Sep 11 04:36 mkfifo1.c
-rwxrwxr-x 1 iot iot 8600 Sep 11 04:33 mkfifo2
-rw-rw-r-- 1 iot iot 632 Sep 11 04:09 mkfifo2.c
????????进程2用到了main函数传参,所以先运行进程1,将管道文件创建出来,再运行进程2,将管道文件名传进去。另开一个终端,运行进程2,在进程1输入的数据将会在进程2打印出来,即实现了进程间的单向通信。
?
三、信号
? ? ? ? 信号也属于早期进程间的通信,信号是软件层面上对中断机制的一种模拟,是一种异步的通信,信号可以直接进行用户空间进程和内核进程之间的交互。在linux中用命令 kill -l 查看所有的信号
? ? ? ? ?与信号通信相关的函数有以下:
1. kill
#include <sys/types.h>
#include <signal.h>
/**
***********************************
*@brief 发送信号
*@param pid:进程号
sig:信号
*@retval int
成功返回0
失败返回-1,并返回错误码EOF
***********************************
*/
int kill(pid_t pid, int sig);
2. signal
/**
***********************************
*@brief 信号注册
*@param signum:信号
handler:事件,捕捉到信号signum后要执行的操作(函数)
*@retval sighandler_t
成功返回sighandler_t
失败返回SIG_ERR,并返回错误码
***********************************
*/
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
????????测试代码如下:
kill_1:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc,char *argv[])
{
if(argc != 3){
printf("User %s<PID><SIG>",argv[0]);
exit(-1);
}
//int a = atoi(argv[1]);//将pid号从字符窜转化为整形
// int b = atoi(argv[2]);//将sig号从字符串转化为整形
// printf("a=%d b=%d\n",a,b);//将两个以转换的整形打印出来
kill(atoi(argv[1]),atoi(argv[2]));
return 0;
}
kill_2:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void show(int sig)
{
printf("你干嘛~\n");
}
int main(void)
{
// signal(2,SIG_IGN);//将信号2忽略
//signal(2,SIG_DFL);//收到信号2默认执行
signal(2,show);
while(1){
sleep(1);
printf("%d\n",getpid()); //获取当前进程的pid号
}
return 0;
}
? ? ? ? 先运行kill_2,每隔1s打印一次当前的进程号,另开一个终端,运行kill_1,每执行一次 ./kill_2 10834 2 就会向进程10834发送一个信号2,kill_2收到信号2后,打印一次你干嘛~
?
? ? ? ? 至此,早期的进程通信结束,下期将更新在 system V 之后的进程间的通信——共享内存、消息队列、信号灯(信号量)。
? ? ? ? 以上测试代码仅实现单向通信,要实现双向通信的话加个线程即可。
?
|