在Linux系统中,进程运行在自己的虚拟内存空间中。
控制多线程下对某个非共享资源的访问,最简单的方法是使用临时文件作为访问标志。每个进程在访问非共享资源前,判断该临时文件是否存在。如果存在,表明有进程占用了该资源。如果不存在,表明可以使用资源。一个进程在获得使用非共享资源后创建临时文件,表明资源已被占用。在释放该资源后删除临时文件,使其他进程获得访问该资源的权限。
对文件某个属性修改,即使用文件锁实现进程间通信,在Linux系统中,可以使用fcntl函数或lockf函数实现文件锁,进而实现进程间通信。
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
可以说lockf函数是fcntl函数在文件锁方面的一个简化调用。lockf函数用于实现文件加锁、检测和解锁的功能。
int lockf(int fd, int cmd, off_t len);
参数fd为打开文件的文件描述符,参数cmd为要执行的操作。cmd可以使用以下的参数:
l F_LOCK:给文件设置互斥锁。如果文件要加锁部分(只是部分重叠)已近被设置上锁,该调用将阻塞,直到原来设置的锁被移除。如果要加锁的部分和原来加锁的部分完全一致,文件锁将合并。当进程关闭文件描述符时,所加的文件锁将被移除。子进程不会从父进程处继承文件锁。
l F_TLOCK:实现的功能与使用F_LOCK参数相同。只是在文件已经加锁的情况下,调用不会阻塞而直接返回错误信息。
l F_ULOCK:对指定的文件部分进行移除锁操作。使用该参数有可能导致加锁部分被分成两部分。
l F_TEST:检测是否加锁。返回0表示文件指定部分未加锁或被当前进程加锁,返回-1表示文件被其他进程加锁。
如果参数len为正值,则加锁部分为pos到pos+len-1(pos为当前文件指针位置);如果len为负值,则加锁部分为pos-len到pos-1;如果len为0,表示加锁部分为pos到文件结束位置。
int flock(int fd, int operation);
flock函数用于实现对文件加锁或解锁的功能。与lockf函数不同,flock不能对文件某个部分加锁,只能对整个文件加锁。flock函数依赖于operation参数,实现相应的功能。openation参数可取如下值:
l LOCK_SH:设置共享锁,多个进程可同时对同一文件拥有共享锁。
l LOCK_EX:设置互斥锁。
l LOCK_UN:解除文件锁。
在调用flock函数设置文件锁时,如果文件已经被其他进程锁定,进程将处于阻塞状态。要使进程不会处于阻塞状态,需要使用LOCK_NB参数,例如flock(fd, LOCK_EX | LOCK_NB)这种形式来调用flock函数。
一个文件不能同时设置共享锁和互斥锁。flock函数所加的文件锁会在调用execve函数后保存下来。使用dup()或fork()时,文件描述符不会继承此种锁定。
信号:
信号是一种通知进程发送某种事件的机制。
在编程实现时,可以通过设置回调函数,实现对信号的不同处理。在捕捉到信号时,可以选择:
l 使用系统默认处理方法:Linux系统对各种信号提供了默认的处理方法。
l 设置捕捉到信号的处理方法:可以通过修改默认信号处理方法,使得进程在收到某个信号的时候执行指定的函数。
l 忽略该信号:对捕捉到的信号不做任何处理。
不是每个进程或用户都可以向系统中的其他进程发送信号。除了系统内核和root用户外,只有在进程具有相同的uid和gid或出于同一进程组,才可以在进程间使用信号进行通信。
常见信号在<asm/signal.h>文件中定义。Linux支持64种信号。
从可靠性方面来说,信号可分为可靠信号和不可靠信号;而从时间上来说,信号又可分为实时信号和非实时信号两大类。
l 不可靠信号:指的是信号值小于SIGRTMIN的信号。对于UNIX系统而言,不可靠信号指的是对于这些信号,程序有可能对信号采取错误的处理及存在信号丢失。
l 可靠信号:指的是信号值在SIGRTMIN到SIGRTMAX间的信号。不存在不可靠信号可能对信号采取错误的处理及存在信号丢失的问题。而且,这些信号支持队列,这样进程在同时捕获多个信号的时候,不会由于调用处理函数而忽略后面收到的信号。
int kill(pid_t pid, int sig);
kill函数用于给进程组或进程发送信号。参数sig为要发送的信号。参数pid取不同的值时,kill函数会产生不同的行为。
int raise(int sig);
raise函数用于给调用进程自身发送信号。等同于kill(getpid(), sig).
unsigned int alarm(unsigned int seconds);
为进程设置警告时钟,在达到该时钟后,给进程发送SIGALRM信号。调用alarm函数后,原来设置的警告时钟将失效。
默认情况下,进程在收到该信号后将终止进程的运行。当然可以修改捕获该信号时的默认处理函数。
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal函数用于设置收到某个信号时的处理函数。参数signum为要捕获的信号值。handler参数为指向要调用的函数指针,可以设置为SIG_IGN(忽略该信号)或SIG_DFL.(采用默认方法),也可以是用户自己编写的处理函数。
对于SIGKILL信号或SIGSTOP信号而言,这两个信号是不能够被捕获或忽略的。
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigaction解决了signal函数对信号采取错误处理的问题。sigaction用于检查或修改指定信号的处理动作。