管道是Linux中进程间通信的一种方式。
原理:利用文件系统接口实现进程间通信
所以管道也是一种特殊的文件系统
要求:只能用于父进程和子进程或者兄弟进程
特点:具有固定的写端和读端
无名管道可以看作一种特殊的文件系统,对于他的读写也可以使用read(),write(),但是它不属于任何文件系统,并且只存于内核的内存空间中。
使用pipe()函数创建管道,fds[1]表示读管道,fds[0]表示写管道
思路:先创建管道----->通过fork创建一个子进程(继承了父进程读写管道)----->关闭无关的读和写
注意点:只有读端存在时,写入数据才有意义,先写后读
特点:突破了无名管道只能在亲缘进程间通信的限制
实现原理:利用文件系统接口实现进程间通信,进程通过打开管道文件进行读写操作,传输数据。
最古老的进程通信的方法
SIGHUP | 通知同一绘画内的各个作业与控制终端不在关联 |
SIGINT | 终止进程 ctrl+c控制 |
SIGQUIT | 和SIGINT相似 ctrl+\控制 |
SIGALRM | 定时器 |
SIGSTOP | 暂停进程 |
二者区别:raise()可以向自身进程发送信号
kill(进程号,信号)
raise(信号)
alarm()用来设置定时器
pause()用来捕捉信号
signal()和signaction()
作用:处理用户感兴趣的一些信号
sigemptyset | 信号集合初始化为空(第一步) |
sigfillset | 初始化包含已定义的信号的集合 |
sigaddset | 添加指定信号到信号集合中 |
sigdelset | 从信号集合中删除指定信号 |
sigismember | 查询指定信号是不是在信号集合中 |
sigprocmask() | 设置屏蔽信号字 |
if(sigemptyset(&set)<0)/*首先信号集合置空*/
if(sigaddset(&set,SIGQUIT)<0)/*添加指定信号到信号集合中*/
if(sigismember(&set,SIGQUIT))/*查询指定信号是不是在信号集合中*/
if(sigprocmask(SIG_BLOCK,&set,NULL))/*设置屏蔽字*/
/*SIG_BLOCK就是增加一个型号集合到当前进程的阻塞集合中*/
进程之间的互斥与同步关系根源:临界资源。
信号量是用来解决进程之间的同步互斥问题的一种进程之间的通信机制
信号量机制如何实现多进程同一时间访问同一临界资源:进程通过P操作阻塞进程,V操作唤醒进程,确保只有有限个进程进入临界区
信号量相关的函数调用代码:
/*sem_com.c:
semaphore信号量,如果是单个资源时,可以简单理解信号量就是临界资源 ,和繁华路段的移动厕所一个道理*/
#include "sem_com.h"
/*信号量初始化函数*/
int init_sem(int sem_id, int init_value)
{
union semun sem_union;
sem_union.val = init_value;/*这里的init_value为信号量传入的初始值*/
if (semctl(sem_id, 0, IPC_SETVAL, sem_union) == -1)/*初始化信号量,初值为0*/
/*sem_id由semget获得的信号量值;
0表示单个信号量;
SETVAL:将信号量值设置为第四项参数sem_union的val值;
semctl()函数就是semaphore control,将信号量标识符为sem_id的信号量,用SETVAL设置成sem_union共用体中成员val的值
因为只针对单个资源,所以只需1个信号量,所以第2个参数为0;只有使用信号集时才会用到大于0的情况;
总结:该函数就是给信号量赋初值,由sem_union共用体指定初值大小,根据第3个参数不同返回值不一样;
第3个参数不同的取值会有不同的函数返回值:
IPC_STAT:获得该信号量(或者信号量集合)的 semid_ds 结构,并存放
在由第 4 个参数 arg 的 buf 指向的 semid_ds 结构中。semid_ds 是在系统中描述信号量的数据结构。
IPC_SETVAL:将信号量值设置为第4个参数的 val 值
IPC_GETVAL:返回信号量的当前值
IPC_RMID:从系统中,删除信号量(或者信号量集)。
*/
{
perror("Initialize semaphore"); /*出错-1*/
return -1;
}
return 0;
}
/*删除信号量函数*/
int del_sem(int sem_id)
{
union semun sem_union;
/*IPC_RMID:从系统中删除指定信号量;*/
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
{
perror("Delete semaphore");/*出错-1*/
return -1;
}
}
/*获取信号量大小的函数*/
int get_semvalue(int sem_id)
{ union semun sem_union;
int a;
if ((a=semctl(sem_id, 0, IPC_GETVAL, sem_union)) == -1)/*GETVAL:返回信号量的当前值;*/
{ perror("Get semaphore value is wrong!");
return -1;
}
return a;
}
/*P操作函数*/
int sem_p(int sem_id)
{
struct sembuf sem_b;
sem_b.sem_num = 0;/*信号量编号,使用单个信号量时,通常取值为 0 */
sem_b.sem_op = -1; /*信号量操作:取值为-1 则表示 P 操作,取值为+1 则表示 V 操作*/
sem_b.sem_flg = SEM_UNDO; /*通常设置为 SEM_UNDO。这样在进程没释放信号量而退出时,系统自动释放该进程中未释放的信号量*/
if (semop(sem_id, &sem_b, 1) == -1)
/* 第二,第三个参数的含义:
&sem_b:指向信号量操作的结构体sembuf;
1:信号量操作结构体中,可以操作信号量的操作个数,通常为1(1个P操作或1个V操作)
semop函数作用:
对指定的信号量sem_id,执行由sembuf结构体所指定的P或V操作,成功则返回信号量标识符。
*/
{
perror("P operation"); /*出错了,返回-1*/
return -1;
}
return 0;/*正常*/
}
/*V操作函数*/
int sem_v(int sem_id)
{ struct sembuf sem_b;
sem_b.sem_num = 0; /* 编号通常为0*/
sem_b.sem_op = 1; /* V 操作+1 */
sem_b.sem_flg = SEM_UNDO; /*通常这么写*/
if (semop(sem_id, &sem_b, 1) == -1)
{
perror("V operation"); /*出错-1*/
return -1;
}
return 0;/*正常0*/
}
创建两个进程简单调用上述接口:
/* sem_fork.c
该源码的功能是
1:创建一个信号量,并赋值为0;
2:调用fork函数生成两个进程;
3:先让子进程休眠3秒,于是父进程先执行P操作,肯定减不了,因为资源为0,父进程阻塞,
4:当休眠3秒时间到时,程序转到子进程,子进程生成一个资源,使得semaphore=1,然后
5:子进程结束,程序转到父进程,由于有资源可消费,此时父进程执行P操作,P操作成功 ,
6:程序结束。
*/
#include
#include
#include
#include
#include
#include
#include
//#include "sem_com.h"
#include "sem_com.c"
#define DELAY_TIME 3 /*这个宏定义用于让这个实验更直观*/
int main(void)
{
pid_t result;
int sem_id;
sem_id = semget(ftok(".", 'a'), 1, 0666|IPC_CREAT); /* 创建一个信号量*/
/*ftok(".",'a'):
使用ftok()函数(可以如此记忆:file to key)把一个已存在的路径名和一个整数标识符转换成
一个key_t值,通常是一个至少32位的整数,称为IPC键,用于标识信号量。
1:需要创建信号量的数目,通常取1;
0666:文件读写权限:对所有用户是读写权限
IPC_CREAT:新建信号量
semget()函数的作用是创建一个信号量,用返回值标识,如果出错返回-1;
*/
init_sem(sem_id, 0);/*信号量初值为0*/
/*调用fork函数,创建一个子进程,其返回值为result*/
result = fork();
/*通过result的值来判断fork函数的返回情况,首先进行出错处理*/
if(result == -1)
{
perror("Fork\n");
}
else if (result == 0) /*返回值为0代表子进程*/
{
printf("The returned value is %d in the child process(PID = %d)\n\n", result, getpid());
printf("Child process will wait for three seconds...\n\n\n"); sleep(3);
printf("The child process will execute V operate,so the father process can do P operate.\n\n");
sem_v(sem_id);/*子进程执行v操作*/
printf("In child process,after V operate,the current semaphore value is %d\n",get_semvalue(sem_id));
}
/*返回值大于0代表父进程*/
else
{
sem_p(sem_id);/*父进程执行p操作*/
printf("The father process execute P operate successful,and then it about to quit.\n\n");
printf("Before quit,the father process's semaphore value is %d\n\n",get_semvalue(sem_id));
sem_v(sem_id);/*父进程执行v操作*/
del_sem(sem_id);/*删除信号量*/
}
exit(0);
}
最为高效的进程间通信方式
用户可以从消息队列中添加消息和读取消息