管道通信
int pipe(int pipefd[2]);返回值 成功 0 失败 -1。管道两端可分别用描述字pipefd[0]以及pipefd[1]来描述,需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字pipefd[0]表示,称其为管道读端;另一端则只能用于写,由描述字pipefd[1]来表示,称其为管道写端。只能用在父子进程。先pipe再fork。
int mkfifo(const char * pathname,mode_t mode);
mkfifo ()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask),因此 umask值也会影响到FIFO文件的权限。Mkfifo()建立的FIFO文件其他进程都可以用读写一般文件的方式存取。当使用open()来打开 FIFO文件时,O_NONBLOCK旗标会有影响。若成功则返回0,否则返回-1,错误原因存于errno中。
错误代码
EACCESS 参数pathname所指定的目录路径无可执行的权限
EEXIST 参数pathname所指定的文件已存在。
ENAMETOOLONG 参数pathname的路径名称太长。
ENOENT 参数pathname包含的目录不存在
ENOSPC 文件系统的剩余空间不足
ENOTDIR 参数pathname路径中的目录存在但却非真正的目录。
EROFS 参数pathname指定的文件存在于只读文件系统内。
################################################################################
例程中是建立完fifo之后先进行读操作再写操作,自己尝试谢了先写再读的程序(先写再读是不是更符合正常人思维?)
遇到了奇怪的问题:写文件打开时提示找不到文件或文件不存在!后来发现了原因是open函数中NON_BLOCK这个选项的问题
现象:
nonblock先写打开,打开错误提示找不到文件
nonblock先读打开,打开成功程序正常运行
block先写打开,打开成功,程序阻塞直到有进程读取fifo再运行之后程序
block先读打开,打开成功,程序阻塞直到有进程写入fifo再运行之后程序
总结:
只写打开先运行必须block,只读打开先运行都可以,也就是写依赖读,先写必须阻塞。有名管道的最好用法是先nonblock读再nonblock写(所以管道也要在读程序中创建),用读写打开则无所谓了。
信号通信
进程用kill函数给其他进程发送信号,用户用kill命令给其他进程发送信号。
信号处理三种方法:1忽略2执行用户自定义函数3执行系统默认动作。
SIGKILL和SIGSTOP不能被忽略,因为他们向超级用户提供了一种停止进程或终止的方法
int kill(pid_t pid, int sig);
1. pid>0时,pid是信号欲送往的进程的标识。
2. pid=0时,信号将送往所有与调用kill()的那个进程属同一个使用组的进程。
3. pid=-1时,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)。
4. pid<-1时,信号将送往以-pid为组标识的进程。
返回值说明: 成功执行时,返回0。失败返回-1,errno被设为以下的某个值 EINVAL(指定的信号码无效(参数 sig 不合法)), EPERM(权限不够无法传送信号给指定进程), ESRCH(参数 pid 所指定的进程或进程组不存在)
int raise(int sig);将参数sig指定的信号发送给自身进程,成功返回0,失败返回-1.
unsigned int alarm(unsigned int seconds);经过制定的seconds秒后会产生信号SIGALRM,如果不捕捉该信号,默认动作是终止进程。
int pause(void);令当前的进程暂停(进入睡眠状态),直到被信号(signal)中断。只返回-1。 错误代码EINTR 表示有信号到达中断了此函数。
void (*signal(int signum,void(* handler)(int)))(int);是注册函数而不是执行函数;
第一个参数signum指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。
第二个参数handler描述了与信号关联的动作,它可以取以下三种值:
(1)一个无返回值的函数地址
此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为signum的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:void func(int sig);(无返回值,整形参数)
(2)SIG_IGN
这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。
(3)SIG_DFL
这个符号表示恢复系统对信号的默认处理。
函数说明:
signal()会依参数signum 指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。当一个信号的信号处理函数执行时,如果进程又接 收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。但是如果在信号处理函数执行时进程 收到了其它类型的信号,该函数的执行就会被中断。
返回值:返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。
SIGHUP:从终端发出的中断信号;
SIGINT:来自键盘的中断信号(cral+c)
SIGKILL:该信号结束接收信号的进程
SIGTERM:kill命令发出的信号
SIGCHLD:标识子进程停止或结束的信号
SIGSTOP:来自键盘(cral+z)或调试程序的终止信号
共享内存通信
共享内存是被多个进程共享的一部分物理内存。共享内存是进程间共享数据最快的一种方法。
int shmget(key_t key, size_t size, int shmflg)得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符。成功返回共享内存的标识符,出错返回-1,错误原因存于error中。
在Linux环境中,对开始申请的共享内存空间进行了初始化,初始值为0x00。
key标识共享内存的键值: 0/IPC_PRIVATE。 当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存用于父子进程;如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。
IPC_CREAT如果共享内存不存在,则创建一个共享内存,否则打开操作。
IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
IPC_EXCL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。
注意的是,使用参数要加上 | 0666 作为校验,在有些Linux系统中,如果不加此校验,则不能顺利获取共享空间的值(如Ubuntu)。此外,有两个常用参数,一般要同时出现,他们 是:S_IRUSH | S_IWUSR 。由于这两个参数非常常用,程序员一般做这样的操作
#define PERM S_IRUSR | S_IWUSR | IPC_CREAT
void *shmat(int shmid, const void *addr, int flag);
shmid:共享存储的id
addr:一般为0,表示连接到由内核选择的第一个可用地址上,否则,如果flag没有指定SHM_RND,则连接到addr所指定的地址上,如果flag为SHM_RND,则地址取整
flag:如前所述,一般为0
返回值:如果成功,返回共享存储段地址,出错返回-1
int shmdt(void *addr);
addr:共享存储段的地址,以前调用shmat时的返回值
shmdt将使相关shmid_ds结构中的shm_nattch计数器值减1
当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的 shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段
int shmctl(int shmid,int cmd,struct shmid_ds *buf)
shmid:共享存储段的id
cmd:一些命令
IPC_STAT 得到共享内存的状态
IPC_SET 改变共享内存的状态
IPC_RMID 删除共享内存
IPC_RMID 命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生在最后一个进程离开这个共享段时。
请注意,共享内存不会随着程序结束而自动消除,要么调用shmctl删除,要么自己用手敲命令去删除,否则永远留在系统中。
终端输入ipcs -m查看共享内存,ipcrm -m shmid 释放共享内存。三个关于共享内存的好文网址。
http://www.cnblogs.com/bizhu/archive/2012/05/18/2507138.html
http://www.cnblogs.com/hicjiajia/archive/2012/05/17/2506638.html
http://lobert.iteye.com/blog/1746041
PS 自己写的程序执行中发现:信号通信中的signal函数仅仅代表为信号的处理注册了一个执行函数,因此写到程序最前面比较好。
消息队列
key_t ftok(const char *pathname, int proj_id);系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
将文件的索引节点号取出,前面加上proj_id得到key_t的返回值。失败返回值为-1
ftok只是根据文件inode在系统内的唯一性来取一个数值.
int msgget(key_t key, int msgflg);两个参数和shmget的一样,成功执行时,返回消息队列标识值。失败返回-1,错误在errno中。
IPC_NOWAIT:默认读写不满足条件时阻塞,使用nowait则不阻塞。
两种情况会创建消息队列:key为ipc_private或msgflg包含ipc_creat
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下
struct msgbuf {
long mtype; /* 消息类型,必须 > 0 */自己制定一个数字作为暗号
char mtext[1]; /* 消息文本*/
}
msgflg:这个参数依然是是控制函数行为的标志,取值可以是:0,表示忽略;IPC_NOWAIT,如果消息队列为空,则返回一个ENOMSG,并将控制权交回调用函数的进程。如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的消息为止。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);当消息从队列内取出后,相应的消息就从队列中删除了。
成功执行时,msgsnd()返回0,msgrcv()返回拷贝到mtext数组的实际字节数。失败两者都返回-1,设置errno
ipcs -q 显示消息队列的使用情况;ipcrm -q msqid 删除对应的消息队列
ftok这个函数看上去好像只能用于取键值,实际上ftok只是通过文件inode的唯一性得到一个不会重复的数(万一多个文件对用同一个inode也就是硬链接,则键值则会重复),从而满足了键值的条件,可以被应用于取键值。msgget和shmget很像,key参数用ipc_private则会建立只可用于父子进程的队列,flg则是选项|权限。msgctl和shmctl一样可以用来在程序最后删除消息队列,仅仅退出程序不会自动删除程序。
阅读(1548) | 评论(0) | 转发(0) |