Unix通信
管道
1.无名管道
#include <unistd.h>
int pipe(int pfd[2]);
- 成功返回0,失败-1
pfd包含两个元素的整形数组用来保存文件描述符
pfd[0]用于读管道,pfd[1]用于写管道
代码案例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
int pfd[2];
int re;
pid_t pid;
re = pipe(pfd);
if (re == -1)
{
perror("pipe");
return -1;
}
pid = fork();
if (pid < 0)
{
perror("pid");
return -1;
}
//子线程
else if (pid == 0)
{
char buf[64] = "haha,i am child,pipe message!\n";
while (1)
{
write(pfd[1], buf, strlen(buf));
sleep(1);
}
}
else
{
char buf[64];
while (1)
{
memset(buf,0,64);
re = read(pfd[0], buf, 64);
if (re > 0)
{
printf("msg from child %s\n", buf);
}
else
{
break;
}
}
}
return 0;
}
2.有名管道
- 提供一个路径名与之相关
#include <unistd.h>
#include <fcntl.h>
int mkfifo(const char *path, mode_t mode);
- 成功返回0,失败返回-1
path创建管道的路径
mode管道文件的权限,例0666
问题及解决方案(转载)
mkfifo: Operation not permitted
- window的文件系统不支持管道文件,只要把创建的管道文件路径设为linux的本地文件夹就行了
在共享文件夹里面创建管道mkfifo失败ubuntu
3.信号简介
- 软件层次上对中断机制的一种模拟,是一种异步通信方式
- Linux内核通过信号通知用户进程,不同的信号类型代表不同的事件
信号响应方式:
- 缺省方式(执行默认程序)
- 忽略方式
- 捕捉方式(执行自定义程序员)
常见信号
- kill -l 查看常见信号
kill [-signal] pid
- sig可指定信号
- pid指定发送对象
killall [-u user|prog]
- prog 指定进程名
- user 指定用户名
信号名 | 含义 |
---|---|
sighup | 终端关闭时,发给终端关联所有进程 |
sigint | 用户输入ctl+c后,内核发送该信号给当前终端的所有前台进程 |
sigquit | ctrl+\,同sigint |
sigll | 进程执行非法指令,进程终止 |
sigsev | 非法访问内存,缓存区溢出 |
sigpipe | 当进程往一个没有读端的管道中写入时产生,代表"管道破裂" |
sigkill | 用于结束进程,无法捕捉和忽略 |
sigstop | 用于暂停进程,无法捕捉和忽略 |
sigtstp | 用于暂停进程,ctrl+z |
sigcint | 进程进入运行状态 |
sigalrm | 信号用于通知进程定时器时间已到 |
sigusr1/2 | 用户自定义信号,用于终止进程 |
4.程序处理信号
信号发送
#include <unistd.h>
#include <signal.h>
int kill(pid_t pid,int sig);
int raise(int sig);//给自己发信号
- 成功返回0,是被返回EOF
pid 接收进程的进程号
0代表同组进程,-1代表所有进程
sig 信号类型
信号相关函数 alarm/pause
int alarm(unsigned int seconds);
- 成功返回上个定时器的剩余时间,失败返回EOF
- seconds 定时器时间
- 一个进程只能设置一个定时器,时间到产生sigalrm信号
int pause(void)
- 使进程一直处于阻塞状态,直到被信号中断
- 被信号中断后产生-1,errno为eintr
代码案例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
alarm(3);//alaem信号时间到后会终止程序
pause();
printf("I have been waken up!\n");//不会被打印
return 0;
}
设置信号响应方式 signal
#include <unistd.h>
#include <signal.h>
void (*signal(int signo,void(*handler(int)))(int);
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighndler_t handler);
- 成功返回原先的信号处理函数,失败返回SIG_ERR
- signo 要设置的信号类型
- handler 指定的信号处理函数,SIG_DFL代表缺省方式
- SIG_IGN代表忽略信号
代码案例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
//定义了一个指向(返回值为void)(参数为int)的函数指针
typedef void (*sight)(int);
sight oldhandle;
int count;
void handlerfunc();
int main(int argc, char const *argv[])
{
//signal返回值是原先(默认)的信号处理函数
oldhandle = signal(SIGINT, handlerfunc);
oldhandle = signal(SIGHUP, handlerfunc);
while (1)
{
sleep(1);
}
return 0;
}
void handlerfunc(int sig)
{
count++;
if (SIGINT == sig)
{
printf("i got sigint\n");
}
else if (SIGHUP == sig)
{
printf("i got sighub\n");
}
//ctrl+c两次后退出程序
if (count == 2)
{
//SIG_DFL:默认信号处理程序
//signal(SIGINT, SIG_DFL);
signal(SIGINT, oldhandle);
}
}
- 使用ctrl+c后程序不会终止,而是执行handlerfunc函数
子进程结束信号
- SIGCHID
- 用于处理子进程的僵尸进程
5.共享内存
- 共享内存是一种搞笑的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
- 共享内存存在内核空间创建,可以被进程映射到用户空间访问,使用灵活
- 由于多个进程可同时访问共享内存,因此需要同步互斥机制配合
共享内存使用步骤
- 创建,共享内存空间,读写,撤销内存映射,删除共享内存对象
IPC对象
- 共享内存
- 队列消息
- 信号灯集
- IPC对象创建后一直存在,直到被显式删除
- 每个IPC对象有一个关联的key
- ipcs(查看)/ipcrm(删除)
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *path,int proj_id);
- path 存在并可访问的路径
- proj_id 用于生成key的数字,范围1-255
创建共享内存
#include <sys/types.h>
#include <sys/shm.h>
int shmget(key_t key,int size,int shmflg);
- 成功返回共享内存id,失败返回EOF
- key 与共享内存关联的key,IPC_PRIVATE或ftok生成
- shmflg 内存共享标志位 IPC_CREAT|0666
共享内存映射
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid,const void *shmaddr,intshmflg);
- 成功返回映射后的地址,失败返回(void *)-1;
- shmid 要映射的共享内存id
- shmaddr 映射后的地址,NULL 表示由系统自动选择映射
- shmflg 标志位 0表示可读写,SHM_RDONLY
共享内存读写
addr=(char *)shmat(shmid,NULL,0);
...
fget(addr,N,stdin);
共享内存撤销
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(void *shmaddr);
- 成功返回0,失败-1
- 不使用共享内存时撤销
- 进程结束时自动撤销
共享内存控制
#include <sys/types.h>
#include <sys/shm.h>
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
- 成功0 ,失败-1
- shmid 要操作的共享内存的id
- crmd 要执行的操作 IPC_RMID(删除)
- buf 保存或设置共享内存属性的地址
代码案例(写)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(int argc, char const *argv[])
{
key_t key;
int shmid;
char *addr;
key=ftok("/mnt/e/LV5_day6",23);
if(key==-1){
perror("fork");
return -1;
}
shmid=shmget(key,1024,IPC_CREAT|0666);
if (shmid==-1)
{
perror("shmget");
return -1;
}
addr=shmat(shmid,NULL,0);
strcpy(addr,"this is share memeory");
shmdt(addr);
return 0;
}
代码案例(读)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(int argc, char const *argv[])
{
key_t key;
int shmid;
char *addr;
key=ftok("/mnt/e/LV5_day6",23);
if(key==-1){
perror("fork");
return -1;
}
shmid=shmget(key,1024,IPC_CREAT|0666);
if (shmid==-1)
{
perror("shmget");
return -1;
}
addr=shmat(shmid,NULL,0);
printf("get share mem=%s\n",addr);
//是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
shmdt(addr);
//删除共享内存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
6.消息队列
- Win10的linux子系统不支持消息队列(虚拟机运行)
- WIN10自带的Linux子系统不支持消息队列?
创建/打开消息队列
#include <sys/ipc.h>
#incluee <sys/msg.h>
int msgget(key_t key,int msgflg);
- 成功队列id,失败EOF;
- key 与共享内存关联的key,IPC_PRIVATE或ftok生成
发送消息队列 msgsnd
#include <sys/ipc.h>
#incluee <sys/msg.h>
int msgsnd(int msgid,const void *msgp,size_t size,int msgflg);
- 成功0,失败-1
- msgid 消息队列id
- msgp 消息缓冲区地址
- size 消息正文长度
- msgflg 标志位0或IPC_NOWAIT
消息队列结构体
- 消息双方使用定义好的统一格式
struct msgbuf
{
long mtype;//消息类型
char mtext[1];//消息正文
}
消息接收 msgrcv
#include <sys/types.h>
#include <sys/ipc.h>
#incluee <sys/msg.h>
int msgrcv(int msgid,void *msgp,size_t size,long msgtype,int msgflg);
- msgtype 指定接收消息的类型
- 0:接收消息队列中第一个消息
- 大于0:第一个msgtype类型的消息
- 小于0:第一个类型值不小于msgtype绝对值且类型值最小的消息
- msgflg : MSG_NOERROR,IPC_NOWWAIT,0
控制消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#incluee <sys/msg.h>
int msgctl(int msgid,int cmd,struct msqid_ds *buf);
- cmd 要执行的操作
- buf 存放消息队列属性的地址
代码案例
#include <sys/msg.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LEN sizeof(MSG) - sizeof(long)
typedef struct
{
long type;
char txt[64];
} MSG;
int main(int argc, char const *argv[])
{
key_t ipkey;
int msgid;
MSG msg_t;
ipkey = ftok(".", 23);
if (ipkey == -1)
{
perror("ftok");
return -1;
}
msgid = msgget(ipkey, IPC_CREAT | 0666);
if (msgid == -1)
{
perror("msgget");
return -1;
}
msg_t.type = 1;
strcpy(msg_t.txt, "message one");
msgsnd(msgid, &msg_t, LEN, 0);
msg_t.type = 2;
strcpy(msg_t.txt, "message two");
msgsnd(msgid, &msg_t, LEN, 0);
}
#include <sys/msg.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LEN sizeof(MSG) - sizeof(long)
typedef struct
{
long type;
char txt[64];
} MSG;
int main(int argc, char const *argv[])
{
key_t ipkey;
int msgid;
int re;
MSG msg_t;
ipkey = ftok(".", 23);
if (ipkey == -1)
{
perror("ftok");
return -1;
}
msgid = msgget(ipkey, IPC_CREAT | 0666);
if (msgid == -1)
{
perror("msgget");
return -1;
}
while (1)
{
re = msgrcv(msgid, &msg_t, LEN, 0, 0);
printf("recrive type:%ld/t txt:%s\n",msg_t.type,msg_t.txt);
if (re < 0)
{
break;
}
}
}
7.信号灯(system V)
- system v信号灯是一个或多个技术信号灯的集合
- 可同时操作集合中多个信号灯
- 申请多个资源避免锁死
打开/创建信号灯 semget
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key,int nsms,int semglg);
nsems 集合中包含的计数信号灯个数
semflg 标志位 IPC_CREAT|0666 IPC_EXCL
信号灯初始化/删除信号灯 semctl
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid,int sennum,int cmd,...);
sennum 要操作的集合中信号灯编号
cmd 要执行的操作 SETVAL IPC_RMIC
第四个参数(联合体union semun)是否存在取决于cmd
P/V操作 semop
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid ,struct sembuf *sops,unsigned nsops);
sops:描述对信号灯操作的结构体(数组)
nsops:要操作的信号灯个数
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
}
semnum:信号灯编号
sem_op:-1 p操作,1 v操作
sem_flg:0/IPC_NOWAIT