目录

知识点1【无名管道】(了解)

1、管道

2、无名管道 没有名字标记的管道

3、无名管道的特性

4、创建无名管道

5、案例1:父进程发 子进程收

6、无名管道的特点

知识点2【文件描述符复制----dup】(了解)

案例1:复制普通文件描述符

案例2:代码实现 ps -A | grep bash

知识点3【文件描述符复制----dup2】(了解)

知识点4【有名管道】命名管道(重要)

1、创建有名管道

2、使用open打开有名管道

3、完整通信案例

4、条件编译 将上面两个.c合成一个

5、单机QQ聊天程序

知识点5【消息队列】(了解)

1、消息队列是消息的链表,存放在内存中,由内核维护 消息队列的特点

2、创建消息队列

1、获取唯一的key 通过key得到唯一的消息队列标识符

2、创建消息队列

3、定义消息类型

4、发送消息

5、接收消息队列的消息

6、消息队列的控制

2、消息队列的案例:

知识点6【磁盘映射mmap】(了解)

1、建立磁盘文件和内存的映射

2、释放映射区

3、拓展文件大小

案例1:

02_write.c

02_read.c

知识点7【内存映射】(了解)

1、共享内存的特点

2、共享内存的步骤

1、获取唯一的Key值

2、获得一个共享存储标识符

3、共享区映射

4、解除共享映射区

5、共享内存控制

3、共享内存的案例

03_write.c

03_read.c

知识点8【信号】(了解)

1、信号的概述

信号的特点:

信号的编号:

信号的四要素:

信号的处理动作:

2、未决信号集和信号阻塞集

知识点9【发出信号的API】(了解)

1、kill函数

2、 raise函数

3、abort函数

4、alarm函数(闹钟)

5、 setitimer函数(定时器)

知识点10【信号的处理动作】(了解)

1、signal函数

2、 sigaction函数

知识点11【集合的操作】(了解)

1、集合的API

2、信号屏蔽集(信号阻塞集)


知识点1【无名管道】(了解)

1、管道

2、无名管道 没有名字标记的管道

无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符

3、无名管道的特性

1、半双工,数据在同一时刻只能在一个方向上流动。

2、数据只能从管道的一端写入,从另一端读出。

3、写入管道中的数据遵循先入先出的规则。

4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。

5、管道不是普通的文件,不属于某个文件系统,其只存在于内存中。

6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

7、从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。

8、管道没有名字,只能在具有公共祖先进程之间使用。(应用场景

4、创建无名管道

int fd[2];
#include <unistd.h>int pipe(int fd[2]);
功能:经由参数filedes返回两个文件描述符
参数:fd为int型数组的首元素地址,其存放了管道的文件描述符fd[0]、fd[1]。fd[0]为读而打开,fd[1]为写而打开管道。
返回值:成功:返回 0失败:返回-1

5、案例1:父进程发 子进程收

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>int main(int argc, char const *argv[])
{//创建无名管道int fd[2];pipe(fd);char buf[128] = "";pid_t pid = fork();if(pid == 0){close(fd[1]);int ret = 0;while(ret = read(fd[0], buf, sizeof(buf))){if(ret == 0){printf("写端关闭\n");break;}printf("进程%d接受数据:%s\n", getpid(), buf);}close(fd[0]);_exit(-1);}else if(pid > 0){close(fd[0]);while(1){fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = 0;write(fd[1], buf, strlen(buf));printf("进程%d发送了消息:%s\n", getpid(), buf);if(strcmp(buf, "bye") == 0)break;}}return 0;
}/**
brightsky@Bright-SKY:~/codes/blog$ ./a.out
123
进程13586发送了消息:123
进程13587接受数据:123
234
进程13586发送了消息:234
进程13587接受数据:234
345
进程13586发送了消息:345
进程13587接受数据:345
bye
进程13586发送了消息:bye
进程13587接受数据:bye*/

6、无名管道的特点

1、默认用read函数从管道中读数据阻塞的。

2、调用write函数向管道里写数据,当缓冲区已满时write也会阻塞

3、通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收到SIGPIPE信号)退出。 从管道中读数据的特点  编程时可通过fcntl函数设置文件的阻塞特性。

设置为阻塞:  fcntl(fd, FSETFL, 0);

设置为非阻塞:  fcntl(fd, FSETFL, O_NONBLOCK);

知识点2【文件描述符复制----dup】(了解)

#include <unistd.h>
int dup(int oldfd);
oldfd:需要复制的文件描述符
返回值:复制成功的文件描述符(最小可用)newfd

newfdoldfd指向 同一个文件

案例1:复制普通文件描述符

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>int main(int argc, char const *argv[])
{int fd = open("a.txt", O_WRONLY|O_CREAT, 0666);printf("fd = %d\n", fd);//复制fdint newfd = dup(fd);printf("newfd = %d\n", newfd);write(fd,"hello", 5);write(newfd,"world", 5);close(fd);close(newfd);return 0;
}//fd = 3
//newfd = 4

