目录
一,总体概况图
二,方法详述
1.有名管道
mkfifo用来在文件系统中创建一个有名管道(fifo),但是这个文件只存在于内存中,
只不过在文件系统中有一个路径而已。
int mkfifo(const char *pathname, mode_t mode);
@pathname: 要创建的FIFO文件(带路径)
@mode: 创建的FIFO文件的权限,如 0664
返回值: 成功返回0,失败返回-1,errno被设置
2.无名管道
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
int main()
{
int pipefd[2];
int r = pipe(pipefd);
if(r == -1)
{
perror("pipe failed");
return -1;
}
pid_t pid = fork();
if(pid > 0)//是父进程返回
{
while(1)
{
char str[32];
read(pipefd[0],str,32);
printf("read buf: %s\n",str);
if(strcmp(str,"over")==0)
{
kill(pid,9);
exit(0);
}
}
}
else //是子进程返回
{
while(1)
{
char str[32];
gets(str);
write(pipefd[1],str,strlen(str)+1);
}
}
}
3.共享内存
1, 申请一个 system_v_ipc 的key
key_t ftok(const char *pathname, int proj_id);
2,创建或打开一个 system v 共享内存区
int shmget(key_t key, size_t size, int shmflg);
3,shmat 用来把共享内存区映射到进程的地址空间
void *shmat(int shmid, const void *shmaddr, int shmflg);
4,shmdt用来把映射的共享内存区解映射
int shmdt(const void *shmaddr);
5, shmctl 对共享内存区进行控制操作
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
@shmid: 共享内存区的ID,表示要操作哪个共享内存区
@cmd: 命令号,常用的有三个
IPC_RMID 用来删除该共享内存,此时第三个参数buf填为NULL
IPC_STAT 用来获取共享内存的状态,通过buf指向用来保存该内存区状态信息的
结构体变量。
IPC_SET 用来设置共享内存区的状态信息,通过buf指向用来保存该共享内存区
状态信息的结构体变量。
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define KEY_PATH "/home/china"
#define PRO_ID 9527
int main()
{
/*step1: 申请一个 system_v_ipc 的key*/
key_t key = ftok(KEY_PATH, PRO_ID);
if(key == -1)
{
perror("ftok failed");
return -1;
}
/*step2: 创建一个 system v 共享内存区*/
int shmid = shmget(key, 4096, IPC_CREAT | 0664);
if(shmid == -1)
{
perror("shmget failed");
return -1;
}
/*step3: 映射*/
char *shm_p = shmat(shmid, NULL, 0);
if(shm_p == NULL)
{
perror("shmat failed");
return -1;
}
while(1)
{
char str[64];
gets(str);
strcpy(shm_p,str);
if(strcmp(str,"quit") == 0)
break;
}
/*step4: 解映射*/
shmdt(shm_p);
/*step5: 销毁共享内存*/
shmctl(shmid, IPC_RMID, NULL);
}
4,信号量
4.1,posix_sem
POSIX概念:
POSIX是可移植性操作系统接口,即用POSIX标准写的代码,在POSIX操作系统上都能编译执行。
POSIX不局限于UNIX/Linux,比如DEC,Open VMS等。
Posix Semaphore 又分为有名信号量和无名信号量
有名信号量:在文件系统中有一个文件名,但不存在文件系统中,而是存在与内存中
无名信号量: 没有文件名,只适用于线程间或者由亲缘关系的进程间使用
有名信号量 无名信号量
创建/初始化 sem_open() sem_init()
P操作 sem_wait()/sem_trywait()
V操作 sem_post()
获取信号量的值 sem_getvalue()
销毁信号量 sem_unlink() sem_destroy()
#include <stdio.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#define SEM_NAME "/test.sem"
int main()
{
int sval;
/*step1: 创建一个信号量*/
sem_t *sem = sem_open(SEM_NAME, O_CREAT, 0664, 1);
if(sem == SEM_FAILED)
{
perror("sem_open failed");
}
/*step2: 操作信号量*/
sem_wait(sem);//P操作
sem_getvalue(sem, &sval);
printf("i get the sem, the sem value is %d\n",sval);
getchar();
sem_post(sem);//V操作
sem_getvalue(sem, &sval);
printf("i release the sem!,the sem value is %d\n",sval);
/*step3: 销毁信号量*/
sem_unlink(SEM_NAME);
}
4.2,sistem_v_sem
1.申请key
key_t ftok(const char *pathname, int proj_id);
2.调用semget来创建或打开一个system_v 的信号量
int semget(key_t key, int nsems, int semflg);
@nsems: 你要创建的信号量集中信号量的个数
如果我们不创建一个新的信号量集,而是访问一个已经存在的信号量集,此处参数应该 指定为0,一旦创建完一个信号量集后,我们就不能改变其中的信号量的个数了。
@semflg:标志位
1) 创建 PC_CREAT | 权限位,如 IPC_CREAT | 0664
2) 打开 0
3,调用semctl 给信号量赋初值
int semctl(int semid, int semnum, int cmd, ...);
@cmd: 操作命令号,常用的有
GETVAL: 获取某一个信号量的值
GETALL: 获取该信号量集的所有信号量的值
SETVAL: 设置某一个信号量的值
SETALL: 设置该信号量集中所有信号量的值
IPC_RMID: 删除该信号量集
IPC_SET: 设置该信号量集的状态信息
IPC_STAT: 获取该信号量集的状态信息
@...arg: 可选的参数,根据cmd是什么情况,它的值是不一样的
if cmd == GETVAL,arg则不需要,semctl的返回值就是该信号量的值
if cmd == GETALL,arg应该为一个short数组的首地址,用来保存所有信号量的值
if cmd == SETVAL,arg应该为一个short的整数,用来指定该信号量的值
if cmd == SETALL,arg应该为一个short数组的首地址,用来保存所有信号量的值
4,调用semop 对信号量进行pv操作
int semop(int semid, struct sembuf *sops, size_t nsops);
@semid: 你要操作的信号量集的ID
@sops: struct sembuf的数组的首地址,因为你可以同时对多个信号量进行操作
@nsops: 第二个参数 struct sembuf 数组的元素个数
struct sembuf
{
unsigned short sem_num; //表示你对哪个信号量进行操作(0,1,2,3...nsems-1)
short sem_op; //表示你对该信号量进行何种操作。
//无论是P操作还是V操作,实质就是对信号量的值进行操作
/*
sem_op < 0 ,表示P操作
sem_op > 0 , 表示V操作
sem_op ==0 , 表示不操作
最后,semval的值等于==> 原semval + sem_op
*/
short sem_flg; //操作标志位
/*
0: 一般情况。 P操作时如果 semval<=0,则wait
IPC_NOWAIT: 非阻塞(不等待)。P操作时如果 semval<=0,则不等待,直接返回-1
SEM_UNDO: 撤销。在进程退出时,操作系统会还原该进程对该信号量所做的操作。为了防止进程带锁死亡而造成死锁。
*/
};
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define PRO_PATH "/home/china"
#define PRO_ID 1234
int main()
{
int r;
/*step1: 用 ftok向内核申请一个system v ipc 对象的key */
key_t key = ftok(PRO_PATH, PRO_ID);
if(key == -1)
{
perror("ftok failed");
return -1;
}
/*step2: 用 semget 来创建或打开一个 system v 信号量集 对象*/
int semid = semget(key, 2, IPC_CREAT | 0664);
if(semid == -1)
{
perror("semget failed");
return -1;
}
/*step3: 用semctl给信号量赋初值*/
r = semctl(semid, 0, SETVAL, 1);
r = semctl(semid, 1, SETVAL, 1);
if(r == -1)
{
perror("semctl failed");
return -1;
}
/*step4: 用 semop 对 system v 信号量进行操作(主要是 P/V 操作)*/
struct sembuf sops[2];
sops[0].sem_num = 0;
sops[0].sem_op = -1;//P操作
sops[0].sem_flg=0;//阻塞等待
sops[1].sem_num = 1;
sops[1].sem_op = -1;//P操作
sops[1].sem_flg=0;//阻塞等待
r = semop(semid, sops, 2);
printf("i have the sem0 and sem1\n");
getchar();
sops[0].sem_num = 0;
sops[0].sem_op = 1;//V操作
sops[0].sem_flg=0;//阻塞等待
sops[1].sem_num = 1;
sops[1].sem_op = 1;//V操作
sops[1].sem_flg=0;//阻塞等待
r = semop(semid, sops, 2);
printf("i release the sem0 and sem1\n");
/*step4: 销毁信号量*/
r = semctl(semid, 0, IPC_RMID);
}
5,信号
5.1 发送信号
int kill(pid_t pid, int sig);
@pid: 指定信号的接收者(可能是多个进程)
pid > 0: 接收者的进程ID
pid==0: 发送信号给调用进程同组的所有进程
pid==-1: 发送信号给调用进程有权限发送的所有进程
pid<-1: 发送信号给组ID等于pid的绝对值的所有进程
@sig: 要发送的信号值
alarm 用来给调用进程设置一个闹钟,闹钟在超时的时候,就会闹(发送一个SIGALRM信号
给调用进程),一个进程在同一时刻只能有一个闹钟。
unsigned int alarm(unsigned int seconds);
@seconds: 设置闹钟的时间,以秒为单位
pause用来等待一个信号的到来,导致调用进程(或线程)休眠(阻塞在那里),直到收到一个信号
(被干掉/执行自己的信号处理函数)。
int pause(void);
5.2,改变信号的处理方式
sighandler_t signal(int signum, sighandler_t handler);
@signum: 要改变处理方式的信号的值
@handler: 指向信号处理函数的指针。有三种情况:
1) SIG_IGN: ignore 忽略
2) SIG_DFL: default采用操作系统默认的处理方式
3) 你自己写的信号处理函数指针
返回值:
如果成功返回该信号的前任处理方式
如果失败返回 SIG_ERR
注意:
信号处理方式,同样可以被fork克隆
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int terminal_flag=0;
void signal_handler(int sigval)
{
switch(sigval)
{
case SIGINT:
terminal_flag=1;break;
case SIGALRM:
puts("haha");
alarm(5);
break;
}
}
int main()
{
int a=2,b=3;
signal(SIGINT, signal_handler);
signal(SIGALRM, signal_handler);
alarm(5);
while(1);
}