C语言是一种面向过程的计算机程序设计语言。最初为unix而生。它既有高级语言的特点
源代码在线查看: 3.进程间通信(ipc).txt
3.进程间通信(IPC)
信号机制
信号是一中异步事件(也称软中断),可通过信号机构停止一个程序。
一个信号必定有他的发送者,接收者。一个信号的意义必须事先定义。
信号表是把信号和信号处理函数一一对应的一个表结构,Unix定义了37种信号。
注意:信号会打断任何的系统阻塞。
信号的处理
信号的处理有三种,忽略,捕获,默认方式(default)。
信号的处理多用捕获和默认方式来处理。
捕获是通过自定义的函数去替换信号表中的默认函数。
几种重要的信号
SIGALRM,超时信号(alram函数设置时钟到时后的发出的信号)
SIGCHLD,当子进程中止时调用,此信号会发送给子进程的父进程,父进程会回收子进程资源,而不必等待子进程结束,可以运行自身的代码,这也就达到了异步运行的目的。(常用信号)
SIGINT,此信号用于中止失控程序(按ctrl+c组合和delete键会发送此信号)
SIGKILL,强制杀死进程的信号,此信号不能互略和捕获,只能按系统默认方式处理。
SIGSTOP,作业控制信号,可以停止一个进程,此信号之能按系统默认方式处理。
SIGUSR1,SIGUSR2,这两个是系统留给用户自定义的信号,一般多使用两个信号,并进行捕获处理。
其余信号参见课本
system()函数创建到子进程会忽略SIGINT,SIGQUIT两种信号。
signal函数
signal函数,是用来修改信号表中信号对应的处理函数。
typedef void(*FUNC)(int)定义函数指针,int参数带表的是实际收到的信号。
FUNC signal(int signo,FUNC fp)信号注册函数,
signal函数的返回值是原来的信号处理函数。
signo参数要是用信号的名字。(信号名本身是定义好的宏)
注意:signal函数在实际使用时,只需要传入自定的函数名即可。自定义的信号处理函数需要特定的结构
即void 函数名(int)。当信号接收后会调用相应的信号处理函数,也就是注册了的自定义函数。
注意:Unix中会有信号丢失处理,signal函数只是注册后运行结束前有效。注册一次后,就只有这一次有效,所以可以在自定的信号捕获函数中递归的调用signal函数,这样可以避免信号丢失。也就使在信号处理函数中再调用signal函数注册这个信号处理函数。
SIG_ERR捕获信号失败,SIG_DFL系统默认行为,SIG_IGN忽略信号,这些可以做signal函数的参数值也是返回值,可以判断信号是否捕获成功,或者是自定义信号处理函数是否注册成功。
注意:在Unix中所有阻塞函数都会被信号打断。
kill命令和kill函数
kill,命令 kill -sig pid(进程id)可向任意进程发出信号。
sig,去掉信号名前的SIG三个字母后信号的名字
注意:这里的参数sig,就是去掉信号名前的SIG三个字母。
kill函数
int kill(pid_t pid,int sig),用于在程序中向任意进程发出信号。pid是进程id。sig是信号的全名。
信号的自举
int raise(int sig),程序本身自己向自己发信号,sig是信号的全名。
alarm函数
unsigned alarm(unsigned int sec)
sec是指定的时间。
这个函数的功能是从现在起的设定的时间间隔后发出一个SIGALRM信号。SIGALRM,信号的默认处理是进程中止退出。我们可以通过重新注册信号处理函数来实现相应功能。
sec是时间间隔的描述。
pause函数
int pause()永久阻塞函数,直到信号打断后才会结束阻塞。
sleep函数
unsigned sleep(unsigned int sec)是进程进入睡眠状态制定的时间。
sec是指定的时间。
sleep函数在被打断后会返回没有睡够的秒数。
Daemon进程
父进程和子进程的进程组id相同,会话包含多个进程组。
Daemon进程是在终端关闭后不会结束的进程。
Daemon进程的父进程的id是1,也就是intit进程。他的进程组的id和会话的id是相同的,就是他本身,不使用控制终端。
//父进程id为1
pid_t=fork();
if(pid>0)exit();
//进程组id和会话id为本身
setsid();
//设置屏蔽字
umask(0);
for(int i=0;i close(i);
}
以上是Daemon进程的标准写法
FIFO管道文件
使用FIFO时要包含两个头文件。
mkfifo 文件名,创建管道文件命令。
int mkfifo(const char* pathname,mode_t mode)创建管道文件。
pathname是管道文件的文件名,mode是文件的权限。
mkfifo函数的返回值表示FIFO文件创建是否成功,不是文件描述符。
使用FIFO的条件
(1)必须是两个进程同时对FIFO类型文件进行操作,并分别以只读和只写的方式才能打开FIFO文件,否则open函数会阻塞。
(2)read()函数的返回值为0表示文件读取完毕,表示写入方关闭了FIFO文件。
(3)FIFO文件在读取之后会自动清空,FIFO文件事先写入后读取,可以用于多进程间的通信。
Message Queue(消息队列)
注意:使用消息队列需要包含三个头文件。
消息队列,实质上是一个由内核维护的消息链表,是用msqid作为消息队列的唯一标识。可以用ipcs来查询消息队列。用ipcrm 消息队列id,删除消息队列。
消息队列的操作
创建消息队列
int msgget(key_t key,int flag)创建消息队列。
参数key也是用以标识消息队列的值,key一般定义在头文件中。(可以用IPC_PRIVATE随机生成key)
注意:key是在创建时用于查找相应的key值,如果查找到key值相同的消息队列就不创建新的队列了,而是使用这个队列,如果没有就会去查第二个参数,如果标识为新建会创建一个消息队列,如果不是会出错
flag是一个标记,一般在创建时写成 (IPC_CREATE|权限)(权限设置和文件相同),如果置为0就会用key去查找已有的消息队列,不会创建新的消息队列。
创建消息队列的返回值就是消息队列的msqid。如果返回值小于0就表示创建失败。
注意:如果要取得已创建的消息队列,就要把第二参数,设为0,key值传入要找的那个队列的key。
删除消息队列
int msgctl(int msqid,int cmd,struct msqid_ds* buf)删除消息队列。
msqid是要删除的消息队列的msqid
cmd,在删除消息队列时使用IPC_RMID这个值(其含义是立即删除消息队列),其他的选项还有IPC_STAT是取此队列的msqid_ds的结构,IPC_SET这个是用传入的参数buf,来修改msqid_ds结构。
msqid_ds这个结构规定了当前状态。
buf这个参数只有在使用cmd为IPC_SET时才使用,其余cmd在使用这个参数时用NULL即可。
注意:删除消息队列的常用方式 msgctl(mspid,IPC_RMID,NULL),消息队列在使用完之后需要删除。
发送/接收消息
在消息队列中存放是引种规定了格式结构的消息。
标准的msg结构,这种结构就是放在消息队列中发送或者是接收的“消息”。这个结构中第一个成员是一个long型的变量,除此之外,可以在这个结构中的定义其他的任意数量,任意类型的成员。
struct msg{
long msgtype;//这个变量表示的是消息的类型,用以区分消息的内容,这样就可以使一个消息队列中可以存放多种消息。
char mtext[MAX_MSG_LENGTH];//消息的正文部分。这里用的是字符串类型,也可以使用其他的类型作为消息的正文。
}
int msgsnd(int msqid,const void* ptr,size_t nbytes,int flag)发送消息
int msgrcv(int msqid,void* ptr,size_t nbytes, long type,int flag)接受消息
msgid,都是消息队列的标识,就是要想哪个消息队列写消息,要从哪个消息队列读取消息。
const void* ptr,是要发送的消息的地址。(消息就是消息结构的变量)
void* ptr,是接收消息的空间。
nbytes,是要发送的消息的大小(长度)。
type,是要接受的消息的类型(如果接收类型设置为0,就表示接收所有消息)
flag,是一个标记位,一般用0作为它的值。
注意:如果要接收一个没有的消息结构的类型,msgrcv函数会阻塞直到有这种消息结构类型的消息被发出。这种阻塞也可以被信号打断。
make命令
make命令使用与多文件编译或者多名令执行的脚本文件执行命令。make可以控制编译过程,只编译需要编译的文件。
makefile 命令用于创建脚本文件。
脚本文件格式
目标文件名:条件1 条件2
(按table键)命令.....
目标文件名(条件1):条件a
(按table键)命令.....
当要得到的文件还需要起他条件时就以此类推。
创建条件文件的命令写在目标文件的下一行且要在开头按table键。
脚本文件中可以定义宏,格式是例:cc=g++ ,是用宏是用${cc}
还可用占位符替代条件和相同的目标名,“${@}”在同一个目标中,可以以此替代目标名,“${?}”这个可以替换条件名。 用#开就是注释。
事例:
CC=g++
COMPIL=-c
NAME=-o
cat_test : cat_impl.o cat_main.o
${CC} ${NAME} ${@} cat_impl.o cat_main.o ;
rm *.o
cat_impl.o : cat_impl.cc
${CC} ${NAME} ${@} ${COMPIL} cat_impl.cc
cat_main.o : cat_main.cc
${CC} ${NAME} ${@} ${COMPIL} cat_main.cc
动态连接库
写一个头文件,头文件中定义了函数,然后写.cc文件实现函数,动态连接库一定要是libxxxx.so(LIUX/UNIX)这种格式。
现将.cc文件编译成.o文件
g++ -c -o xxx.o xxx.cc
g++ libxxx.so -shared xxx.o(目标文件)
动态连接库的使用
在主函数中调用了动态连接库中的函数
需要指定动态连接库的位置,要对环境变量进行修改(LD_LIBRARY_PATH)添加上自己定义的动态连接库的路径。
连接库是使用的名字(xxx)是去掉lib和.so就可以了
g++ sample.cc -lxxx -Lxxx