案例2:代码实现 ps -A | grep bash

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>int main(int argc, char const *argv[])
{//创建管道int fd[2];pipe(fd);//创建进程pid_t pid = fork();if(pid == 0)//子进程{   //关闭写端close(fd[1]);//执行grep bash从管道的 读端获取数据//关闭0描述符close(0);//将0复制到fd[0]上dup(fd[0]);//执行grep bashexeclp("grep", "grep", "bash", NULL);//关闭读端close(fd[0]);_exit(-1);}else if(pid >0)//父进程{   //关闭读端close(fd[0]);//执行ps -A 将结果输出到 无名管道的写端//关闭1描述符close(1);//将1复制到fd[1]dup(fd[1]);//执行ps -Aexeclp("ps", "ps", "-A", NULL);//关闭写端close(fd[1]);wait(NULL);}return 0;
}brightsky@Bright-SKY:~/codes/blog$ ./a.out
brights+    3113  0.0  0.0  19752  5316 pts/0    Ss   08:23   0:00 /usr/bin/bash
brights+   13037  0.0  0.0  19648  5540 pts/1    Ss+  10:19   0:00 bash
brights+   14721  0.0  0.0  17556   736 pts/0    S+   15:04   0:00 grep bash

知识点3【文件描述符复制----dup2】(了解)

int dup2(int oldfd, int newfd);

让newfd指向oldfd指向的文件:先关闭newfd, 然后newfd复制到oldfd上

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>int main(int argc, char const *argv[])
{int fd[2];pipe(fd);pid_t pid = fork();if(pid == 0){close(fd[0]);close(1);dup2(fd[1], 1);execlp("ps", "ps", "-aux", NULL);}else if(pid > 0){close(fd[1]);close(0);dup2(fd[0], 0);execlp("grep", "grep", "bash", NULL);}return 0;
}brightsky@Bright-SKY:~/codes/blog$ ./a.out
brights+    3113  0.0  0.0  19752  5316 pts/0    Ss   08:23   0:00 /usr/bin/bash
brights+   13037  0.0  0.0  19648  5540 pts/1    Ss+  10:19   0:00 bash
brights+   14894  0.0  0.0  17556   724 pts/0    S+   15:27   0:00 grep bash

知识点4【有名管道】命名管道(重要)

有名管道:用于没有血缘关系的进程间通信。

1、创建有名管道

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo( const char *pathname, mode_t mode);
参数:pathname:FIFO的路径名+文件名。mode:mode_t类型的权限描述符
返回值:成功:返回 0失败:如果文件已经存在,则会出错且返回-1

2、使用open打开有名管道

int fd = open("a.txt", O_WRONLY);
int fd = open("a.txt", O_RDONLY);

的方式打开 会阻塞到 另一个进程以的方式打开

的方式打开 会阻塞到 另一个进程以的方式打开

3、完整通信案例

08_write.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{//创建一个有名管道mkfifo("a.txt", 0666);//以写的方式打开有名管道int fd = open("a.txt", O_WRONLY);if(fd < 0){perror("open");return 0;}printf("写端打开了\n");//获取键盘输入 并向有名管道发送数据while(1){char buf[128]="";printf("请输入发送的内容:");fgets(buf, sizeof(buf), stdin);buf[strlen(buf)-1]=0;//发送数据write(fd, buf, strlen(buf));if(strcmp(buf,"bye")==0)break;}close(fd);return 0;
}

08_read.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{//创建一个有名管道mkfifo("a.txt", 0666);//以写的方式打开有名管道int fd = open("a.txt", O_RDONLY);if(fd < 0){perror("open");return 0;}printf("读端打开了\n");//不停的从有名管道读取内容while(1){char buf[128]="";read(fd, buf,sizeof(buf));printf("读到的内容:%s\n",buf);if(strcmp(buf,"bye")==0)break;}close(fd);return 0;
}

4、条件编译 将上面两个.c合成一个

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{//创建一个有名管道mkfifo("a.txt", 0666);//打开有名管道#ifdef WRITEint fd = open("a.txt", O_WRONLY);#endif#ifdef READint fd = open("a.txt", O_RDONLY);#endifif(fd < 0){perror("open");return 0;}printf("写端打开了\n");#ifdef WRITE//获取键盘输入 并向有名管道发送数据while(1){char buf[128]="";printf("请输入发送的内容:");fgets(buf, sizeof(buf), stdin);buf[strlen(buf)-1]=0;//发送数据write(fd, buf, strlen(buf));if(strcmp(buf,"bye")==0)break;}#endif#ifdef READ//不停的从有名管道读取内容while(1){char buf[128]="";read(fd, buf,sizeof(buf));printf("读到的内容:%s\n",buf);if(strcmp(buf,"bye")==0)break;}#endifclose(fd);return 0;
}

