简介:
进程间通讯方式有:
1.内存映射(mmap):
使用mmap函数将磁盘空间映射到内存
2.管道
3.信号
4.套接字(socket)
5.信号机制
通过进程中kill函数,去给另一个函数发送信号,另一个函数捕捉到该信号之后,重新定义该信号的操作,实现进程间通讯
6.system V IPC:
简介:
IPC对象包括:共享内存,消息队列,信号灯集
1.每个IPC对象都有唯一的ID与Key关联(通过访问key就知道访问的是哪一块IPC对象)
2.IPC对象创建后一直存在,直到被显式删除
3.属于内核中的数据结构
共享内存:
很老,但仍有应用
消息队列:
过时
信号灯集:
过时
方式1、内存映射
简介:
使用系统调用函数mmap,将一个磁盘文件与内存中的一个缓冲区相映射,进程可以像访问普通内存一样对文件进行访问,不需要再调用read,write。
(进程在内存中都有自己的内存空间,不能相互访问,即使内存地址相同也不能相互访问)
(同一个磁盘文件是可以被多个进程相互访问的,因此将磁盘文件映射到内存中的缓冲区,相当于直接在内存中对磁盘文件进行读写,实现用户空间和内核空间高校交互)
原本的进程间交互方式:
使用mmap系统调用函数后:
函数实现:
mmap函数:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
功能:创建共享内存映射
函数返回值:成功返回创建的映射区首地址,失败返回宏:MAP_FAILED( ((void *) -1) )(也就是-1),设置errno值
参数说明:
addr:指定要映射的内存地址,一般设置为 NULL 让操作系统自动选择合适的内存地址。
length:必须>0。映射地址空间的字节数,它从被映射文件开头 offset 个字节开始算起。
prot:指定共享内存的访问权限。可取如下几个值的可选:PROT_READ(可读), PROT_WRITE(可写), PROT_EXEC(可执行), PROT_NONE(不可访问)。
flags:由以下几个常值指定:
MAP_SHARED(共享的)(进程间通讯)
MAP_PRIVATE(私有的)(单个进程)(方便对文件快速读写)
MAP_FIXED(表示必须使用 start 参数作为开始地址,如果失败不进行修正)
其中,MAP_SHARED , MAP_PRIVATE必选其一,而MAP_FIXED 则不推荐使用。MAP_ANONYMOUS(匿名映射,用于血缘关系进程间通信)(类似与无名管道)
fd:表示要映射的文件描述符。如果匿名映射写-1(不需要文件)。
offset:表示映射文件的偏移量,一般设置为 0 表示从文件头部开始映射。
注意事项:
(1) 创建映射区的过程中,隐含着一次对映射文件的读操作,将文件内容读取到映射区。
(2) 当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护),如果不满足报非法参数(Invalid argument)错误。
当MAP_PRIVATE时候,mmap中的权限是对内存的限制,只需要文件有读权限即可,操作只在内存有效,不会写到物理磁盘,且不能在进程间共享。
(3) 映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭,文件关闭后,使用修改映射内存中的数据,磁盘文件本身也会被修改。
(4) 用于映射的文件大小必须>0,当映射文件大小为0时
指定非0大小创建映射区,访问映射地址会报总线错误
指定0大小创建映射区,报非法参数错误(Invalid argument)
(5) 文件偏移量必须为0或者4K的整数倍(否则 会报非法参数Invalid argument错误).
(因为内存是按页分配的,一页为4k字节)
(6)可访问内存空间根据实际文件大小进行定义;和mmap函数参数映射的内存大小无关(只要mmap参数length不为0即可),但是大于文件实际大小的内存位置不会被写入文件
例:文件实际字节小于一页(也就是4k,4096字节),可访问内存就为一页(mmap参数length可以取不为0的任意数)
(7)文件大小代表映射的内存能写入的数据大小,文件中原有数据也会被映射到内存中去
munmap函数
int munmap(void *addr, size_t length);
返回值:成功返回0,失败返回-1,并设置errno值。
addr:调用mmap函数成功返回的映射区首地址
length:映射区大小(即:mmap函数的第二个参数)
注意事项:
相关函数:
lseek函数:
off_t lseek(int fd,off_t offset,int whence);
功能:查看文件长度,是一个在 C 语言中的系统调用,用于在文件中移动文件指针。
成功时,返回新的文件偏移量(从文件开头开始的字节数)。
失败时,返回 -1
,并设置 errno
以指示错误。
fd:被查看文件的文件描述符
offset:在文件内的偏移量(也就是起始地址)
whence:一个整型常量,用于指定偏移量的引用点。
可取的值有:
SEEK_SET
: 文件开头,offset
是相对于文件开头的字节偏移。
SEEK_CUR
: 当前文件位置,offset
是相对于当前文件位置的字节偏移。
SEEK_END
: 文件末尾,offset
是相对于文件末尾的字节偏移。
memcpy函数:
void *memcpy(void *dest, const void *src, size_t n);
功能:是 C 和 C++ 中的一个标准库函数,用于从一个内存位置复制数据到另一个内存位置。它定义在 <string.h>
头文件中,常用于处理内存块的拷贝。
无返回值
参数:
dest
:指向要复制到的目标内存块的指针。
src
:指向要复制的源内存块的指针。
n
:要复制的字节数。
相关命令:
使用方式:
写:
#include<stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
int main() {
void *addr;
int fd;
/*
* 1.打开文件
*/
fd=open("text",O_RDWR);
if(fd<0) {
perror("open");
return 0;
}
/*
* 2.映射文件到内存
*/
addr=mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(addr<0) {
perror("mmap");
return 0;
}
/*
* 3.关闭文件
*/
close(fd);
/*
* 4.读写数据-写
*/
int i=0;
while(i<4096) {
memcpy(addr+i,"b",1);
i++;
sleep(1);
}
return 0;
}
读:
#include<stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
int main(){
void *addr;
int fd;
/*
* 1.打开文件
*/
fd=open("text",O_RDWR);
if(fd<0){
perror("open");
return 0;
}
/*
* 2.映射文件到内存
*/
addr=mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(addr<0){
perror("mmap");
return 0;
}
/*
* 3.关闭文件
*/
close(fd);
/*
* 4.读写数据-读
*/
while(1){
printf("%s\n",(char *)(addr));
sleep(1);
}
return 0;
}
注意事项:
方式2、system V IPC共享内存
简介:
1.共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
2.共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活。
3.由于多个进程可同时访问共享内存,因此需要同步和互斥机制配合使用。
函数实现:
1.创建key
key_t f tok(const char *path, int id);
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *path, int proj_id);
成功时返回合法的key值,失败时返回EOF
path 存在且可访问的文件的路径
proj_id 用于生成key的数字,范围1-255。
2.创建/打开共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);
成功时返回共享内存的id,失败时返回EOF
key 和共享内存关联的key,IPC_PRIVATE 或 ftok生成
size 创建共享内存的大小(字节为单位)
shmflg 共享内存标志位,用于指定共享内存的创建方式和访问权限。
常用的标志包括:
IPC_CREAT
: 如果指定的共享内存段不存在,则创建一个新的共享内存段。
IPC_EXCL
: 如果指定的共享内存段已经存在,则调用失败。
权限标志位,如 0666
,可以指定访问权限。
3.映射共享内存
即把指定的共享内存映射到进程的地址空间用于访问
#include <sys/ipc.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
成功时返回映射后的地址,失败时返回(void *)-1
shmid 要映射的共享内存id
shmaddr 映射后的地址, NULL表示由系统自动映射
shmflg 标志位 0表示可读写;SHM_RDONLY表示只读
4.读写共享内存
5.撤销共享内存映射
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(void *shmaddr);
成功时返回0,失败时返回EOF
6.删除共享内存对象
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
成功时返回0,失败时返回EOF
shmid 要操作的共享内存的id
- cmd: 操作的命令,决定了函数的行为。常用的命令包括:
IPC_RMID
: 删除共享内存段。IPC_STAT
: 获取共享内存段的状态信息(存储在struct shmid_ds
中)。IPC_SET
: 设置共享内存段的属性。
- buf: 指向
struct shmid_ds
的指针,用于存储或提供状态信息。如果cmd
是IPC_STAT
,buf
应指向一个有效的struct shmid_ds
结构体。如果cmd
是IPC_SET
,buf
应指向一个包含要设置的新值的struct shmid_ds
结构体;如果cmd
是IPC_RMID
,则可以设置为 NULL。
相关函数:
strcpy函数
char *strcpy(char *dest, const char *src)
返回目标字符串 dest
的指针。
功能:函数是 C 语言中用于字符串复制的标准库函数,定义在 <string.h>
头文件中。它的基本功能是将一个字符串的内容复制到另一个字符串中。
参数:
dest
:目标字符串,复制后的内容将存放在这里。这个字符串必须有足够的空间来存放源字符串及其结束符(\0
)。
src
:源字符串,内容将被复制的对象。
相关命令:
ipcs命令:
可以在终端查看共享内存、消息队列、信号量(都属于内核中数据结构)
touch命令:创建文件
使用方式:
读:
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
int main() {
key_t key;
int shmid;
void *addr;
int dt;
int ctl;
/*
* 1.生成key
*/
key=ftok("keytest",34);
if(key<0) {
perror("ftok");
return 0;
}
printf("key=%d\n",key);
/*
* 2.创建共享内存
*/
shmid=shmget(key,512,IPC_CREAT|0666);
if(shmid<0) {
perror("shmget");
return 0;
}
printf("ID=%d\n",shmid);
/*
* 3.映射共享内存
*/
addr=shmat(shmid,NULL,0);
if(addr<0) {
perror("shmat");
return 0;
}
printf("addr=%p\n",addr);
/*
* 4.读写内存-读
*/
printf("%s\n",(char *)addr);
/*
* 5.撤销共享内存
*/
dt=shmdt(addr);
if(dt<0) {
perror("shmdt");
return 0;
}
/*
* 6.删除共享内存
*/
ctl=shmctl(shmid,IPC_RMID,NULL);
if(ctl<0) {
perror("shmctl");
}
return 0;
}
写:
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
int main() {
key_t key;
int shmid;
void *addr;
/*
* 1.生成key
*/
key=ftok("keytest",34);
if(key<0) {
perror("ftok");
return 0;
}
printf("key=%d\n",key);
/*
* 2.创建共享内存
*/
shmid=shmget(key,512,IPC_CREAT|0666);
if(shmid<0) {
perror("shmget");
return 0;
}
printf("ID=%d\n",shmid);
/*
* 3.映射共享内存
*/
addr=shmat(shmid,NULL,0);
if(addr<0) {
perror("shmat");
return 0;
}
printf("addr=%p\n",addr);
/*
* 4.读写内存-写
*/
strcpy(addr,"hello");
}
注意事项:
1.写内存使如果创建了共享内存,读内存时就不需要再创建
2.共享内存创建后不会随着程序结束而消失,如果共享内存不被删除则一直存在,共享内存中数据一直也存在
3.使用shmdt撤销后,此处共享内存将不能被访问,但是该共享内存还存在,如果不在使用该共享内存,需要使用shmctl将共享内存删除
方式3、system V IPC消息队列
简介:
队列:先进先出(链表实现)(内核中的一种数据结构)
消息队列由消息队列 ID 来唯一标识
消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等
消息队列可以按照类型来发送/接收消息
函数实现:
1.打开/创建消息队列
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
成功时返回消息队列的id,失败时返回EOF
key: 消息队列的唯一标识符的键值。这个值可以通过ftok
函数生成,它通常是一个整数,用于标识不同的消息队列。
msgflg: 控制消息队列创建和访问的标志,是一个位标志,可以使用以下几种选项组合:
IPC_CREAT
: 如果消息队列不存在,则创建一个新的消息队列。
IPC_EXCL
: 与IPC_CREAT一起使用,确保如果消息队列已存在,msgget
将失败,而不是返回现有队列的ID。
其他标志,如权限位,可以控制访问权限(例如:0666
表示所有用户可读写)。
2.发送消息
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t size, int msgflg);
成功时返回0,失败时返回-1
参数1:msgid 消息队列id
参数2:msgp 消息缓冲区地址(msgT结构体地址)
参数:msgp:
消息格式:
typedef struct{
long msg_type;//必须是long型,表示消息的类型
char buf[128];
}msgT;
注意:
1.消息结构必须有long类型的msg_type字段,表示消息的类型。(与接收方关联)
2.消息长度不包括结构体中"消息类型long msg_type"的长度(也就是参数size=结构体长度-消息类型长度=buf消息长度)
3.发送端消息类型取值必须大于0
参数3:size 消息正文长度
参数4:msgflg 标志位 0 或 IPC_NOWAIT
参数:msgflg:
0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回
3.接收消息:
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, void *msgp, size_t size, long msgtype,int msgflg);
成功时返回收到的消息长度,失败时返回-1
参数1:msgid 消息队列id
参数2:msgp 消息缓冲区地址
参数3:size 指定接收的消息长度
参数4:msgtype 指定接收的消息类型
参数msgtype:
msgtype=0:收到的第一条消息,任意类型都接收。
msgtype>0:收到的第一条 msg_type类型的消息。
msgtype<0:接收类型等于或者小于msgtype绝对值的第一个消息。
例子:如果msgtype=-4,只接受类型是1、2、3、4的消息
参数5:msgflg 标志位
参数:msgflg:
0:阻塞式接收消息(如果没有消息,将会一直等待)
IPC_NOWAIT:没有消息也返回,返回错误码,此时错误码为ENOMSG
MSG_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息
4.控制消息队列:
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid, int cmd, struct msqid_ds *buf);
成功时返回0,失败时返回-1
msgid 消息队列id
cmd 要执行的操作 IPC_STAT / IPC_SET / IPC_RMID(删除)
buf 存放消息队列属性的地址(删除操作时给空)
相关函数:
创建key
key_t f tok(const char *path, int id);
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *path, int proj_id);
成功时返回合法的key值,失败时返回EOF
path 存在且可访问的文件的路径
proj_id 用于生成key的数字,范围1-255。
strcpy函数
char *strcpy(char *dest, const char *src)
返回目标字符串 dest
的指针。
功能:函数是 C 语言中用于字符串复制的标准库函数,定义在 <string.h>
头文件中。它的基本功能是将一个字符串的内容复制到另一个字符串中。
参数:
dest
:目标字符串,复制后的内容将存放在这里。这个字符串必须有足够的空间来存放源字符串及其结束符(\0
)。
src
:源字符串,内容将被复制的对象。
相关命令:
ipcs命令:
可以在终端查看共享内存、消息队列、信号量(都属于内核中数据结构)
使用方式:
发送端:
1 申请Key
2打开/创建消息队列 msgget
3向消息队列发送消息 msgsnd
#include <sys/types.h>
#include <sys/ipc.h>
#include<stdio.h>
#include<string.h>
#include <sys/msg.h>
#define MSGLEN (sizeof(msgT)-sizeof(long))
typedef struct {
long msg_type;
char buf[128];
} msgT;
int main() {
key_t key;
int msgid;
int ret;
msgT msg;
/*
* 1.创建key
*/
key=ftok(".",100);
if(key<0) {
perror("ftok");
return 0;
}
/*
* 2.创建消息队列
*/
msgid=msgget(key,IPC_CREAT|0666);
if(msgid<0) {
perror("msgget");
return 0;
}
/*
* 3.发送消息
*/
msg.msg_type=1;
strcpy(msg.buf,"this msg type 1");
ret=msgsnd(msgid,&msg,MSGLEN,0);
if(ret<0) {
perror("msgsnd");
return 0;
}
}
接收端:
1打开/创建消息队列 msgget
2从消息队列接收消息 msgrcv
3 控制(删除)消息队列 msgctl
#include <sys/types.h>
#include <sys/ipc.h>
#include<stdio.h>
#include<string.h>
#include <sys/msg.h>
#define MSGLEN (sizeof(msgT)-sizeof(long))
typedef struct{
long msg_type;
char buf[128];
}msgT;
int main(){
key_t key;
int msgid;
int ret;
msgT msg;
/*
* 1.创建key
*/
key=ftok(".",100);
if(key<0){
perror("ftok");
return 0;
}
/*
* 2.创建消息队列
*/
msgid=msgget(key,IPC_CREAT|0666);
if(msgid<0){
perror("msgget");
return 0;
}
/*
* 3.接收消息
*/
ret=msgrcv(msgid,&msg,MSGLEN,0,0);
if(ret<0){
perror("msgrcv");
return 0;
}
printf("%d %s\n",(int)msg.msg_type,msg.buf);
}
循环接收:
/*
* 3.接收消息
*/
while(1) {
ret=msgrcv(msgid,&msg,MSGLEN,0,0);
if(ret<0) {
perror("msgrcv");
return 0;
}
printf("%d %s\n",(int)msg.msg_type,msg.buf);
}
删除消息队列:
ret=msgctl(msgid,IPC_RMID,NULL);
if(ret<0){
perror("msgctl");
return 0;
}
注意事项:
1.执行一次msgsnd就会将消息发送一次
2.执行一次msgrcv就会将消息接收一次
方式4、管道
4.1、无名管道
特点:
1.只能用于具有亲缘关系的进程之间的通讯(父子进程)
2.单工的通信模式,具有固定的读端和写端(一个无名管道只能一端写,一端读。如果要实现双工通讯(两端都可以同时作为读端和写端)就需要建立两条无名管道)
3.无名管道创建时会返回两个文件描述符,分别用于读写管道
创建无名管道
#include <unistd.h>
int pipe(int pfd[2]);
成功时返回 0,失败时返回 EOF
pfd 包含两个元素的整形数组,用来保存文件描述符
pfd[0] 为读描述符
pfd[1] 为写描述符
代码实现:
父子进程:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int pfd[2];
int ret;
char buf[20]= {0};
pid_t pid;
ret=pipe(pfd);
if(ret<0) {
perror("pipe");
return 0;
}
pid=fork();
if(pid<0) {
perror("fork");
return 0;
}
/*
* 子进程写
*/
else if(pid==0) {
close(pfd[0]);
while(1) {
strcpy(buf,"hhhahahah");
write(pfd[1],buf,strlen(buf));
sleep(1);
}
}
/*
* 父进程读
*/
else {
close(pfd[1]);
while(1) {
ret=read(pfd[0],buf,20);
if(ret>0) {
printf("%s\n",buf);
}
}
}
return 0;
}
多进程:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int pfd[2];
int ret,i;
char buf[40]= {0};
char endbuf[40]= {0};
pid_t pid;
ret=pipe(pfd);
if(ret<0) {
perror("pipe");
return 0;
}
for(i=0; i<2; i++) {
pid=fork();
if(pid<0) {
perror("fork");
return 0;
}
/*
* 子进程
*/
else if(pid==0) {
break;
//跳出循环,子进程不再执行fork
}
/*
* 父进程
*/
else {
}
}
/*
* 父进程代码
*/
if(i==2) {
close(pfd[1]);
while(1) {
ret=read(pfd[0],endbuf,40);
if(ret>0) {
printf("%s\n",endbuf);
}
}
return 0;
}
/*
* 第二个子进程代码
*/
if(i==1) {
close(pfd[0]);
while(1) {
strcpy(buf,"This is second");
write(pfd[1],buf,strlen(buf));
sleep(1);
}
return 0;
}
/*
* 第一个子进程代码
*/
if(i==0) {
close(pfd[0]);
while(1) {
strcpy(buf,"This is one");
write(pfd[1],buf,strlen(buf));
usleep(10000);
}
return 0;
}
return 0;
}
流程:
文件描述符0,1,2分别被标准输出、标准输入、标准错误占用,只能从文件描述符3开始
创建子进程,由于子进程继承了父进程打开的文件描述符,所以父子进程就可以通过创建的管道进行通信。
如果父进程使用写端,就将父进程中的读端关闭,子进程的写端关闭,反之如此
相关函数:
fork函数
write函数
read函数
strcpy函数
sleep函数
usleep函数
注意事项:
1.在同一个进程中是可以又读又写的,只不过无名管道用于亲缘关系进程通讯,因此一般不用于同一个进程又读又写
2.管道可以用于大于两个进程共享
3.创建的管道固定为64k字节
4.管道中的数据,只要读出来了,就不存在于管道中了
无名管道的读写特性:
① 读管道:
1. 管道中有数据,read 返回实际读到的字节数。
2. 管道中无数据:
(1) 管道写端被全部关闭,read 返回 0 (好像读到文件结尾)
(2) 写端没有全部被关闭,read 阻塞等待(不久的将来可能有数据递达,此时会让出 cpu)
② 写管道:
1. 管道读端全部被关闭, 进程异常终止(也可使用捕捉 SIGPIPE 信号,使进程不终止)
2. 管道读端没有全部关闭:
(1) 管道已满,write 阻塞。(管道大小 64K)
(2) 管道未满,write 将数据写入,并返回实际写入的字节数。
问题记录:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int pfd[2];
int ret;
char buf[20]= {0};
char endbuf[20]= {0};
pid_t pid;
ret=pipe(pfd);
if(ret<0) {
perror("pipe");
return 0;
}
pid=fork();
if(pid<0) {
perror("fork");
return 0;
}
/*
* 子进程
*/
else if(pid==0) {
// close(pfd[0]);
while(0) {
strcpy(buf,"hhhahahah");
write(pfd[1],buf,strlen(buf));
ret=read(pfd[0],endbuf,20);
if(ret>0) {
printf("%s\n",endbuf);
}
sleep(1);
}
}
/*
* 父进程
*/
else {
// close(pfd[1]);
while(1) {
strcpy(buf,"hhhahahah");
write(pfd[1],buf,strlen(buf));
ret=read(pfd[0],endbuf,20);
if(ret>0) {
printf("%s\n",endbuf);
}
sleep(1);
}
}
while(1);
return 0;
}
思路:父进程读写管道,将buf中的字符串写入管道,再从管道中读取字符串到endbuf,如果读取成功,将ednbuf打印出来。
子进程同样操作,都是可以单进程通过无名管道读写的。
4.2有名管道:
特点:
1 有名管道可以使非亲缘的两个进程互相通信
2 通过路径名来操作,在文件系统中可见,但内容存放在内存中
3 文件 IO 来操作有名管道
4 遵循先进先出规则
5 不支持 leek 操作(虽然是文件)
6 单工读写
函数实现:
创建管道
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
成功时返回 0 ,失败时返回 EOF
path 创建的管道文件路径
mode 管道文件的权限,如 0666
注意:
不能将管道建立在共享目录下,因为windows不支持管道
#include <sys/types.h>
#include <sys/stat.h>
#include<stdio.h>
int main(){
int ret;
ret=mkfifo("/myfifo",666);
if(ret<0){
perror("mkfifo");
return 0;
}
return 0;
}
打开管道
open(const char *path, O_RDONLY);//1
open(const char *path, O_RDONLY | O_NONBLOCK);//2
open(const char *path, O_WRONLY);//3
open(const char *path, O_WRONLY | O_NONBLOCK);//4
四种打开模式,默认为阻塞状态,与上O_NONBLOCK为非阻塞状态
注意:
只能使用文件IO,不能使用标准IO
读进程:
#include <sys/types.h>
#include <sys/stat.h>
#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
int main() {
int ret,fd;
char buf[20]= {0};
ret=mkfifo("/myfifo",666);
if(ret<0) {
perror("mkfifo");
//return 0;
}
fd=open("/myfifo",O_RDONLY);
if(fd<0) {
perror("open");
return 0;
}
while(1) {
ret=read(fd,buf,20);
if(ret>0) {
printf("%s\n",buf);
}
}
return 0;
}
写进程:
#include <sys/types.h>
#include <sys/stat.h>
#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
int main() {
int ret,fd;
char buf[20]= {0};
ret=mkfifo("/myfifo",777);
if(ret<0) {
perror("mkfifo");
//return 0;
}
fd=open("/myfifo",O_WRONLY);
if(fd<0) {
perror("open");
return 0;
}
while(1) {
strcpy(buf,"ahhhhh");
write(fd,buf,strlen(buf));
}
return 0;
}
注意事项:
1 程序也是可以用 O_RDWR(读写)模式打开 FIFO 文件进行读写操作,但是其行为也未明确定义,
且,有名管道一般用于进程间通讯,不使用同一个进程实现读写的方式(同有名管道)
2 第二个参数中的选项 O_NONBLOCK,选项 O_NONBLOCK 表示非阻塞,加上这个选项后,表示
open 调用是非阻塞的,如果没有这个选项,则表示 open 调用是阻塞的
3.对于以只读方式(O_RDONLY)打开的 FIFO 文件,如果 open 调用是阻塞的(即第二个参
数为 O_RDONLY),除非有一个进程以写方式打开同一个 FIFO,否则它不会返回;如果 open
调用是非阻塞的的(即第二个参数为 O_RDONLY | O_NONBLOCK),则即使没有其他进程以写
方式打开同一个 FIFO 文件,open 调用将成功并立即返回。
对于以只写方式(O_WRONLY)打开的 FIFO 文件,如果 open 调用是阻塞的(即第二个参数为
O_WRONLY),open 调用将被阻塞,直到有一个进程以只读方式打开同一个 FIFO 文件为止;
如果 open 调用是非阻塞的(即第二个参数为 O_WRONLY | O_NONBLOCK),open 总会立即返回,但如果没有其他进程以只读方式打开同一个 FIFO 文件,open 调用将返回-1,并且 FIFO 也
不会被打开。
4.数据完整性,如果有多个进程写同一个管道,使用 O_WRONLY 方式打开管道,如果写入
的数据长度小于等于 PIPE_BUF(4K),那么或者写入全部字节,或者一个字节都不写入,
系统就可以确保数据决不会交错在一起。
方式5、信号机制
简介:
1.信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
2.linux内核通过信号通知用户进程,不同的信号类型代表不同的事件
3.进程对信号有不同的相应方式:
(1)缺省信号(根据信号指示执行)
(2)忽略信号(忽略信号指示)
(3)捕捉信号(捕捉信号,改变信号行为)
信号的产生:
1.按键产生
2.系统调用函数产生(例如raise、kill)
3.硬件异常产生
4.命令行产生(例:kill)
5.软件条件(例:被0除,非法访问内存)
常用信号:
命令实现:
kill +选项 +进程pid
通过终端命令给进程发送信号
函数实现:
kill函数:
int kill(pid_t pid, int signum)
功能:发送信号
参数:
pid:
> 0:发送信号给指定进程
= 0:发送信号给跟调用 kill 函数的那个进程处于同一进程组的进程。
< -1: 取绝对值,发送信号给该绝对值所对应的进程组的所有组员。
= -1:发送信号给,有权限发送的所有进程。(轻易不要使用)
signum:待发送的信号
实现案例:
1.nx.c生成可执行文件nx,执行nx
#include<stdio.h>
int main(){
while(1);
return 0;
}
2.ps -ef|grep nx查看nx进程pid
3.cs.c文件执行kill函数
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
int main(){
kill(2664,11);
return 0;
}
4.结果
raise函数:
int raise(int sig);
功能:给自己发送信号,等价于 kill(getpid(), signo);
sig:要发送的信号
实现案例:
1.cs.c文件
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
int main(){
raise(11);
return 0;
}
2.结果:
定时器函数:
int alarm(unsigned int seconds);
成功时返回上个定时器的剩余时间,失败时返回 EOF
seconds 定时器的时间
注意:
一个进程中只能设定一个定时器,时间到时产生 SIGALRM
实现案例:
1.三秒后产生SIGALRM信号
#include<stdio.h>
#include<unistd.h>
int main(){
alarm(3);
while(1);
return 0;
}
2.结果
int pause(void);
可以用于,阻止程序继续往下执行,收到信号后再继续执行,实现信号驱动程序
功能:进程一直阻塞,直到被信号中断
被信号中断后返回 -1 , errno 为 EINTR
函数行为:
1如果信号的默认处理动作是终止进程,则进程终止,pause函数么有机会返回。
2如果信号的默认处理动作是忽略,进程继续处于挂起状态,pause函数不返回
3 如果信号的处理动作是捕捉,则调用完信号处理函数之后,pause返回-1。
4 pause收到的信号如果被屏蔽,那么pause就不能被唤醒
实现案例1:
1.cs.c(代替while,直到被信号中断)
#include<stdio.h>
#include<unistd.h>
int main(){
alarm(3);
pause();
return 0;
}
2.结果
实现案例2:
解释:
在程序执行中给pause函数,有信号过来再将信号阻塞,往下执行cs函数,cs函数执行完毕,解除信号阻塞,在阻塞期间来的信号会被延迟,阻塞解除之后将信号释放,pause接收到释放的信号,就会进入下一个循环。
#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include <signal.h>
void handle(int sig){
printf("sig=%d\n",sig);
}
void cs(){
printf("111\n");
sleep(3);
printf("222\n");
}
int main(){
struct sigaction act;
act.sa_handler=handle;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
pause();
printf("after pause\n");
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
while(1){
sigprocmask(SIG_BLOCK,&set,NULL);
cs();
sigprocmask(SIG_UNBLOCK,&set,NULL);
pause();
}
}
问题:
信号好像发生在解除对SIGINT的阻塞和pause之间。如果发生了这种情况,或者如果在解除阻塞时刻和pause之间确实发生了信号,那么就产生了问题。因为我们可能不会再见到该信号,所以从这种意义上而言,在此时间窗口(解除阻塞和pause之间)中发生的信号丢失了,这样就使pause永远阻塞。
为了纠正此问题,需要在一个原子操作中先恢复信号屏蔽字,然后使进程休眠,这种功能是由sigsuspend函数提供的。
int sigsuspend(const sigset_t *sigmask);
函数功能相当于解除sigprocmaskg阻塞,并且把信号给pause
功能:将进程的屏蔽字替换为由参数sigmask给出的信号集,然后使进程休眠
参数:
sigmask:希望屏蔽的信号
修改案例2如下:
#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include <signal.h>
void handle(int sig){
printf("sig=%d\n",sig);
}
void cs(){
printf("111\n");
sleep(3);
printf("222\n");
}
int main(){
struct sigaction act;
act.sa_handler=handle;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
pause();
printf("after pause\n");
sigset_t set,set2;
sigemptyset(&set);
sigaddset(&set,SIGINT);
while(1){
sigprocmask(SIG_BLOCK,&set,NULL);
cs();
//sigprocmask(SIG_UNBLOCK,&set,NULL);
//pause();
sigsuspend(&set2)
}
}
循环定时器函数:
ualarm
useconds_t ualarm(useconds_t usecs, useconds_t interval);
以 useconds 为单位,第一个参数为第一次产生时间,第二个参数为间隔产生
例:ualarm(3,4),执行之后,3秒后产生信号,间隔4秒再产生信号(以后为4秒循环)
setitimer
int setitimer(int which,,const struct itimerval *new_value,struct itimerval *old_value);
功能:定时的发送 alarm 信号
参数:
which:
ITIMER_REAL:以逝去时间递减。发送 SIGALRM 信号
ITIMER_VIRTUAL: 计算进程(用户模式)执行的时间。 发送 SIGVTALRM 信号
ITIMER_PROF: 进程在用户模式(即程序执行时)和核心模式(即进程调度用时)均计算时
间。 发送 SIGPROF 信号
new_value: 负责设定 timout 时间
old_value: 存放旧的 timeout 值,一般指定为 NULL
struct itimerval {
struct timeval it_interval; // 闹钟触发周期(例如触发后,每隔 1 秒触发)
struct timeval it_value; // 闹钟触发时间(例如 5 秒后触发)
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
#include<stdio.h>
#include<unistd.h>
#include <signal.h>
#include<unistd.h>
#include <sys/time.h>
void handle(int sig) {
printf("cath the SIGALRM \n");
}
int main() {
struct sigaction act;
struct itimerval new_value;
act.sa_handler=handle;
act.sa_flags=0;
sigemptyset(&(act.sa_mask));
new_value.it_interval.tv_sec=3;
new_value.it_interval.tv_usec=0;
new_value.it_value.tv_sec=5;
new_value.it_value.tv_usec=0;
setitimer(ITIMER_REAL,&new_value,NULL);
sigaction(SIGALRM,&act,NULL);
while(1) {
sleep(1);
}
}
信号的捕捉:
解释:1.程序遇到信号,2.会进入内核,如果信号的处理动作是非自定义,内核将信号处进入5,再返回1;如果信号处理东西是自定义,如上图;
信号捕捉过程:
1.定义新的信号的执行函数handle。
2.使用signal/sigaction 函数,把自定义的handle和指定的信号相关联。
函数实现:
signal函数:
typedef void (*sighandler_t)(int);(只能写成这种形式)
typedef void (*sighander_t)(int)
含义:将一个返回值为void类型,参数为int类型的函数指针定义成sighander_t,函数参数就是传进来的信号
sighandler_t signal(int signum, sighandler_t handler);
功能:捕捉信号执行自定义函数
返回值:成功时返回原先的信号处理函数,失败时返回SIG_ERR
参数:
Signum 要捕捉的信号类型
handler 指定的信号处理函数:
SIG_DFL代表缺省方式; SIG_IGN 代表忽略信号;
系统建议使用sigaction函数,因为signal在不同类unix系统的行为不完全一样。
实现1.
#include<stdio.h>
#include<unistd.h>
#include <signal.h>
#include<unistd.h>
void handle(int sig){
printf("cath the SIGINT \n");
}
int main(){
signal(SIGINT,handle);
while(1){
sleep(1);
}
}
实现2.
#include<stdio.h>
#include<unistd.h>
#include <signal.h>
#include<unistd.h>
typedef void (*sighandler_t)(int);
sighandler_t old;
void handle(int sig){
printf("cath the SIGINT \n");
signal(SIGINT,old);
}
int main(){
old=signal(SIGINT,handle);
while(1){
sleep(1);
}
}
sigaction函数:
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
参数:
signum:处理的信号
act,oldact::处理信号的新行为和旧的行为,是一个sigaction结构体。
sigaction结构体成员定义如下:
sa_handler: 是一个函数指针,其含义与 signal 函数中的信号处理函数类似
sa_sigaction: 另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。
sa_flags参考值如下:(不使用以下标志位可写0)
SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数
SA_RESTART:使被信号打断的系统调用自动重新发起。
SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
re_restorer:是一个已经废弃的数据域
实现1.
#include<stdio.h>
#include<unistd.h>
#include <signal.h>
#include<unistd.h>
void handle(int sig){
printf("cath the SIGINT \n");
}
int main(){
struct sigaction act;
act.sa_handler=handle;
act.sa_flags=SA_RESETHAND;
sigemptyset(&(act.sa_mask));
sigaction(SIGINT,&act,NULL);
while(1){
sleep(1);
}
}
信号的阻塞:
信号来的时候会打断程序进行,如果希望信号不打断程序进行,就需要将信号阻塞
信号的阻塞概念:
信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生(程序是可以收到信号的)。
信号的状态:
信号递达(Delivery ):实际信号执行的处理过程(3种状态:忽略,执行默认动作,捕获)
信号未决(Pending):从产生到递达之间的状态(程序不中断,程序接收不到这个信号)
将一个信号写进信号屏蔽字,如果信号来了,就将信号写入未决信号集里(挂起),未决信号集和信号屏蔽字每一位都代表一个信号。
例:2 SIGINT信号,未决信号集和信号屏蔽字中第二个比特位就表示SIGINT信号
3 SIGOUT信号,未决信号集和信号屏蔽字中第三个比特位就表示SIGOUT信号
使用方式:可以将某个信号的未决信号集位,置1,当该信号到来,就将该信号的信号屏蔽字中的表示位,置1,该信号就被挂起,不会打断程序进行。
函数实现:
sigset_t set; 自定义信号集。 是一个32bit 64bit 128bit的数组每一个bit位表示一个信号。
sigemptyset(sigset_t *set); 清空信号集
sigfillset(sigset_t *set); 将信号集全部置1
sigaddset(sigset_t *set, int signum); 将一个信号添加到集合中
sigdelset(sigset_t *set, int signum); 将一个信号从集合中移除
sigismember(const sigset_t *set,int signum); 判断一个信号是否在集合中。
(此时还未真正的将信号屏蔽,以下函数才将信号真正屏蔽)
#include <signal.h>
int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );
返回值:若成功则返回0,若出错则返回-1
首先,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。
其次,若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。
how可选用的值:(注意,不能阻塞SIGKILL和SIGSTOP信号)
SIG_BLOCK : 把参数set中的信号添加到信号屏蔽字中
SIG_UNBLOCK: 从信号屏蔽字中删除参数set中的信号
SIG_SETMASK: 把信号屏蔽字设置为参数set中的信号
使用流程:
1.创建信号集
2.清空信号集
3.将信号add进信号集
4.将set中的信号添加到信号屏蔽字中
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
void handle(int sig){
printf("get sig=%d",sig);
}
int main(){
struct sigaction act;
act.sa_handler=handle;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
sigprocmask(SIG_BLOCK,&set,NULL);
while(1){
sleep(1);
}
}
相关命令:
kill -l:查看当前系统支持哪些信号
ps -ef |grep +进程名:查看此进程
相关函数:
sigemptyset
是一个 C 语言的标准库函数,属于信号处理部分,主要用来初始化一个信号集合。在使用信号相关功能时,通常需要创建并管理信号集,比如在信号屏蔽或处理程序中。
### 函数原型
```c
#include <signal.h>
int sigemptyset(sigset_t *set);
```
### 参数
- `set`: 指向需要初始化的信号集合的指针。
### 返回值
- 成功时返回 `0`,失败时返回 `-1`,并设置 `errno`。
### 功能
`sigemptyset` 将给定的信号集合 `set` 清空,确保集合中不包含任何信号。这通常在需要创建一个新的信号集合时使用,特别是在设置信号屏蔽或者与信号处理程序交互的情况下。
### 示例
以下是一个使用 `sigemptyset` 的示例代码:
```c
#include <stdio.h>
#include <signal.h>
int main() {
sigset_t set;
// 初始化信号集
if (sigemptyset(&set) != 0) {
perror("sigemptyset");
return 1;
}
// 检查集合是否为空
if (sigismember(&set, SIGINT)) {
printf("集合中包含 SIGINT\n");
} else {
printf("集合中不包含 SIGINT\n");
}
return 0;
}
```
### 注意事项
- 使用 `sigemptyset` 后,信号集是一个空集合,您可以使用其他函数如 `sigaddset` 向其中添加特定的信号。
- 该函数通常用于在多线程和进程间的信号处理时,确保信号的正确管理。
相关信号:
SIGCHLD信号实现回收子进程
SIGCHLD的产生条件
1子进程终止时
2子进程接收到SIGSTOP信号停止时
3子进程处在停止态,接受到SIGCONT后唤醒时
#include<stdio.h>
#include<unistd.h>
#include <signal.h>
#include<unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include<stdlib.h>
void handle(int sig) {
wait(NULL);
printf("cath the sig=%d \n",sig);
}
int main() {
int pid;
struct sigaction act;
act.sa_handler=handle;
act.sa_flags=0;
sigemptyset(&(act.sa_mask));
pid=fork();
if(pid>0){
sigaction(SIGCHLD,&act,NULL);
while(1){
printf("this is father\n");
sleep(1);
}
}
else{
sleep(5);
exit(0);
}
}
含义:父进程执行,在父进程内捕捉SIGCHLD信号,子进程exit(0)结束后,产生SIGCHLD信号,父进程捕捉到该信号,跳转到相关函数,将子进程回收
方式7、信号灯
进程和线程间通讯的一种方式,并且经常与共享内存一块使用,管理进程或线程间同步操作,只作为一种行为。
信号量和信号灯是同一种东西。
信号量代表某一类资源,其值表示系统中该资源的数量。
信号量是一个受保护的变量,只能通过三种操作来访问。
初始化
P操作(申请资源)(资源-1)
V操作(释放资源)(资源+1)
posix信号灯的使用:
初始化:sem_open()、sem_init()
wait是P操作,post是V操作
与共享内存使用:
初始共享内存为空;此时写信号量初始应为1,可写;读信号量初始为0,不可读
写入共享内存数据后;写信号量减1,为0,不可再写,读信号量加1,为1,可读;
读出共享内存数据后;读信号量减1,为0,不可再读,写信号量加1,为1,可写;
如此循环;
注意:
sem_open创建好信号量文件之后,如果没有将文件删除:关闭可执行文件,再打开可执行文件,就不可再次使用;因此需要在退出时将信号量文件删掉;
posix有名信号灯:
使用pthread库实现,编译时需要链接上pthread库
有名信号灯打开:
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
参数:
name:name是给信号灯起的名字
oflag:打开方式,常用O_CREAT
mode:文件权限。常用0666
value:信号量值。二元信号灯值为1,普通表示资源数目(有10个资源,就可以初始化写为10)
信号灯文件位置:/dev/shm
使用open打开信号灯后,文件会自动放在该目录下边
有名信号灯关闭:
int sem_close(sem_t *sem);参数为open函数的返回值
作用:关闭信号灯,不可使用(文件依旧存在)
有名信号灯的删除:
int sem_unlink(const char* name);
作用:删除/dev/shm下创建的信号灯文件
实现:
一般创建两个信号量,一个读,一个写;
向共享内存中写入数据,需要sem_wait检查写信号量是否有资源,有资源就继续往下执行,写入数据,再sem_post读信号量。
从共享内存读数据,需要sem_wait检查读信号量是否有组员,有资源就继续往下执行,读出数据,再sem_post写信号量。
写端:
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <signal.h>
#include<stdlib.h>
void unlink(int arg){
sem_unlink("sem_w");
exit(0);
}
int main(){
sem_t *sem_w;
sem_t *sem_r;
void *addr;
pthread_t shmid;
key_t key;
struct sigaction act;
act.sa_handler=unlink;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
key=ftok("/tmp",22);
if(key<0){
perror("ftok");
return 0;
}
shmid=shmget(key,512,IPC_CREAT|0666);
if(shmid<0){
perror("shmget");
return 0;
}
addr=shmat(shmid,NULL,0);
if(addr<0){
perror("shmat");
return 0;
}
/*
* 初始化信号量
*/
sem_r=sem_open("/sem_r",O_CREAT,0666,0);
sem_w=sem_open("/sem_w",O_CREAT,0666,1);
while(1){
sem_wait(sem_w); //判断信号量,有资源才会继续往下执行,同时信号量会减1
printf(">");
fgets(addr,512,stdin);
sem_post(sem_r); //将信号量加1
}
}
读端:
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <signal.h>
#include<stdlib.h>
void unlink(int arg){
sem_unlink("sem_r");
exit(0);
}
int main(){
sem_t *sem_w;
sem_t *sem_r;
char *addr;
pthread_t shmid;
key_t key;
struct sigaction act;
act.sa_handler=unlink;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
key=ftok("/tmp",22);
if(key<0){
perror("ftok");
return 0;
}
shmid=shmget(key,512,IPC_CREAT|0666);
if(shmid<0){
perror("shmget");
return 0;
}
addr=shmat(shmid,NULL,0);
if(addr<0){
perror("shmat");
return 0;
}
/*
* 初始化信号量
*/
sem_r=sem_open("/sem_r",O_CREAT,0666,0);
sem_w=sem_open("/sem_w",O_CREAT,0666,1);
while(1){
sem_wait(sem_r); //判断信号量,有资源才会继续往下执行,同时信号量会减1
printf("%s\n",addr);
sem_post(sem_w); //将信号量加1
}
}
posix无名信号灯:
(linux中只支持线程同步)
使用pthread库实现,编译时需要链接上pthread库
无名信号灯初始化/打开
int sem_init(sem_t *sem, int shared, unsigned int value);
参数:
sem:需要初始化的信号灯变量
shared: shared指定为0,表示信号量只能由初始化这个信号量的进程使用,不能在进程间使用,linux 不支持进程间同步。
Value:信号量的值(初始值)
无名信号灯销毁:
int sem_destroy(sem_t* sem);
参数:信号灯变量
System信号灯:
创建/打开信号灯:
int semget(key_t key, int nsems, int semflg);
功能:创建/打开信号灯
参数:key:ftok产生的key值(和信号灯关联的key值)
nsems:信号灯集中包含的信号灯数目
semflg:信号灯集的访问权限,通常为IPC_CREAT |0666
返回值:成功:信号灯集ID ; 失败:-1