目录
一、无名管道
相关概念
相关函数介
相关说明
实战
二、有名管道
有名管道和无名管道的区别
有名管道和无名管道的相同点
有名管道的相关函数
实战
三、消息队列
消息队列的介绍
消息队列的特点
相关函数
实战
四、共享内存
共享内存优缺点
共享内存的特点
相关函数介绍
实战
五、信号
信号概述
信号入门版——不带信息,单纯地接受和发送信号
信号高级版——带消息
实战
六、信号量
信号量概述
信号量简述
相关函数介绍
七、ipcs指令与ipcrm指令
pipe函数:int pipe(fd[2]),fd[1]为写入端(入队),fd[0]为读出端(出队)
#include
#include
#include
int main()
{
pid_t pid;
int fd[2];
char buf[1024] = {0};
if(pipe(fd) < 0){
printf("creat pipe failed!\n");
}
pid = fork();
if(pid < 0){
perror("fork");
exit(1);
}
if(pid == 0){
printf("this is child!\n");
close(fd[1]);
int nread = read(fd[0],buf,1024);
printf("read:%d,content:%s\n",nread,buf);
exit(0);
}else{
printf("this is fsther!\n");
close(fd[0]);
write(fd[1],"father's data!",20);
wait(NULL);
}
return 0;
}
程序运行后结果为
当读程序与写程序都进行了管道创建,则要注意去除EEXIST错误信息。
注意:当使用open函数打开时,尽量使用只读或只写的权限打开
读与创建程序
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
char buf[128] = {0};
if((mkfifo("./file",0777) < 0) & (errno != EEXIST)){
printf("create fifo failed!\n");
}
fd = open("./file",O_RDONLY);
while(1){
int nread = read(fd,buf,128);
printf("read:%d,context:%s\n",nread,buf);
}
close(fd);
return 0;
}
写与创建程序
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
int cnt = 0;
char buf[128] = "zhen shuai!";
if((mkfifo("./file",0777) < 0) & (errno != EEXIST)){
printf("create fifo failed!\n");
}
fd = open("./file",O_WRONLY);
while(1){
write(fd,buf,strlen(buf));
sleep(1);
if(cnt == 5){
break;
}
cnt++;
}
close(fd);
return 0;
}
消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
函数原型 | 函数作用及返回值 | 函数参数 | |
---|---|---|---|
int msgget(key_t key, int flag); |
创建或打开消息队列:成功返回队列ID,失败返回-1 | key:消息对列的表示(一般通过ftok函数获得) | |
flag:指名队列的访问权限和创建标志,创建标志为IPC_CREAT、IPC_EXC | |||
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); |
添加消息:成功返回0,失败返回-1 | msgid:消息队列的id,通常为msgget的返回值 |
|
msgp:是消息结构体。 | long mtype:接受进程可以用此值进行消息选择 | ||
char mtext:是一个数组,他的最大允许长度由msgsz决定 | |||
msgsz:用来决定mtext中最大长度。 | |||
msgflg:0是阻塞、IPC_NOWAIT队列满时函数不等待直接返回、IPC_NOERROR当发送字节大于size字节时,阶段部分将被丢弃,且不通知发送进程。 | |||
int msgrcv(int msqid, const void *msgp, size_t msgsz, long msgtyp,int msgflg); |
读取消息:成功返回消息数据的长度,失败返回-1 | msgid:与msgsnd函数一样。 | |
msgp:与msgsnd函数一样。 | |||
msgsz:与msgsnd函数一样。 | |||
msgtyp>0:接受第一个类型等于msgtyp的消息 msgtyp==0:接受队列中的第一个消息 msgtyp<0:接受类型小于或等于msgtyp绝对值的第一个最低类型消息 |
|||
msgflg:与msgsnd函数一样。 | |||
int msgctl(int msqid, int cmd, struct msqid_ds *buf); | 控制消息队列,常用来删除消息队列:成功返回0,失败返回-1 | msqid:消息队列标识符 | |
cmd:通常为IPC_RMID表示删除消息队列 | |||
buf:通常为NULL |
发送信息
#include
#include
#include
#include
#include
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
key_t key = ftok(".",1);
int msqid;
struct msgbuf msgp = {888,"xiaoyang zhen shuai!"};
msqid = msgget(key,IPC_CREAT|0777);
int nrcv = msgsnd(msqid,&msgp,strlen(msgp.mtext),0);
msgctl(msqid,IPC_RMID,0);
return 0;
}
接受信息
#include
#include
#include
#include
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
key_t key = ftok(".",1);;
int msqid;
struct msgbuf msgp;
msqid = msgget(key,IPC_CREAT|0777);
if(msqid < 0){
printf("creat failed!\n");
perror("why");
}
int nrcv = msgrcv(msqid,&msgp,128,888,0);
printf("rcv = %d,context = %s\n",nrcv,msgp.mtext);
msgctl(msqid,IPC_RMID,0);
return 0;
}
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
函数名称 | 函数作用及返回值 | 函数参数 | |
---|---|---|---|
int shmget(key_t key,size_t size,int flag) | 用来创建共享内存。成功返回共享内存ID,失败返回-1。 | key:用来变换成一个标识符 | |
size:为要请求的内存长度(内核是以页为单位分配内存,当size参数的值不是系统内存页长的整数倍时,系统会分配给进程最小的可以满足size长的页数,但是最后一页的剩余部分内存是不可用的。) | |||
flag:IPC_CREAT(注意要增加权限)、IPC_EXCL | |||
void *shmat(int shm_id, const void *addr, int flag); | 用来建立进程与共享内存之间的连接。成功返回指向共享内存的指针,失败返回-1。 | shm_id:指定要引入的共享内存 | |
addr:和flag共同决定引入的地址值 | addr为0,表明让内核来决定第1个可以引入的位置。 | ||
flag:和addr共同决定引入的地址值 | addr非零,并且flag中指定SHM_RND,则此段引入到addr所指向的位置(此操作不推荐使用,因为不会只对一种硬件上运行应用程序,为了程序的通用性推荐使用第1种方法)。为0则为读写的方式,为SHM_RDONLY为只读。 | ||
int shmdt(void *addr); | 用来断开进程与共享内存之间的连接。成功返回0,失败返回-1。 | 参数addr是调用shmat函数的返回值 | |
int shmctl(int shm_id, int cmd, struct shmid_ds *buf); | 用来根据cmd指令对共享内存进行操作,刚用来删除共享内存。成功返回0,失败返回-1。 | shm_id:指定要控制的共享内存 | |
cmd:常用的是IPC_RMID (从系统中删除该共享内存)。 |
|||
buf:通常再删除时为0. |
#include
#include
#include
int main()
{
key_t key;
int shmid;
char *shmaddr;
key = ftok(".",1);
// shmid = shmget(key,1024 * 4,IPC_CREAT|0666);
shmid = shmget(key,1024 * 4,0);
if(shmaddr < 0){
printf("shmget failed!\n");
}
shmaddr = shmat(shmid,NULL,0);
printf("data = %s\n",shmaddr);
shmdt(shmaddr);
shmctl(shmid,IPC_RMID,0);
return 0;
}
写数据
#include
#include
#include
#include
#include
int main()
{
int shmid;
key_t key;
char *shmaddr;
char buf[128] = {0};
key = ftok(".",1);
shmid = shmget(key,1024 * 4,IPC_CREAT|0666);
if(shmid > 0){
printf("shmget success!\n");
}
shmaddr = shmat(shmid,NULL,0);
strcpy(shmaddr,"xiaoyang");
printf("write success!\n");
shmdt(shmaddr);
return 0;
}
信号的名字和编号:每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO ”、“SIGCHLD”等等。信号定义在signal.h
头文件中,信号名都定义为正整数。具体的信号名称可以使用kill -l
来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用
信号的处理:信号的处理有三种方法,分别是:忽略、捕捉和默认动作
忽略信号:为忽略信号不执行原本的操作,但是sigkill和sigstop无法忽略。可以通过SIG_IGN这个宏进行忽略
捕捉信号:通过用户自定义函数来告诉内核,当信号到来时调用该函数,做相应的操作操作。
默认动作:系统默认的动作,每个信号都有其对应的默认动作。
函数名称 | 函数作用及返回值 | 函数参数 |
---|---|---|
int kill(pid_t pid, int sig); | 向对应的pid进程发送信号。成功返回0,失败返回-1. | pid:对应进程的pid |
sig:需要发送的信号的序号 | ||
sighandler_t signal(int signum, sighandler_t handler); | 信号注册函数。成功返回一个特定处理函数的指针,失败返回SIG_ERR | signum:信号的序号 |
handle:函数指针,指向中断函数 |
实战
#include
#include
//typedef void (*sighandler_t)(int);
//sighandler_t signal(int signum, sighandler_t handler);
void sighandle(int signum)
{
switch(signum){
case 2:
printf("SIGINT\n");
break;
case 1:
printf("SIGUP\n");
break;
case 10:
printf("SIGUSE1\n");
break;
}
}
int main()
{
signal(SIGINT,SIG_IGN);
signal(SIGUSR1,sighandle);
signal(SIGHUP,sighandle);
while(1);
return 0;
}
#include
#include
#include
//int kill(pid_t pid, int sig);
int main(int argc,char **argv)
{
int pid;
int signum;
char cmd[128] = {0};
if(argc != 3){
printf("input error!\n");
}
pid = atoi(argv[2]);
signum = atoi(argv[1]);
sprintf(cmd,"kill -%d -%d",signum,pid);
// if((kill(pid,signum)) < 0){
// perror("kill");
// }
system(cmd);
return 0;
}
函数名称 | 函数作用以及返回值 | 函数参数 | |
---|---|---|---|
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact); | 接受信号及其携带的消息。成功返回0,失败返回-1 | signum:传输信号的序号 | |
act:结构体 | (*sa_handler)(int):与signal函数的handle相同 | ||
(*sa_sigaction)(int, siginfo_t *, void *):为函数指针。int为信号的序号;void*是接收到信号所携带的额外数据;而struct siginfo这个结构体主要适用于记录接收信号的一些相关信息 | |||
sa_mask:sa_mask 成员用来指定在信号处理函数执行期间需要被屏蔽的信号 | |||
sa_flags:通常使用SA_SIGINFO,使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。 | |||
oldac:为记录信号之前的内容。 | |||
int sigqueue(pid_t pid, int sig, const union sigval value); | 只能把信号发送给单个进程,可以使用 value 参数向信号处理程序传递整数值或者指针值。 | pid:接收信号的进程id | |
sig:发送信号的序号 | |||
value:联合体,指定信息传递的参数。 |
发送
#include
#include
#include
// int sigqueue(pid_t pid, int sig, const union sigval value);
int main(int argc,char **argv)
{
if(argc != 3){
printf("put data number error!\n");
exit(1);
}
int pid;
int signum;
pid = atoi(argv[2]);
signum = atoi(argv[1]);
union sigval value;
value.sival_int = 100;
sigqueue(pid,signum,value);
printf("done!\n");
return 0;
}
接受
#include
#include
// int sigaction(int signum, const struct sigaction *act,
// struct sigaction *oldact);
void handle(int signum, siginfo_t *info, void *context)
{
printf("get signal = %d\n",signum);
if(context != NULL){
printf("get Signal number = %d\n",info->si_signo);
printf("get data = %d\n",info->si_int);
printf("get value data = %d\n",info->si_value.sival_int);
}
}
int main()
{
struct sigaction act;
act.sa_sigaction = handle;
act.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1,&act,0);
printf("pid = %d\n",getpid());
while(1);
return 0;
}
semget
创建新的信号量集合时,必须指定集合中信号量的个数(即nsems
),通常为1; 如果是引用一个现有的集合,则将nsems
指定为 0 。函数参数 | 参数意义 | |
---|---|---|
semid | 信号量集的标识符,一般由semget返回的。 | |
*sops | sembuf结构体的数组指针 | short sem_num:信号量组中对应的序号,0~sem_nums-1 |
short sem_op:信号量值在一次操作中的改变量(一般为+1、-1) | ||
short sem_flg:IPC_NOWAIT, SEM_UNDO(一般选择UNDO阻塞) | ||
nsops | 为sops中的元素个数 |
函数参数 | 参数意义 |
---|---|
semid | 信号量集的标识符 |
semnum | 信号量组中对应的序号 |
cmd |
|
IPC_RMID :删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。 |
|
union semun | 该函数有三个或四个参数,而几个参数是由cmd决定的,该参数就是第四个参数。联合体。如果使用该参数,则其类型为semun。 |
#include
#include
#include
#include
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
void pGetKey(int semid)
{
struct sembuf set;
set.sem_num = 0;
set.sem_op = -1;
set.sem_flg = SEM_UNDO;
semop(semid,&set,1);
printf("get key!\n");
}
void vPutKey(int semid)
{
struct sembuf set;
set.sem_num = 0;
set.sem_op = +1;
set.sem_flg = SEM_UNDO;
semop(semid,&set,1);
printf("put back key!\n");
}
int main()
{
int semid;
int key;
int pid;
key = ftok(".",2);
semid = semget(key,1,IPC_CREAT|0666);
union semun initsem;
initsem.val = 0;
semctl(semid,0,SETVAL,initsem);
pid = fork();
if(pid > 0){
pGetKey(semid);
printf("this is father!\n");
vPutKey(semid);
}
if(pid == 0){
printf("this is child!\n");
vPutKey(semid);
}
semctl(semid,0,IPC_RMID,initsem);
return 0;
}