5、单机QQ聊天程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>int main(int argc, char const *argv[])
{mkfifo("myfifo_1", 0664);mkfifo("myfifo_2", 0664);#ifdef BOBint fd1 = open("myfifo_1", O_WRONLY);int fd2 = open("myfifo_2", O_RDONLY);
#endif#ifdef LUCYint fd2 = open("myfifo_1", O_RDONLY);int fd1 = open("myfifo_2", O_WRONLY);
#endifchar buf[128] = "";int num = 0;for(num=0; num<2; num++){pid_t pid = fork();if(pid == 0)break;}switch (num){case 0:while(1){fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = '\0';write(fd1, buf, strlen(buf));if(strcmp(buf, "bye") == 0){printf("写结束\n");break;}bzero(buf, strlen(buf));}break;case 1:while(read(fd2, buf, sizeof(buf)) != 0){printf("接收到:%s\n", buf);if(strcmp(buf, "bye") == 0){printf("读结束\n");break;}bzero(buf, strlen(buf));}break;case 2:while(wait(NULL) != -1);break;}return 0;
}/**
brightsky@Bright-SKY:~/codes/blog$ ./lucy
123
接收到:123
nihao
接收到:wohao
bye
写结束
接收到:bye
读结束brightsky@Bright-SKY:~/codes/blog$ ./bob
接收到:123
123
接收到:nihao
wohao
接收到:bye
读结束
bye
写结束*/

知识点5【消息队列】(了解)

1、消息队列是消息的链表,存放在内存中,由内核维护 消息队列的特点

1、消息队列中的消息是有类型的。

2、消息队列中的消息是有格式的。

3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。

4、消息队列允许一个或多个进程向它写入或者读取消息。

5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。

6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。

7、只有内核重启人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统

2、创建消息队列

        1、获取唯一的key 通过key得到唯一的消息队列标识符

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:获得项目相关的唯一的IPC键值。
参数:pathname:路径名proj_id:项目ID,非0整数(只有低8位有效)
返回值:成功返回key值,失败返回 -1

    2、创建消息队列

#include <sys/msg.h>
int msgget(key_t key, int msgflg);key:IPC键值。msgflg:标识函数的行为及消息队列的权限。参数:msgflg的取值:IPC_CREAT:创建消息队列。IPC_EXCL:检测消息队列是否存在。位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和open函数的mode_t一样,但可执行权限未使用。返回值:成功:消息队列的标识符,失败:返回-1查看消息队列:ipcs -q删除消息队列:ipcrm -q  msqid

        3、定义消息类型

typedef struct _msg
{long mtype;      /*消息类型*/char mtext[100]; /*消息正文*/... /*消息的正文可以有多个成员*/
}MSG;

        4、发送消息

#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgflg);
功能:将新消息添加到消息队列。参数:msqid:消息队列的标识符。msgp:待发送消息结构体的地址。msgsz:消息正文的字节数。msgflg:函数的控制属性0:msgsnd调用阻塞直到条件满足为止。IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回。返回值:成功:0;失败:返回-1

        5、接收消息队列的消息

#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp,  size_t msgsz, long msgtyp, int msgflg);
功能:从标识符为msqid的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除。参数:msqid:消息队列的标识符,代表要从哪个消息列中获取消息。msgp: 存放消息结构体的地址。msgsz:消息正文的字节数。msgtyp:消息的类型、可以有以下几种类型msgtyp = 0:返回队列中的第一个消息msgtyp > 0:返回队列中消息类型为msgtyp的消息msgtyp < 0:返回队列中消息类型值小于或等于msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息msgflg:函数的控制属性0:msgrcv调用阻塞直到接收消息成功为止。MSG_NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。IPC_NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1。返回值:成功返回读取消息的长度,失败返回-1

        6、消息队列的控制

#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列。参数:msqid:消息队列的标识符。cmd:函数功能的控制。buf:msqid_ds数据类型的地址,用来存放或更改消息队列的属性。cmd:函数功能的控制IPC_RMID:删除由msqid指示的消息队列,将它从系统中删除并破坏相关数据结构。IPC_STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中。IPC_SET:将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值。返回值:成功:返回 0;失败:返回 -1

发送方:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
//定义消息类型
typedef struct
{long mtype;//消息类型char mtext[128];//消息正文
}MSG;int main(int argc, char const *argv[])
{//得到唯一的key值key_t key = ftok("/",2101);printf("key=%x\n", key);//创建消息队列int msgid = msgget(key, IPC_CREAT|0666);printf("msgid=%d\n", msgid);//发送消息MSG msg;msg.mtype = 10;//接受者的接收类型必须是10才能收到该数据strcpy(msg.mtext,"send data");msgsnd(msgid, &msg, sizeof(MSG)-sizeof(long), 0);return 0;
}

