在使用条件变量的时候,下面哪个步骤是正确的?
1、对互斥量加锁
2、改变互斥量保护的条件
3、给等待条件的线程发信号
4、对互斥量解锁
或者
1、对互斥量加锁
2、改变互斥量保护的条件
3、对互斥量解锁
4、给等待信号的线程发信号
总的来说,这两种情况可能都是正确的,但每一种方法都有不足之处。
在第一种情况下,等待线程可能会被安排在调用 pthread_cond_broadcast之后运行。如果程序运行在多处理机上,由于还持有互斥锁,一些线程就会运行而马上阻塞。
在第二种情况下,运行线程可以在第三步和第四步之间获取互斥锁,然后使条件失效,最后释放互斥锁。接着,当调用 pthread_cond_broadcast时,条件不再为真,线程无需运行,这就使为什么唤醒线程必须重新检查条件,不能仅仅因为 pthread_cond_wait返回就假定条件就为真。
看过书上之后,发现看的迷迷糊糊,就上网提问了,得到了以下回答:
http://bbs.youkuaiyun.com/topics/390980156
1、对互斥量加锁
2、改变互斥量保护的条件
3、给等待条件的线程发信号
4、对互斥量解锁
/ /等待线程收到在 第3步发送的信号 而运行,但又立刻阻塞在互斥锁上,因为此时持有锁的线程 第4步 还未运行
或者
1、对互斥量加锁
2、改变互斥量保护的条件
3、对互斥量解锁
4 、给等待信号的线程发信号
//第3步后,可能另一线程运行并使条件为假,这时在第 4 步后,线程就不应该运行,如果不判断条件、仅仅因为 pthread_cond_wait返回就运行则错了,强调的是要同时判断两者
守护进程特征
守护进程常常在系统引导装入时启动,仅在系统关闭时才终止。是在后台运行的。大多数首进程都是以超级用户特权运行。所有的守护进程都没有控制终端,其终端名设置为问号。
用户层守护进程的父进程是init进程
守护进程编程规则:
这个规则之前提到过,这里我们详细的看一看
(一)、首先要做的是调用 umask 将文件创建模式创建屏蔽字设置为一个已知值
因为这个是会被继承的,这里要为将来使用作准备
(二)、调用fork,然后使父进程 exit。
1、如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止会让shell认为这条命令已经执行完毕
2、让子进程不是进程组组长,作为下面要调用的setsid的先决条件。
(三)、调用setsid创建一个新会话
1、成为新会话的会话首进程
2、成为一个新进程组的组长进程
3、没有控制终端
(三. 五) 在这里,有人建议此时再使用一个fork, 终止父进程,这样就保证了该进程不是会话首进程,可以防止它获取控制终端。
另一种方式是 无论何时打开一个终端设备,都指定 O_NOCTTY
(四)、将当前工作目录改为根目录
从父进程继承来的当前工作目录可能是一个挂在的文件系统,因为守护进程必须是一直存在的,那么该文件系统就不能被卸载
当然,可能是其他目录
(五)、关闭不再需要的文件描述符
(六)、某些守护进程打开 /dev/null 使其具有文件描述父 0,1,2,这样任何一个试图读标准输入、写标准输出或标准错误的库例程都不会产生任何效果
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void mdaemonize()
{
int i,n,fd0,fd1,fd2;
pid_t pid;
struct sigaction sa;
umask(0);
if((pid=fork()) < 0)
exit(1);
else if(pid != 0)
exit(0);
setsid();
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
// why do we have to set SIGHUP? 我查了一下,可能是因为避免session leader退出的时候发送过来的SIGHUP,但因为我们不是前台进程,所以不会收到这个信号
if(sigaction(SIGHUP,&sa,NULL) < 0)
exit(1);
//<strong>在UNP中,书上说这里必须要设置SIGHUP,因为当会话头进程(首个fork产生的子进程)终止时,其会话中的所有进程(即再次fork产生的子进程)都收到SIGHUP信号
</strong> if((pid=fork()) < 0)
exit(1);
if(pid != 0)
exit(0);
if(chdir("/") < 0)
exit(1);
// close all the descriptors
n = getdtablesize();
for(i=0; i<n; i++)
close(i);
// Attach file descriptor 0,1 and 2 to /dev/null
fd0 = open("/dev/null",O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
if(fd0!=0 || fd1!=1 || fd2 !=2)
{
printf("wrong , %d %d %d\n",fd0,fd1,fd2);
exit(1);
}
sleep(10);
}
int main()
{
mdaemonize();
return 0;
}
以上程序的运行结果为
uid pid ppid pgid sid
500 5046 1 5045 5045 0 15:27 ? 00:00:00 ./DZ
大部分是理解了,就是不知到为什么需要处理SIGHUP信号呢? 哪里说会话首进程终止会发送信号SIGHUP了? 我自己试了下,接收到该信号进程终止,进程还是照旧运行啊。。。
出错记录
守护进程是如何处理出错消息的呢?
因为其本身不具备控制终端,所以不能只是简单的写道标准错误。
大多数守护进程都使用了 syslog 函数。
有3种产生日志消息的方法:
1、内核例程调用 log函数。
2、大多数守护进程调用 syslog 函数老产生日志信息。
3、无论是本机进程还是通过 TCP/IP 链接到此主机的其他主机上,都可以将日志消息发向 UDP端口 514.
此系列函数:
openlog、 syslog、 closelog 、setlogmask
单实例守护进程
为了正常运作,某些守护进程会实现为, 在同一时刻只运行该守护进程的一个副本。
例如,对cron 守护进程而言, 如果同时有多个实例运行,那么每个副本都可能试图开始某个预定的操作,于是造成该操作的重复执行,从而导致出错。
这里我们使用文件和记录锁解决这个问题。如果每一个守护进程创建一个固定名字的文件,并在该文件的整体上加一把写锁,那么只允许创建一把这样的写锁。在此之后的创建写锁的尝试都会失败,这就向守护进程指明已经有一个副本正在运行。
在守护进程终止时,这把锁被自动删除。
以下函数就通过使用文件锁说明如何使守护进程只存在一个副本。文件中存当前守护进程的进程ID。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define LOCKFILE "/var/run/daemon.pid"
#define LOCKMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int lockfile(int fd) //详情见下一章关于记录锁部分
{
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
return (fcntl(fd,F_SETLK, &fl));
}
int already_daemon()
{
int fd;
char buf[16];
if(fd = open(LOCKFILE, O_RDWR | O_CREAT, LOCKMODE) < 0)
exit(1);
if(lockfile(fd) < 0)
{
if(errno == EACCESS || errno == EAGAIN) //如果当前已经有守护进程副本在运行了,就设置 errno 为 EACCESS或EAGAIN
{
close(fd);
return 1;
}
exit(1);
}
ftruncate(fd,0); //因为我们要文件存的是当前守护进程的进程ID,这个函数用于将文件长度截断为0。另一方面,如果不截断,虽然会覆盖,但若是之前的长度超过当前要插入的字符串,那么之前的字符串会遗留一部分在文档中。
sprintf(buf,"ld",(long)getpid());
write(fd,buf,strlen(buf)+1);
return 0;
}
守护进程的惯例
1、若守护进程使用锁文件,那么通常存储在 /var/run 目录中,名字常常是 name.pid
2、若守护进程支持配置选项,那么配置文件常常在 /etc中, 名字常常是 name.conf
3、守护进程可以用命令行启动,但通常它们是由系统初始化脚本(/etc/rc* 或 /etc/init.d/*)之一启动的。如果守护进程终止时,应当自动的重新启动它,则我们可以在 /etc/inittab中为该守护进程包括 respawn项,这样,init 就将重新启动该守护进程
4、守护进程启动时会读取 配置文件,之后就不会再读取。 若我们修改了配置文件,可以发送 SIGHUP信号给守护进程,它就会重新读取配置文件。
以下我们就看一下如何通过 SIGHUP 信号来重读,这里会用到上面提到的 mdaemonize()函数和 already_daemon()函数(此程序使用 sigwait函数 )(这里依旧不进行守护进程的出错处理)
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
sigset_t mask;
void reread()
{
//....
}
void *th_fun(void *arg)
{
int signo;
while(1)
{
if(sigwait(&mask,&signo) != 0) //利用线程处理 SIGHUP和 SIGTERM信号,一个是重读配置文件,一个是终止
exit(1);
switch(signo)
{
case SIGHUP:
reread();
break;
case SIGTERM:
exit(0);
default:
//出错处理
}
}
}
int main(int ac, char **av) //参数即为配置文件
{
char *cmd;
pthread_t tid;
struct sigaction sa;
if((cmd=strrchr(av[0],'/')) == NULL) //取出配置文件的名字
cmd = av[0];
else
cmd ++;
mdaemonize(cmd);
if(already_daemon()) //发现已经有一个当前守护进程的副本存在,选择退出
exit(1);
sa.sa.handler = SIG_DFL; //接着就是设置SIGHUP的处理为默认。因为在 mdaemonize函数中,我们将此信号设置为忽略,如果这里不改变,那么创建的线程也将会忽略,从而即使有sigwait函数在等他,这个信号也永远不会出现。
sigemptyset(&sa.sa_mask);
sa.sa_flag = 0;
if(sigaction(SIGHUP, &sa, NULL) < 0)
exit(1);
sigfillempty(&mask;) //我们处理信号的是用一个线程来处理的,所以首先在主线程里屏蔽所有信号
if(pthread_sigmask(SIG_BLOCK,&mask,NULL) != 0)
exit(1);
if(pthread_create(&tid,NULL,th_fun,0) != 0)
exit(1);
// other things to deal with
return 0;
}
虽然SIGHUP和SIGTERM的默认动作是终止进程。因为我们阻塞了这些信号,所以当SIGHUP和SIGTERM的其中一个发送到守护进程的时候,守护进程不会消亡,作为代替,调用sigwait的线程在返回时候将指示已经接收到该信号。
当然,我们也可以不用线程而是简单的将处理放在信号处理函数中。
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
sigset_t mask;
void reread()
{
//....
}
void sigterm(int s)
{
//守护进程消息处理syslog
exit(1);
}
void sighup(int s)
{
reread();
}
int main(int ac, char **av) //参数即为配置文件
{
char *cmd;
pthread_t tid;
struct sigaction sa;
if((cmd=strrchr(av[0],'/')) == NULL) //取出配置文件的名字
cmd = av[0];
else
cmd ++;
mdaemonize(cmd);
if(already_daemon()) //发现已经有一个当前守护进程的副本存在,选择退出
exit(1);
sa.sa.handler = sighup;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask,SIGHUP);
sa.sa_flag = 0;
if(sigaction(SIGHUP, &sa, NULL) < 0)
exit(1);
sa.sa.handler = sigterm;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask,SIGTERM);
sa.sa_flag = 0;
if(sigaction(SIGTERM, &sa, NULL) < 0)
exit(1);
// other things to deal with
return 0;
}
在学习过程中,我发现对 SIGHUP的理解一直存在疑惑。这里先贴一个地址,虽然看了依旧不是非常清楚,但至少能学到点什么
http://bbs.chinaunix.net/thread-766356-1-1.html