目录
一、六种通信方式
二、无名管道(PIPE)和有名管道(FIFO)
1、无名管道
(1)特点
(2)管道PIPE的使用
(3)注意点
(4)代码
2、有名管道(FIFO)
(1)FIFO特征
(2)FIFO的应用
(3) 注意事项
(4)read.c
(5)write.c
三、信号
1、命令
2、信号/事件 产生方式
3、信号的种类
4、注意
5、信号的相关函数
(1)kill(产生一个信号,把某个进程杀死,signal捕获产生的信号)
(2)signal(跟kill配套使用)
(3)raise
(4)pause(挂起)
(5)sigprocmask(阻塞/解除)
(6)信号集操作函数簇
(7)sigqueue(发给谁,发送的信号,携带的数据)(跟sigaction是一对)
(8)sigaction
6、例程
(1)子进程中捕获信号并相应
(2)信号阻塞的应用
(3)sigqueue和aigaction的应用
1、无名管道(PIPE)和有名管道(FIFO)
2、信号(signal)
3、system V-IPC 之共享内存
4、system V-IPC 之消息队列
5、system V-IPC 之信号量
6、套接字
a、一边出、一边进 b、没有名字,不能用open打开
c、只用于亲缘进程间(父子、兄弟、祖孙进程)d、不能用lseek()定位
a、pipe 中的参数是一个具有两个整型数的数组,用来存放文件描述符,一个是读端,另一个是 写端
b、图说明管道在创建子进程后的状态
c、pipefd[0] --> 读取端口
pipefd[1] --> 写入端口
#include
#include
#include
#include
char * buf = NULL ;
int pipefd[2]
void f(void)
{
while(1)
{
printf("父进程*请输入:\n");
fgets(buf , 1024 , stdin );
int ret_val = write( pipefd[1] , buf , strlen(buf)+1 );
printf("成功写入:%d字节 \n" , ret_val );
ret_val = read( pipefd[0] , buf , 1024 );
printf("父进程*成功读取:%d字节 内容:%s \n" , ret_val , buf );
}
}
void s(void)
{
while(1)
{
int ret_val = read( pipefd[0] , buf , 1024 );
printf("子进程*成功读取:%d字节 内容:%s \n" , ret_val , buf );
printf("子进程*请输入:\n");
fgets(buf , 1024 , stdin );
ret_val = write( pipefd[1] , buf , strlen(buf)+1 );
printf("成功写入:%d字节 \n" , ret_val );
}
}
int main(int argc, char const *argv[])
{
if(pipe( pipefd))
{
perror("pipe error");
return -1 ;
}
buf = calloc(1, 1024);
int pid = fork();
if ( pid > 0 )
{
f();
}
else if (pid == 0)
{
s();
}
else
{
perror("fork error ");
}
return 0;
}
a、有名字,存储于普通文件系统中 b、可以用open()获取 FIFO 的文件描述符
c、使用统一的 read( )/write( )来读写 d、不能使用 lseek( )来定位
e、具有写入原子性,支持多写者同时进行写操作而数据不会互相践踏
f、First In First Out,最先被写入 FIFO 的数据,最先被读出来
a、管道文件在打开的时候如果只有一方(读者/写者)则阻塞等待对方到达然后同时打开 文件 。
#include
#include
#include
#include
#include
#define FIFO_PATH "/tmp/my_fifo"
int main(int argc, char const *argv[])
{
// 创建管道文件
if( access(FIFO_PATH, F_OK)) // 判断管道文件是否已存在 如果存在则返回 0 否则 -1
{
if( mkfifo(FIFO_PATH , 0666 )) // 创建
{
perror("mkfifo error");
return -1 ;
}
}
printf("管道文件创建成功!! \n") ;
// 打开管道文件
int fd_fifo = open(FIFO_PATH, O_RDONLY );
if (-1 == fd_fifo)
{
perror("open error");
return -1 ;
}
printf("管道文件打开成功!! \n") ;
// 读取信息
char buf[128]= {0};
int ret_val = read(fd_fifo , buf , sizeof( buf ));
printf("成功读取:%d 字节 内容:%s \n" , ret_val , buf );
// 关闭文件
close(fd_fifo);
return 0;
}
#include
#include
#include
#include
#include
#define FIFO_PATH "/tmp/my_fifo"
int main(int argc, char const *argv[])
{
// 创建管道文件
if( access(FIFO_PATH, F_OK)) // 判断管道文件是否已存在 如果存在则返回 0 否则 -1
{
if( mkfifo(FIFO_PATH , 0666 )) // 创建
{
perror("mkfifo error");
return -1 ;
}
}
printf("管道文件创建成功!! \n") ;
// 打开管道文件
int fd_fifo = open(FIFO_PATH, O_WRONLY );
if (-1 == fd_fifo)
{
perror("open error");
return -1 ;
}
printf("管道文件打开成功!! \n") ;
// 写入信息
int ret_val = write(fd_fifo , "hello Even" , sizeof("hello Even"));
printf("成功写入:%d 字节 \n" , ret_val);
// 关闭文件
close(fd_fifo);
return 0;
}
kill -l //罗列出系统中的信号
(1)用户按键:用户使用某种特殊字符递给终端,产生一个信号(事件)递送给进程
(2)硬件故障:进程执行错误,比如访问一个无效内存地址,这时候先由硬件报告给内核,再由内核把事件递送给进程
(3)kill函数或者命令:通过函数把需要的事件直接递送给进程
(1)可靠信号:
(2)不可靠信号:信号发生了,但是它可能会丢失
其中 1~31项为不可靠信号, 34~64为可靠信号
(1)信号 SIGKILL 和 SIGSTOP 是两个特殊的信号,他们不能被忽略、阻塞或捕捉
(2)任何进程都可以使用函数 kill( )来产生任何信号。
(3)接收信号的目标进程,按照如下顺序来做出反应:
A) 如果该信号被阻塞,那么将该信号挂起,不对其做任何处理,等到解除对其阻塞为 止。否则进入 B。
B) 如果该信号被捕捉,那么进一步判断捕捉的类型:
B1) 如果设置了响应函数,那么执行该响应函数。
B2) 如果设置为忽略,那么直接丢弃该信号。 否则进入 C。
C) 执行该信号的缺省动作
(4)信号函数可以继承到子进程中
额外携带的数必须是一下联合体:
union sigval
{
int sigval_int;
void * sigval_prt;
};
struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
void (*sa_sigaction)(int, siginfo_t *, void *);
#include
#include
#include
void func(int arg)
{
printf("这里是信号响应函数 %d \n" , arg );//信号响应
}
int main(int argc, char const *argv[])
{
int pid = fork();
int i = 0 ;
if (pid > 0 )
{
printf("这里是父进程 \n " );
sleep(3);
printf("猎杀时间到了..。 \n " );
sleep(1);
kill(pid , 4 ); //产生信号
}
else if (pid == 0 )
{
signal( 4 , func);//捕获信号
while(1)
{
printf("这里是子进程 : %d \n ", i++ );
sleep(1);
}
}
return 0;
}
#include
#include
#include
void func(int arg)
{
printf("这里是信号响应函数 %d \n" , arg );
}
int main(int argc, char const *argv[])
{
// 设置信号响应的函数
signal( 3 , func );
signal( 4 , func );
// 初始化信号集
sigset_t set ;
sigemptyset( &set ); // 将信号集清空
sigaddset( &set , 3 ); // 将指定的一个信号添加到信号集中
sigaddset( &set , 4 ); // 将指定的一个信号添加到信号集中
// 设置阻塞信号集中的信号
sigprocmask(SIG_SETMASK , &set , NULL ); // 把信号集中的信号设置为阻塞状态
// 给自己发送 3.4 号信号
raise(3);
raise(4);
sleep(2);
// 解除阻塞
sigprocmask(SIG_UNBLOCK , &set , NULL ); // 把信号集中的信号设置为阻塞状态
return 0;
}
运行后,等待2秒钟,才开始输出这里是.....
#include
#include
#include
#include
#include
void func(int sig, siginfo_t * info , void * arg )
{
printf("sig:%d , info: %s arg:%s \n" , sig , (char * )info->si_ptr , (char*)arg );
}
int main(int argc, char const *argv[])
{
// 定义ACT结构体并设置其信息
struct sigaction act ;
bzero(&act , sizeof(act)); // 清空结构体
act.sa_sigaction = func ;
act.sa_flags |= SA_SIGINFO ;//使用拓展信号函数而不是标准响应函数
// 设置捕获的信号响应函数
if( sigaction( 3 , &act, NULL ))//将信号捕获函数设置号
{
perror("设置捕获失败!!");
return -1 ;
}
// 设置好携带的参数
union sigval value;
value.sival_int = 1024 ; // 设置整型数组
value.sival_ptr = "Hello Even"; // 设置一个地址(指针)可以是任意类型的指针
// 发送信号
pid_t pid = getpid( );
if(sigqueue(pid , 3 , value))//发送信号,成功是0,失败是-1
{
perror("发送信号失败!!");
return -1 ;
}
printf("发送信号成功!!\n");
sleep(1);
return 0;
}