接收方:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
//定义消息类型
typedef struct
{long mtype;//消息类型char mtext[128];//消息正文
}MSG;
int main(int argc, char const *argv[])
{//得到唯一的key值key_t key = ftok("/",2101);printf("key=%x\n", key);//创建消息队列int msgid = msgget(key, IPC_CREAT|0666);printf("msgid=%d\n", msgid);//接收消息队列中的消息MSG msg;int len = msgrcv(msgid, &msg, sizeof(MSG)-sizeof(long), 10, 0);printf("len=%d msg=%s\n", len, msg.mtext);//删除消息队列(尽量别调用 会删除消息队列的)msgctl(msgid, IPC_RMID, NULL);return 0;
}

2、消息队列的案例:

typedef struct msg
{long type;    //接收者类型char text[100];   //发送内容char name[20];    //发送者姓名
}MSG;

每个程序都有两个任务,一个任务是负责接收消息,一个任务是负责发送消息,通过fork创建子进程实现多任务。  一个进程负责接收信息,它只接收某种类型的消息,只要别的进程发送此类型的消息,此进程就能收到。收到后通过消息的name成员就可知道是谁发送的消息。   另一个进程负责发信息,可以通过输入来决定发送消息的类型。  设计程序的时候,接收消息的进程接收消息的类型不一样,这样就实现了发送的消息只被接收此类型消息的人收到,其它人收不到。这样就是实现了给特定的人发送消息。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>typedef struct{long mtype;char text[1024];char name[32];
}MSG;#ifdef BOBchar *myname = "bob";
#endif#ifdef LUCYchar *myname = "lucy";
#endif#ifdef JACKchar *myname = "jack";
#endifint main(int argc, char const *argv[])
{char *names[] = {"bob", "lucy", "jack"};int name_num = sizeof(names)/sizeof(names[0]); key_t key = ftok("/", 2101);int msgid = msgget(key, IPC_CREAT|0664);MSG msg;int num = 0;for(num=0; num<2; num++)if(fork() == 0)break;switch (num){case 0:while(1){int i = 0;for(i=0; i<name_num; i++)if(strcmp(names[i], myname) == 0)msg.mtype = i + 1;msgrcv(msgid, &msg, sizeof(MSG) - sizeof(long), msg.mtype, 0);printf("%s:%s\n", msg.name, msg.text);} break;case 1:while(1){bzero(&msg, sizeof(msg));scanf("%s %s", msg.name, msg.text);int i = 0;for(i=0; i<name_num; i++)if(strcmp(names[i], msg.name) == 0)msg.mtype = i + 1;strcpy(msg.name, myname);msgsnd(msgid, &msg, sizeof(MSG) - sizeof(long), 0);}break;case 2:while(wait(NULL) != -1);break;}return 0;
}/**
brightsky@Bright-SKY:~/codes/blog$ ./bob
jack 123
lucy:byebrightsky@Bright-SKY:~/codes/blog$ ./jack
bob:123
lucy nihaobrightsky@Bright-SKY:~/codes/blog$ ./lucy
jack:nihao
bob bye*/

知识点6【磁盘映射mmap】(了解)

1、建立磁盘文件和内存的映射

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
addr 地址,填NULLlength 长度 要申请的映射区的长度prot 权限PROT_READ 可读PROT_WRITE 可写flags 标志位MAP_SHARED 共享的 -- 对映射区的修改会影响源文件MAP_PRIVATE 私有的fd 文件描述符 需要打开一个文件offset  指定一个偏移位置 ,从该位置开始映射返回值成功 返回映射区的首地址失败 返回 MAP_FAILED ((void *) -1)

2、释放映射区

int munmap(void *addr, size_t length);
addr  映射区的首地址
length 映射区的长度
返回值成功 返回0失败 返回 -1

3、拓展文件大小

int truncate(const char *path, off_t length);
path  要拓展的文件
length 要拓展的长度

案例1:

02_write.c

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc, char const *argv[])
{//1、open打开需要映射 磁盘文件int fd = open("tmp.txt", O_RDWR|O_CREAT, 0666);if(fd < 0){perror("open");}//2、拓展文件的大小truncate("tmp.txt", 16);//3、建立磁盘文件和内存的映射char *str=NULL;str = (char *)mmap(NULL, 16, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);//4、往str指向的空间 就代表磁盘文件(写)strcpy(str, "hehe");//5、解除映射munmap(str, 16);return 0;
}

02_read.c

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc, char const *argv[])
{//1、open打开需要映射 磁盘文件int fd = open("tmp.txt", O_RDWR|O_CREAT, 0666);if(fd < 0){perror("open");}//2、拓展文件的大小truncate("tmp.txt", 16);//3、建立磁盘文件和内存的映射char *str=NULL;str = (char *)mmap(NULL, 16, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);//4、往str指向的空间 就代表磁盘文件(读)printf("读到的内容:%s\n", str);//5、解除映射munmap(str, 16);return 0;
}

知识点7【内存映射】(了解)

1、共享内存的特点

1、共享内存是进程间共享数据的一种最快的方法。   一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。

2、使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。   若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据

2、共享内存的步骤

