无名管道只能实现有亲缘关系的进程之间的通信,比如父子进程。
pipe函数:
#include
int pipe(int pipefd[2]);//创建管道
参数:是我们得到的文件描述符
- fd[0]: 读端
- fd[1]: 写端
返回值:
- 成功创建返回0
- 创建失败返回-1.
#include
#include
#include
#include
#include
#include
int main(void)
{
pid_t pid;
int fd[2];
char buf[32] = {0};
// 在fork函数之前创建管道
pipe(fd);
printf("fd[0] is %d\n", fd[0]);
printf("fd[1] id %d\n", fd[1]);
pid = fork();
if (pid < 0) {
printf("fork error\n");
return -1;
}
// 父进程
if (pid > 0) {
int status;
close(fd[0]);
write(fd[1], "hello", 5);
close(fd[1]);
wait(&status);
exit(0);
}
// 子进程
if (pid == 0) {
close(fd[1]);
read(fd[0], buf, 32);//阻塞读
printf("buf is %s\n", buf);
close(fd[0]);
exit(0);
}
}
有名管道可以实现两个互不相关的进程间通信。
mkfifo 函数
#include
#include
int mkfifo(const char *pathname, mode_t mode);
参数:是我们得到的文件描述符
- pathname: 路径
- mode: 读写模式
返回值:
- 成功创建返回0
- 创建失败返回-1.
fifo_read.c
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
char buf[32] = {0};
int fd;
if (argc < 2)
{
printf("Usage: %s \n", arg[0]);
return -1;
}
fd = open(argv[1], O_RDONLY);
while (1)
{
sleep(1);
read(fd, buf, 32);
printf("buf is $s\n", buf);
memset(buf, 0, sizeof(buf));
}
close(fd);
return 0;
}
fifo_write.c
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int ret;
int fd;
if (argc < 2)
{
printf("Usage: %s \n", argv[0]);
return -1;
}
if (access(argv[1], F_OK) == -1) // 判断argv[1]传入的文件是否存在
{
ret = mkfifo(argv[1], 0666);
if (ret == -1)
{
printf("mkfifo error\n");
return -2;
}
printf("mkfifo ok\n");
}
fd = open(argv[1], O_WRONLY);
while (1)
{
sleep(1);
write(fd, "hello", 5);
}
close(fd);
return 0;
}
可以通过命令* " kill -l " *查看系统中有哪些信号。
kill函数:
#include
#include
int kill(pid_t pid, int sig);
raise函数:
#include
int raise(int sig);
raise函数等价于kill(getpid(), sig);
alarm函数:
设置一个时间值,当设置的时间到了就产生一个信号。
#include
unsigned int alarm(unsigned int seconds);
// 9号信号是“不能被捕获的进程终止信号”,进程接收到信号后默认操作是“终止进程”。
// 这个程序只会打印 "raise before\n"
#include
#include
#include
#include
#include
int main(void)
{
printf("raise before\n");
raise(9);
printf("raise after\n");
return 0;
}
kill.c
// 用法与“kill 命令用法相同”
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
pid_t pid;
int sig;
if (argc < 3)
{
printf("Usage:%s \n", argv[0]);
return -1;
}
sig = atoi(argv[2]);
pid = atoi(argv[1]);
kill(pid, sig);
return 0;
}
test.c 一秒钟打印一句helloworld
// 先运行test.c
// 使用 " ps aux | grep ./test " 查看pid号
// 运行kill,发送信号到 test进程。
#include
#include
int main(void)
{
while(1)
{
printf("hello world\n");
sleep(1);
}
return 0;
}
// 设置时间3秒,时间值到了以后,发送信号"SIGALRM",默认操作是终止该进程
#include
#include
#include
#include
#incldue < stdlib.h>
int main(int argc, char *argv[])
{
int i;
alarm(3);
while (1)
{
sleep(1);
i++;
printf("i = %d\n", i);
}
return 0;
}
如果一个进程要接收信号,需要这个进程不停止。一共有三种方式实现:
pause函数:
#include
int pause(void);
// pause() 会令目前的进程暂停(进入睡眠状态),直到被信号(signal)所中断。
// 返回值:只返回-1
信号处理的三种方式:系统默认,忽略,捕获
signal函数:
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
// 可以简化成:
// signal(参数1,参数2);
// 参数1:我们要进行处理的信号,系统的信号我们可以在终端键入 kill -l 查看。
// 参数2:处理的方式(是系统默认还是忽略还是捕获)。
三个例子:
“SIGINT” 信号由“ctrl+c”产生。
#include
#include
#include
int main(void)
{
signal(SIGINT, SIG_IGN); //忽略2号信号,ctrl+c无法终止该进程
//signal(SIGINT, SIG_DFL);//系统默认的处理方式
while (1)
{
printf("wait signal\n");
sleep(1);
}
return 0;
}
#include
#include
#include
void myfun(int sig)
{
if (sig == SIGINT) // 接收到“ctrl+c”打印“get signal\n”
{
printf("get signal\n");
}
}
int main(void)
{
signal(SIGINT, myfun);// 接收到2号信号,执行myfun函数
// 接收到其他信号,不会忽略,也不会执行myfun函数
while (1)
{
printf("wait signal\n");
sleep(1);
}
return 0;
}
#include
#include
int shmget(key_t key, size_t size, int shmflg);
#include
#include
#include
int main(void) {
int shmid;
shmid = shmget(IPC_PRIVATE, 1024, 0777);
if (shmid < 0) {
printf("shmget is error\n");
return -1;
}
printf("shmget is ok and shmid is %d\n", shmid);
return 0;
}
可以通过命令 ipcs -m 查看是否创建成功
可以使用命令 ipcrm -m ${shmid} 删除
ftok函数
#include
#include
key_t ftok(const char *pathname, int proj_id);
#include
#include
#include
#include
int main(void) {
int shmid;
key_t key;
key = ftok("./a.c", 'a');
shmid = shmget(key, 1024, 0777 | IPC_CREAT);// 使用ftok函数返回值时,需要加上IPC_CREAT权限,否则创建失败
if (shmid < 0) {
printf("shmget is error\n");
return -1;
}
printf("shmget is ok and shmid is %d\n", shmid);
return 0;
}
两种创建方式的区别,如果使用IPC_PRIVATE作为第一个参数,创建的共享内存,key值是0,如果使用ftok函数返回值创建,key值不是0。
shmat函数,创建共享内存用户空间的地址映射
#include
#include
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmdt函数,将进程里的共享内存地址映射删掉
#include
#include
int shmdt(const void *shmaddr);
注意:shmdt函数是将进程中的地址映射删除,也就是说当一个进程不需要共享内存的时候,就可以使用这个函数将他从进程地址空间中脱离,并不会删除内核里面的共享内存对象。
shmctl函数,删除共享内存
#include
#include
int shmctl(int shmid, int cmd, struct shmid_ds buf);
#include
#include
#include
#include
#include
#include
#include
#include
int main(void)
{
int shmid;
key_t key;
pid_t pid;
char *s_addr, *p_addr;
key = ftok("./a.c", 'a');
shmid = shmget(key, 1024, 0777 | ICP_CREATE);
if (shmid < 0)
{
printf("shmget is error\n");
return -1;
}
printf("shmget is ok and shmid is %d\n", shmid);
pid = fork();
if (pid > 0)
{
p_addr = shmat(shmid, NULL, 0);
strncpy(p_addr, "hello", 5);
wait(NULL);
exit(0);
}
if (pid == 0)
{
sleep(2);
s_addr = shmat(shmid, NULL, 0);
printf("saddr is %s\n", s_addr);
exit(0);
}
return 0;
}
msgget函数:创建消息队列
#include
#Include
#include
int msgget(key_t key, int msgflg);
msgctl函数:删除消息队列
#include
#include
#include
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
#include
#include
#include
#include
int main(void)
{
int msgid;
key_t key;
key = ftok("./a.c", 'a');
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid < 0)
{
printf("msgget is error\n");
}
printf("msgget is ok and msgid is %d\n", msgid);
return 0;
}
可以使用命令 tpcs -q 查看消息队列是否创建成功
msgsend函数:向消息队列发送消息
#include
#include
#include
int msgsend(int msqid, const void *msgp, size_t msgsz, int msgflg);
消息结构体,上文所述的发送的消息的字节数,仅包含消息内容‘mtext’的长度
struct msgbuf {
long mtype;//消息类型
char mtext[1]; //消息内容
}
msgrcv函数,接收消息
#include
#include
#include
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
消息队列写
#include
#include
#include
#include
#include
struct msgbuf {
long mtype;
char mtext[128];
};
int main(void)
{
int msgid;
key_t key;
struct msgbuf msg;
key = ftok("./a.c", 'a');
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid < 0)
{
printf("msgget is error\n");
}
printf("msgget is ok and msgid is %d\n", msgid);
msg.mtype = 1;
strncpy(msg.mtext, "hello", 5);
msgsnd(msqid, &msg, strlen(msg.mtext), 0);
return 0;
}
消息队列读
#include
#include
#include
#include
#include
struct msgbuf {
long mtype;
char mtext[128];
};
int main(void)
{
int msgid;
key_t key;
struct msgbuf msg;
key = ftok("./a.c", 'a');
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid < 0)
{
printf("msgget is error\n");
}
printf("msgget is ok and msgid is %d\n", msgid);
msgrcv(msgid, &msg, 128, 0, 0);
printf("msg.mtype is %ld\n", msg.mtype);
printf("msg.mtext is %s\n", msg.mtext);
return 0;
}
信号量不以传输数据为主要目的,他的主要作用是保护共享资源。
semget函数,获取信号量的ID
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
semctl函数
#include
#include
#include
int semctl(int semid, int semnum, int cmd, union semun arg);
semop函数
#include
#include
#include
int semop(int semid, struct sembuf *sops, size_t nsops);
sturct sembuf {
unsigned short sem_num; //要操作的信号量的编号
short sem_op; //P/V操作,1为V操作,释放资源。-1为P操作,分配资源。0为等待,直到信号量的值变成0
short sem_flg;//0表示阻塞,IPC_NOWAIT表示非阻塞
}
#include
#include
#include
#include
#include
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *buf;
};
int main(void)
{
int semid;
int key;
union semun semun_union;
pid_t pid;
struct sembuf sem;
key = ftok("./a.c", 0666);
semid = semget(key, 1, 0666 | IPC_CREAT);
semun_union.val = 0;
semctl(semid, 0, SETVAL, semun_union);
pid = fork();
if (pid > 0)// 父进程
{
sem.sem_num = 0;
sem.op = -1;
sem.flg = 0;
semop(semid, &sem, 1);
printf("This is parents\n");
}
if (pid == 0)// 子进程
{
sleep(2);
sem.sem_num = 0;
sem.op = 1;
sem.flg = 0;
semop(semid, &sem, 1);
printf("This is son\n");
}
return 0;
}