1、获取唯一的Key值

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:获得项目相关的唯一的IPC键值。
参数:pathname:路径名proj_id:项目ID,非0整数(只有低8位有效)
返回值:成功返回key值,失败返回 -1

2、获得一个共享存储标识符

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,int shmflg);
功能:创建或打开一块共享内存区
参数:key:IPC键值size:该共享存储段的长度(字节)shmflg:标识函数的行为及共享内存的权限。
参数:
shmflg:IPC_CREAT:如果不存在就创建IPC_EXCL:如果已经存在则返回失败位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,格式和open函数的mode_t一样,但可执行权限未使用。
返回值:成功:返回共享内存标识符。失败:返回-1

查看共享内存  ipcs -m  删除共享内存  ipcrm -m shmid

3、共享区映射

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr,int shmflg);
功能:将一个共享内存段映射到调用进程的数据段中。
参数:shmid:共享内存标识符。shmaddr:共享内存映射地址(若为NULL则由系统自动指定),推荐使用NULL。shmflg:共享内存段的访问权限和映射条件0:共享内存具有可读可写权限。SHM_RDONLY:只读。SHM_RND:(shmaddr非空时才有效)没有指定SHM_RND则此段连接到shmaddr所指定的地址上(shmaddr必需页对齐)。指定了SHM_RND则此段连接到shmaddr- shmaddr%SHMLBA 所表示的地址上。
返回值:成功:返回共享内存段映射地址失败:返回 -1

4、解除共享映射区

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。
参数:shmaddr:共享内存映射地址。
返回值:成功返回 0,失败返回 -1。

5、共享内存控制

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd,struct shmid_ds *buf);
功能:共享内存空间的控制。
参数:
shmid:共享内存标识符。
cmd:函数功能的控制。
buf:shmid_ds数据类型的地址,用来存放或修改共享内存的属性。cmd:函数功能的控制IPC_RMID:删除。IPC_SET:设置shmid_ds参数。IPC_STAT:保存shmid_ds参数。SHM_LOCK:锁定共享内存段(超级用户)。SHM_UNLOCK:解锁共享内存段。返回值:成功返回 0,失败返回 -1。

注意: 

SHM_LOCK用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。  这样做的优势在于让共享内存一直处于内存中,从而提高程序性能

3、共享内存的案例

03_write.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(int argc, char const *argv[])
{//ftok获取唯一的key值key_t key = ftok("/", 2101);//根据key值 得到内存映射标识符int shmid =  shmget(key, 32,IPC_CREAT|0666);//建立映射区char *str = (char *)shmat(shmid, NULL, 0);//写操作strcpy(str, "hello share mem");//解除映射shmdt(str);return 0;
}

03_read.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(int argc, char const *argv[])
{//ftok获取唯一的key值key_t key = ftok("/", 2101);//根据key值 得到内存映射标识符int shmid =  shmget(key, 32,IPC_CREAT|0666);//建立映射区char *str = (char *)shmat(shmid, NULL, 0);//读操作printf("读到的数据:%s\n", str);//解除映射shmdt(str);return 0;
}

知识点8【信号】(了解)

1、信号的概述

信号:软件中断 异步的

        信号的特点:

简单  不能携带大量信息  满足某个特设条件才发送。

        信号的编号:

ctrl+c --->2 SIGINT

ctrl+/ --->3 SIGQUIT

ctrl+Z --->19 SIGSTOP

9 SIGKILL 不可忽略的

信号的四要素:

1)编号         2)名称         3)触发方式         4)默认处理动作

信号的处理动作:

1)默认动作         2)忽略信号         3)自定义动作

2、未决信号集和信号阻塞集

上面两个集合是在进程的PCB中。

未决信号集:集合中的所有信号 未被处理 状态为1

信号阻塞集:集合中的所有信号 被屏蔽 无法处理 但是信号一旦从阻塞集中出来 该信号被立即处理

知识点9【发出信号的API】(了解)

1、kill函数

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
功能:给指定进程发送指定信号(不一定杀死)
参数:pid : 取值有 4 种情况 :pid > 0:  将信号传送给进程 ID 为pid的进程。pid = 0 :  将信号传送给当前进程所在进程组中的所有进程。pid = -1 : 将信号传送给系统内所有的进程。pid < -1 : 将信号传给指定进程组的所有进程。这个进程组号等于 pid 的绝对值。sig : 信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令 kill - l("l" 为字母)进行相应查看。不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致。
返回值:成功:0失败:-1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>int main(int argc, char const *argv[])
{pid_t pid = fork();if(pid == 0){int i = 5;for(i=5; i>0; i--){printf("子进程%d中的i=%d\n", getpid(), i);sleep(1);}_exit(-1);}else if(pid > 0){sleep(3);kill(pid, SIGKILL);pid = wait(NULL);printf("子进程:%d退出了\n", pid);}return 0;
}brightsky@Bright-SKY:~/codes/blog$ ./a.out
子进程16741中的i=5
子进程16741中的i=4
子进程16741中的i=3
子进程:16741退出了

2、 raise函数

#include <signal.h>
int raise(int sig);
功能:给当前进程发送指定信号(自己给自己发),等价于 kill(getpid(), sig)
参数:sig:信号编号
返回值:成功:0失败:非0值
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>int main(int argc, char const *argv[])
{printf("5s后,当前进程结束\n");sleep(5);raise(SIGKILL);return 0;
}brightsky@Bright-SKY:~/codes/blog$ ./a.out
5s后,当前进程结束
已杀死

3、abort函数

#include <stdlib.h>
void abort(void);
功能:给自己发送异常终止信号 6) SIGABRT,并产生core文件,等价于kill(getpid(), SIGABRT);
参数:无
返回值:无
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>int main(int argc, char const *argv[])
{printf("5s后,当前进程结束\n");sleep(5);abort();return 0;
}brightsky@Bright-SKY:~/codes/blog$ ./a.out
5s后,当前进程结束
已放弃 (核心已转储)

4、alarm函数(闹钟)

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终止。每个进程都有且只有唯一的一个定时器。取消定时器alarm(0),返回旧闹钟余下秒数。
参数:seconds:指定的时间,以秒为单位
返回值:返回0或剩余的秒数定时,与进程状态无关(自然定时法)!就绪、运行、挂起(阻塞、暂停)、
终止、僵尸……无论进程处于何种状态,alarm都计时
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>int main(int argc, char const *argv[])
{printf("5s后,当前进程结束\n");alarm(5);sleep(3);int s = alarm(5);printf("剩余秒数:%d\n", s);while(1);return 0;
}brightsky@Bright-SKY:~/codes/blog$ ./a.out
5s后,当前进程结束
剩余秒数:2
闹钟

5、 setitimer函数(定时器)

#include <sys/time.h>
int setitimer(int which,  const struct itimerval *new_value,
struct itimerval *old_value);
功能:设置定时器(闹钟)。 可代替alarm函数。精度微秒us,可以实现周期定时。
参数:which:指定定时方式a) 自然定时:ITIMER_REAL → 14)SIGALRM计算自然时间b) 虚拟空间计时(用户空间):ITIMER_VIRTUAL → 26)SIGVTALRM  只计算进程占用cpu的时间c) 运行时计时(用户 + 内核):ITIMER_PROF → 27)SIGPROF计算占用cpu及执行系统调用的时间new_value:struct itimerval, 负责设定timeout时间struct itimerval {struct timerval it_interval; // 闹钟触发周期struct timerval it_value;    // 闹钟触发时间};struct timeval {long tv_sec;            // 秒long tv_usec;           // 微秒}itimerval.it_value: 设定第一次执行function所延迟的秒数itimerval.it_interval:  设定以后每几秒执行functionold_value: 存放旧的timeout值,一般指定为NULL
返回值:成功:0失败:-1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <signal.h>void myfunc(int sig){printf("hello\n");
}int main(int argc, char const *argv[])
{struct itimerval new_value;//定时周期new_value.it_interval.tv_sec = 1;new_value.it_interval.tv_usec = 0;//第一次触发时间new_value.it_value.tv_sec = 5;new_value.it_value.tv_usec = 0;signal(SIGALRM, myfunc);setitimer(ITIMER_REAL, &new_value, NULL);while(1);return 0;
}brightsky@Bright-SKY:~/codes/blog$ ./a.out
hello        //第一次触发时间5S
hello
hello        //间隔周期1S
hello
hello

知识点10【信号的处理动作】(了解)

信号的自定义动作:注册信号的自定义处理函数

【注意】:SIGKILL 9和 SIGSTOP 19不能更改信号的处理方式,因为它们向用户提供了一种使进程终止的可靠方法。

1、signal函数

#include <signal.h>
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:注册信号处理函数(不可用于 SIGKILL、SIGSTOP 信号),即确定收到信号后处理函数的入口地址。此函数不会阻塞。
参数:signum:信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令 kill - l("l" 为字母)进行相应查看。handler : 取值有 3 种情况:SIG_IGN:忽略该信号SIG_DFL:执行系统默认动作信号处理函数名:自定义信号处理函数,如:func回调函数的定义如下:void func(int signo){// signo 为触发的信号,为 signal() 第一个参数的值}
返回值:成功:第一次返回 NULL,下一次返回此信号上一次注册的信号处理函数的地址。如果需要使用此返回值,必须在前面先声明此函数指针的类型。失败:返回 SIG_ERR
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <signal.h>void signal_handler(int sig){if(sig == SIGINT){printf("recv SIGINT\n");_exit(-1);}else if(sig == SIGQUIT){printf("recv SIGQUIT\n");_exit(-1);}
}int main(int argc, char const *argv[])
{printf("wait for SIGINT OR SIGQUIT\n");//SIGINT : Ctrl+c   SIGQUIT : Ctrl+\signal(SIGINT, signal_handler);signal(SIGQUIT, signal_handler);while(1);return 0;
}brightsky@Bright-SKY:~/codes/blog$ ./a.out
wait for SIGINT OR SIGQUIT
^Crecv SIGINT
brightsky@Bright-SKY:~/codes/blog$ ./a.out
wait for SIGINT OR SIGQUIT
^\recv SIGQUIT

2、 sigaction函数

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
功能:检查或修改指定信号的设置(或同时执行这两种操作)。
参数:signum:要操作的信号。act:   要设置的对信号的新处理方式(传入参数)。oldact:原来对信号的处理方式(传出参数)。如果 act 指针非空,则要改变指定信号的处理方式(设置),如果 oldact 指针非空,则系统将此前指定信号的处理方式存入 oldact。
返回值:成功:0失败:-1

struct sigaction结构体:

struct sigaction {void(*sa_handler)(int); //旧的信号处理函数指针void(*sa_sigaction)(int, siginfo_t *, void *); //新的信号处理函数指针sigset_t   sa_mask;      //信号阻塞集int        sa_flags;     //信号处理的方式void(*sa_restorer)(void); //已弃用
};

1) sahandler、sasigaction:信号处理函数指针,和 signal() 里的函数指针用法一样,应根据情况给sasigaction、sahandler 两者之一赋值,其取值如下:

a) SIG_IGN:忽略该信号

b) SIG_DFL:执行系统默认动作

c) 处理函数名:自定义信号处理函数

2) sa_mask:信号阻塞集,在信号处理函数执行过程中,临时屏蔽指定的信号。

3) saflags:用于指定信号处理的行为,通常设置为0,表使用默认属性。它可以是一下值的“按位或”组合:

SARESTART:使被信号打断的系统调用自动重新发起(已经废弃)

SANOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。

SANOCLDWAIT:使父进程在它的子进程退出时不会收到

SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。

SANODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。

SARESETHAND:信号处理之后重新设置为默认的处理方式。

SASIGINFO:使用 sasigaction 成员而不是 sahandler 作为信号处理函数

信号处理函数:

void(*sa_sigaction)(int signum, siginfo_t *info, void *context);
参数说明:signum:信号的编号。info:记录信号发送进程信息的结构体。context:可以赋给指向 ucontext_t 类型的一个对象的指针,以引用在传递信号时被中断的接收进程或线程的上下文
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/time.h>
void my_func(int sig)
{printf("ctrl+c被按下了\n");
}
int main()
{struct sigaction act;//act存放回调函数act.sa_handler = my_func;//act添加阻塞集 act.sa_mask sigemptyset(&act.sa_mask);//SA_RESETHAND:信号处理之后重新设置为默认的处理方式act.sa_flags=0;//默认方式//act.sa_flags |= SA_RESETHAND;sigaction(SIGINT, &act, NULL);while(1);return 0;
}

知识点11【集合的操作】(了解)

1、集合的API

#include <signal.h>
int sigemptyset(sigset_t *set);       //将set集合置空
int sigfillset(sigset_t *set);          //将所有信号加入set集合
int sigaddset(sigset_t *set, int signo);  //将signo信号加入到set集合
int sigdelset(sigset_t *set, int signo);   //从set集合中移除signo信号
int sigismember(const sigset_t *set, int signo); //判断信号是否存在
#include <stdio.h>
#include <signal.h>
int main(int argc, char const *argv[])
{//定义一个信号集合sigset_t set;//清空集合sigemptyset(&set);//将SIGINT添加到集合中sigaddset(&set, SIGINT);//判断SIGINT在集合中if(sigismember(&set, SIGINT)){printf("SIGINT在集合中\n");}//将SIGINT从集合中去除sigdelset(&set, SIGINT);if(sigismember(&set, SIGINT)==0){printf("SIGINT不在集合中\n");}return 0;
}

2、信号屏蔽集(信号阻塞集)

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:检查或修改信号阻塞集,根据 how 指定的方法对进程的阻塞集合进行修改,新的信号阻塞集由 set 指定,而原先的信号阻塞集合由 oldset 保存
参数:howSIG_BLOCK:向信号阻塞集合中添加 set 信号集,新的信号掩码是set和旧信号掩码的并集。相当于 mask = mask|setSIG_UNBLOCK:从信号阻塞集合中删除 set 信号集,从当前信号掩码中去除 set 中的信号。相当于 mask = mask & ~ setSIG_SETMASK:将信号阻塞集合设为 set 信号集,相当于原来信号阻塞集的内容清空,然后按照 set 中的信号重新设置信号阻塞集。相当于mask = set
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <signal.h>int main(int argc, char const *argv[])
{//创建信号集合sigset_t set;//清空信号集合sigemptyset(&set);//将SIGINT加入信号集合sigaddset(&set, SIGINT);//将set集合设置阻塞集中 用old_set备份阻塞集sigprocmask(SIG_BLOCK, &set, NULL);printf("SIGINT信号被阻塞10s\n");sleep(10);printf("SIGINT解除阻塞\n");sigprocmask(SIG_UNBLOCK, &set, NULL);while(1);return 0;
}brightsky@Bright-SKY:~/codes/blog$ ./a.out
SIGINT信号被阻塞10s
^C^C^C^C^CSIGINT解除阻塞

Linux_进程间通信(详解)相关推荐

  1. C++进程间通信 详解2

    文章目录 一.概述 二.进程间通信概念及方法 1. 管道的概念 2. pipe 3. 管道的读写行为 4. 管道缓冲区大小 5. 管道优劣 6. FIFO 7. 共享存储映射 8. mmap函数 9. ...

  2. C++ 进程间通信详解

    一,C++ 常用进程间通信 管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信. 命名管道(named pipe):命名管道克服了管道没有名字的 ...

  3. Linux进程间通信详解

    之前我总结了有关进程及进程控制的相关知识,不是很了解的朋友可以看一看: 进程:https://blog.csdn.net/Sun_Life_/article/details/88580785 进程控制 ...

  4. Linux进程间通信详解(四) —— 共享内存及函数

    共享内存的概念 共享内存是指多个进程可以把一段内存共同的内存映射到自己的进程空间中,从而实现数据的共享和传输,它是存在与内核级别的一种资源,是所有进程间通信中方式最快的一种. 在shell环境下可以使 ...

  5. exit函数_Linux进程间通信详解(三) 消息队列及函数

    消息队列的概念 消息队列就是一个消息的链表,每个消息队列都有一个队列头,用结构struct msg_queue来描述.队列头中包含了该队列的大量信息,包括消息队列的键值.用户ID.组ID.消息数目.读 ...

  6. Linux进程间通信详解(三) —— 消息队列及函数

    消息队列的概念 消息队列就是一个消息的链表,每个消息队列都有一个队列头,用结构struct msg_queue来描述.队列头中包含了该队列的大量信息,包括消息队列的键值.用户ID.组ID.消息数目.读 ...

  7. OS--进程间通信详解(二)

    OS–进程间通信详解(二) 文章目录 OS--进程间通信详解(二) 一.进程间通信 1.互斥量 Futexes Pthreads中的互斥量 2.管程 3.消息传递 消息传递系统的设计要点 用消息传递解 ...

  8. OS--进程间通信详解(一)

    OS–进程间通信详解(一) 文章目录 OS--进程间通信详解(一) 一.进程间通信 1.竞态条件 2.临界区 3.忙等互斥 屏蔽中断 锁变量 严格轮询法 Peterson 解法 TSL指令 4.睡眠与 ...

  9. Linux C编程--进程间通信(IPC)4--管道详解

    linux管道 管道相关内容的简介 管道是单向的字节流,它将某个进程的标准输出连接到另一个进程的标准输入.管道和有名管道是最早的进程间通信机制之一,管道可用于具有亲缘关系进程间的通信,有名管道克服了管 ...

  10. linux 进程间通信 dbus-glib【实例】详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)

    linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...

最新文章

  1. bulma.css_如何建立一个? 具有Bulma CSS的特斯拉响应页面
  2. php成绩管理前段模板,php学生成绩管理系统(模板).doc
  3. Android studio之如何快速查看页面的布局
  4. 对51CTO的初步看法
  5. 本地安装Docker
  6. 一款社区论坛小程序源码
  7. (06)Verilog HDL组合逻辑:always
  8. oracle复制表结构与表数据
  9. SpringSecurity-1-UserDetailsService接口
  10. 普通网站防暴力破解的新设计
  11. Windows-局域网文件服务器文件共享软件 FtpServer
  12. 【02】Java进阶:17-单例设计模式、多例设计模式、枚举、工厂设计模式、Lombok
  13. Windows调出屏幕键盘的步骤
  14. Codeforces 1419B. Stairs
  15. 银行软件测试论文参考文献,软件测试毕业论文参考文献.doc
  16. 王阳明:越是多变时,越要学会进化(附个体进化的底层心力逻辑)
  17. office2016选中、编辑突然变慢
  18. 【pion】ice-single-port解析
  19. iOS8 苹果自带的毛玻璃效果
  20. python数据分析:新闻文本聚类

热门文章

  1. Socket接口测试
  2. 画出识别c语言注释的转换图,C语言程序设计基础与实训教程》第1章:基础知识.ppt...
  3. 匿名自执行函数是闭包吗?
  4. 1032 挖掘机技术哪家强(测试点2)
  5. 31 Linux input子系统按键驱动--4IO驱动16按键
  6. Linux时间变慢解决方法
  7. 如何创建java bean_eclipse如何创建javabean?怎样在eclipse当中创建?
  8. 车载小程序,数字化赋予出行新体验
  9. WinDbg重建堆栈
  10. STM32单片机TFTLCD显示实验:TFTLCD简介