Linux进程内消息总线设计

文章目录

  • Windows平台进程内消息总线
  • 如果没有消息总线,会产生什么问题
    • 死循环包含关系
    • 高耦合、低内聚
  • 消息总线
    • 结构图
    • 原理
      • 生产者与总线的关系
      • 总线与消费者的关系
  • Linux进程内消息总线设计
    • 使用进程间实时信号来实现进程内消息总线
      • 参考文档
      • 整体流程
        • 主线程注册总线消息处理函数
        • 生产者线程产生并发送消息到总线
        • 接收并处理消息
          • 总线接收到消息
          • 总线消息处理函数
          • 消费者总线消息处理虚函数
          • 消费者消息处理实函数
      • 核心原理
        • sigqueue发送消息
        • sigaction处理消息
      • 存在的问题
        • 消息被哪个线程执行,不可预知
        • 不可调用不可重入函数
        • 不可调用异步信号不安全函数
    • 使用线程间信号来实现进程内消息总线
      • 整体流程
        • 主线程注册总线消息处理函数
          • 注册总线消息处理函数
          • 创建总线消息处理专用线程
          • 总线消息处理专用线程等待消息
        • 生产者线程产生并发送消息到总线
        • 接收并处理消息
          • 总线消息处理专用线程接收到消息
          • 总线消息处理函数
          • 消费者总线消息处理虚函数
          • 消费者消息处理实函数
      • 核心原理
        • sigprocmask阻塞所有异步消息
        • pthread_sigqueue发送消息
        • 创建总线消息专用线程使用sigwaitinfo接收消息
    • 小结

Windows平台进程内消息总线

??Windows平台下开发C/C++程序,同一进程中的不同模块,如果要发送消息,可以使用PostMessage、SendMessage等函数。其中:PostMessage为异步消息发送接口;SendMessage为同步消息发送接口。

??比如:

??如果窗口A要给窗口B发送消息。那么:窗口A只需要拥有窗口B的窗口句柄,然后 调用PostMessage向窗口B的句柄发送消息;窗口B只需要在自己的消息处理函数中增加对消息的处理即可。

??如果窗口B要给窗口A发送消息。那么:窗口B只需要拥有窗口A的窗口句柄,然后调用 PostMessage向窗口A的句柄发送消息;窗口A只需要在自己的消息处理函数中增加对消息的处理即可。

??因此,无论窗口A向窗口B发送消息,还是窗口B给窗口A发送消息,或者A、B之间互发消息,A、B都不需要关注对方如果处理消息,而只需要关注对方需要处理什么消息。

如果没有消息总线,会产生什么问题

??还是上面的例子,如果没有消息总线:

??如果窗口A要给窗口B发送消息。那么窗口A需要调用窗口B的消息处理函数。窗口A需要包含窗口B的头文件。窗口A需要关注窗口B如何处理消息。即:窗口A跟窗口B耦合度太高。

??如下图所示:

??如果窗口B要给窗口A发送消息。那么窗口B需要调用窗口A的消息处理函数。窗口B需要包含窗口A的头文件。窗口B需要关注窗口A如何处理消息。即:窗口B跟窗口A耦合度太高。

??如下图所示:

死循环包含关系

??如果窗口A要给窗口B发送消息,同时窗口B也要给窗口A发送消息。那么窗口A需要包含窗口B的头文件,同时窗口B也需要包含窗口A的头文件。这样就会出现A包含B,同时B又包含A的死循环包含关系。在编译的时候就出错。当然即便出现了死循环关系,我们也可以通过一些小技巧避免编译时错误。这里不再缀述。

??如下图所示:

高耦合、低内聚

??如果没有消息总线,消息发送者就必须关注消息接收者如何处理消息,从而导致发送者模块与接收者模块间的强耦合关系。这种设计不符合软件设计的高内聚、低耦合原则。

消息总线

??消息总线正好可以解决以上问题。

结构图

原理

??在消息总线的设计中,共有三个角色、一个定义、一个消息处理过程。三个角色是:生产者、消费者、总线。一个定义是:消息定义。一个消息处理过程是:消息处理函数。生产者只负责生产消息。消费者只负责处理消息。总线只负责接收和驱动消息。生产者与消费者之间,共享消息定义。 总线的子类的实例包含了所有消费者。各消费者只需要实现自己关注的消息处理函数,即可完成消息处理。

生产者与总线的关系

??生产者负责生产消息,并将消息发送给总线,总线负责接收消息。

总线与消费者的关系

??总线负责接收消息,并驱动消费者执行消息处理函数。消费者总线继承于总线。消息者总线中包含了所有的消费者。各消息者只需要实现自己关注的消息处理函数,即可完成消息处理。

??这样生产者与消费者之间的耦合性仅存在于消息定义。实现了两者间的高内聚、低耦合。同时当两者需要互发消息时,也解除了两者间的死循环包括关系。

??由于消息发送者不需要关注消息的处理。因此,消息总线给程序设计带来以下好外:

  • 避免了发送者与接收者之间的死循环包含关系。
  • 模块符合高内聚、低耦合的设计原则,模块易于维护。

Linux进程内消息总线设计

??Windows平台进程内消息总线的实现,可以通过:PostMessage,SendMessage函数来实现。遗憾的是,Linux系统本身并未直接提供与PostMessage(SendMessage)函数功能类似的函数供进程内不同模块间实现消息互通。不过,我们可以使用Linux系统的相关库函数自己封装一套与PostMessage(SendMessage)函数功能类似的进程内消息总线。

??下面我们将详细介绍两种方案来实现Linux进程内消息总线。第一种方案:使用进程间实时信息来实现。第二种方案:使用线程间信号来实现。同时,我们将对比两种方案的优劣。

使用进程间实时信号来实现进程内消息总线

参考文档

《Linux-UNIX系统编程手册》第22.8章 实时信号

整体流程

??消息总线的主要流程如下:

  • 主线程注册总线消息处理函数

    • 注册总线消息处理函数
    • 总线等待消息
  • 生产者线程产生并发送消息到总线
  • 接收并处理消息
    • 总线接收到消息
    • 总线消息处理函数
    • 消费者总线消息处理虚函数
    • 消费者消息处理实函数

主线程注册总线消息处理函数

int main(int argc, char* argv[])
{
	int error = S_OK;

	if (nullptr != g_Service)
	{
	    ...
		message_register_handler(message_handler, g_Service);
        ...
	}

	message_unregister_handler();
	return 0;
}
int message_register_handler(lpfn_message_handler msgHandler, void *userData)
{
	int hr = S_OK;
	struct sigaction act;

	g_userSig.signum = SIGUSER;
	g_userSig.msgHandler = msgHandler;
	g_userSig.userData = userData;

	//register message process function
	act.sa_sigaction = signal_handler;
	act.sa_flags = SA_SIGINFO;
	//wait signal
	hr = sigaction(SIGUSER, &act, NULL);
	return hr;
}

生产者线程产生并发送消息到总线

int message_post(int message, void *data1, void *data2)
{
	if (!g_userSig.tidp)
		return 0;

	int hr = 0;
	pid_t pid = getpid();
	union sigval val;
	user_signal_data_t *sig = (user_signal_data_t *)malloc(sizeof(user_signal_data_t));
	memset(sig, 0, sizeof(user_signal_data_t));

	sig->checkCode = USER_SIGNAL_CHECK_CODE;
	sig->message = message;
	sig->data1 = data1;
	sig->data2 = data2;

	val.sival_ptr = sig;
	hr = sigqueue(pid, g_userSig.signum, val);
	if (0 != hr)
	{
		//EAGAIN
		printf("message post: hr:%d, errorcode: %d, errorstr: %s\n", hr, errno, strerror(errno));
		hr = E_PTHREAD_SIGQUEUE_FAILED;
	}

	return hr;
}

接收并处理消息

总线接收到消息

原理请参考:《Linux-UNIX系统编程手册》22.8.2 处理实时信号

//总线等待消息。收到消息时,即会触发总线消息处理函数
	hr = sigaction(SIGUSER, &act, NULL);
总线消息处理函数
void signal_handler(int signum, siginfo_t * info, void * context)
{
	user_signal_data_t *pdata = nullptr;
	if (g_userSig.signum == signum)
	{
		if (context)
		{
			pdata = (user_signal_data_t *)info->si_ptr;
			if (g_userSig.msgHandler)
			{
				g_userSig.msgHandler(pdata->message, pdata->data1, pdata->data2, g_userSig.userData);
			}
			free(pdata);
			pdata = nullptr;
		}
	}
	else
	{
		printf("unknown signal: %d\n", signum);
	}
}
int message_handler(int message, void *msgData1, void *msgData2, void *userData)
{
	CBaseService *service = (CBaseService *)userData;
	service->on_message(message, msgData1, msgData2);
	return S_OK;
}
消费者总线消息处理虚函数
int CBaseComsumerService::on_message(int message, void *msgData1, void *msgData2)
{
	int hr = E_NOTIMPL;
	return hr;
}
消费者消息处理实函数
int CComsumerService::on_message(int message, void *msgData1, void *msgData2)
{
	int hr = S_OK;
	hr = CBaseComsumerService::on_message(message, msgData1, msgData2);
	if (SUCCEEDED(hr))
		return hr;

    //process messages related to myself only
	switch (message)
	{
	case UM_MESSAGE_A:
		on_msg_a(message, msgData1, msgData2);
		break;
	case UM_MESSAGE_B:
		on_msg_b(message, msgData1, msgData2);
		break;
	default:
		hr = E_NOTIMPL;
		break;
	}

	return hr;
}

核心原理

sigqueue发送消息

??在消息总线的设计中,使用sigqueue携带自定义参数,将消息发送给本进程。从而实现了进程内消息的发送。

sigaction处理消息

??使用sigaction接收消息,并交给signal_handler信号处理函数。signal_handler调用注册的总线消息处理函数:message_handler。message_handler调用消费者的消息处理虚函数,实现消息的分发。

存在的问题

??本小节的大多数内容摘抄自:《Linux-UNIX系统编程手册》第21.1.2章 可重入函数和异步信号安全函数。

消息被哪个线程执行,不可预知

??使用sigqueue发送信号时,对信号的处置属于进程层面,进程中的所有线程共享对每个信号的处置设置。如果某一线程使用函数sigaction()为某类信号(比如,SIGINT)创建了处理函数,那么当收到SIGINT时,任何线程都会去调用该处理函数。与之类似,如果将对信号的处置设置为忽略(ignore),那么所有线程都会忽略该信号。

??这意味着,在本例的消息总线中:消息被哪个线程执行,不可预知。

不可调用不可重入函数

??要解释可重入函数、不可重入函数为何物,首先需要区分单线程程序和多线程程序。典型UNIX程序都具有一条执行线程,贯穿程序始终,CPU围绕单条执行逻辑来处理指令。而对于多线程程序而言,同一进程却存在多条独立、并发的执行逻辑流。

??不过,多执行线程的概念与使用了信号处理器函数的程序也有关联。因为信号处理器函数可能会在任一时点异步中断程序的执行,从而在同一个进程中实际形成了两条(即主程序和信号处理器函数)独立(虽然不是并发)的执行线程。

??如果同一个进程的多条线程可以同时安全地调用某一函数,那么该函数就是可重入的。此处,“安全”意味着,无论其他线程调用该函数的执行状态如何,函数均可产生预期结果。

??更新全局变量或静态数据结构的函数可能是不可重入的。(只用到本地变量的函数肯定是可重入的。)如果对函数的两个调用(例如:分别由两条执行线程发起)同时试图更新同一全局变量或数据类型,那么二者很可能会相互干扰并产生不正确的结果。例如,假设某线程正在为一链表数据结构添加一个新的链表项,而另一线程也正试图更新同一链表。由于为链表添加新项涉及对多枚指针的更新,一旦另一线程中断这些步骤并修改了相同的指针,结果就会产生混乱。

??在 C 语言标准函数库中,这种可能性非常普遍。例如,malloc()和free()就维护有一个针对已释放内存块的链表,用于从堆中重新分配内存。如果主程序在调用 malloc()期间为一个同样调用malloc()的信号处理器函数所中断,那么该链表可能会遭到破坏。因此,malloc()函数族以及使用它们的其他库函数都是不可重入的。

??还有一些函数库之所以不可重入,是因为它们使用了经静态分配的内存来返回信息。此类函数的例子包括 crypt()、getpwnam()、gethostbyname()以及

getservbyname()。如果信号处理器用到了这类函数,那么将会覆盖主程序中上次调用同一函数所返回的信息(反之亦然)。

??将静态数据结构用于内部记账的函数也是不可重入的。其中最明显的例子就是stdio函数库成员(printf()、scanf()等),它们会为缓冲区I/O更新内部数据结构。所以,如果在信号处理器函数中调用了printf(),而主程序又在调用printf()或其他stdio 函数期间遭到了处理器函数的中断,那么有时就会看到奇怪的输出,甚至导致程序崩溃或者数据的损坏。

??即使并未使用不可重入的库函数,可重入问题依然不容忽视。如果信号处理器函数和主程序都要更新由程序员自定义的全局性数据结构,那么对于主程序而言,这种信号处理器函数就是不可重入的。

??如果函数是不可重入的,那么其手册页通常会或明或暗地给出提示。对于其中那些使用或返回静态分配变量的函数,需要特别留意。

??这意味着,在本例的消息总线中:像malloc、printf等系统函数均不可使用,因为他们都是不可重入函数。

不可调用异步信号不安全函数

??异步信号安全的函数是指当从信号处理器函数调用时,可以保证其实现是安全的。如果某一函数是可重入的,又或者信号处理器函数无法将其中断时,就称该函数是异步信号安全的。

??POSIX.1-1990提供了异步信号安全函数表。SUSv3强调,表之外的所有函数对于信号而言都是不安全的,但同时指出,仅当信号处理器函数中断了不安全函数的执行,且处理器函数自身也调用了这个不安全函数时,该函数才是不安全的。换言之,编写信号处理器函数有如下两种选择。

  • 确保信号处理器函数代码本身是可重入的,且只调用异步信号安全的函数。
  • 当主程序执行不安全函数或是去操作信号处理器函数也可能更新的全局数据结构时,阻塞信号的传递。

??第 2 种方法的问题是,在一个复杂程序中,要想确保主程序对不安全函数的调用不为信号处理器函数所中断,这有些困难。出于这一原因,通常就将上述规则简化在信号处理器函数中绝不调用不安全的函数。

??这意味着,在本例的消息总线中:不可调用异步信号不安全函数。

使用线程间信号来实现进程内消息总线

整体流程

??消息总线的主要流程如下:

  • 主线程注册总线消息处理函数

    • 注册总线消息处理函数
    • 创建总线消息处理专用线程
    • 总线消息处理专用线程等待消息
  • 生产者线程产生并发送消息到总线
  • 接收并处理消息
    • 总线消息处理专用线程接收到消息
    • 总线消息处理函数
    • 消费者总线消息处理虚函数
    • 消费者消息处理实函数

主线程注册总线消息处理函数

注册总线消息处理函数
int main(int argc, char* argv[])
{
	int error = S_OK;

	if (nullptr != g_Service)
	{
	    ...
		message_register_handler(message_handler, g_Service);
        ...
	}

	message_unregister_handler();
	return 0;
}
创建总线消息处理专用线程
int message_register_handler(lpfn_message_handler msgHandler, void *userData)
{
	int hr = S_OK;
	g_userSig.signum = SIGUSER;
	g_userSig.msgHandler = msgHandler;
	g_userSig.userData = userData;

	if (0 != (pthread_create(&g_userSig.tidp, nullptr, message_thread_function, (void*)&g_userSig)))
	{
		printf("create message thread error!\n");
		hr = E_PTHREAD_CREATE_FAILED;
	}

	return hr;
}
总线消息处理专用线程等待消息
static void * message_thread_function(void *arg)
{
	sigset_t set;
	int sig;
	siginfo_t info;
	user_signal_t *usersig = (user_signal_t *)arg;

	/* wait SIGUSER. process must block SIGUSER, otherwise, SIGUSER signal may be processed  by other thread*/
	sigemptyset(&set);
	sigaddset(&set, SIGQUIT);//wait SIGQUIT, to exit thread
	sigaddset(&set, SIGUSER);

	while (true)
	{
		sig = sigwaitinfo(&set, &info);
		if (sig < 0)
		{
			if (errno == EINTR)
			{
				perror("message thread: sigwaitinfo ");
				continue;
			}

			printf("message thread: parent error: %s\n", strerror(errno));
			break;
		}

		if (SIGQUIT == sig)
		{
			printf("message thread: receive quit signal\n");
			break;
		}

		if (SIGUSER == sig)
		{
			user_signal_data_t *pdata = nullptr;
			pdata = (user_signal_data_t *)info.si_ptr;
			if (pdata)
			{
				if (USER_SIGNAL_CHECK_CODE == pdata->checkCode)
				{
					if (usersig->msgHandler)
					{
						usersig->msgHandler(pdata->message, pdata->data1, pdata->data2, g_userSig.userData);
					}

					free(pdata);
					pdata = nullptr;
				}
				else
				{
					printf("message thread: receive unknown message data\n");
				}
			}
			else
			{
				printf("message thread: receive data pointer is null\n");
			}
		}
	}

	printf("message thread: exit!\n");
	return nullptr;
}

生产者线程产生并发送消息到总线

int message_post(int message, void *data1, void *data2)
{
	if (!g_userSig.tidp)
		return 0;

	int hr = 0;
	pid_t pid = getpid();
	union sigval val;
	user_signal_data_t *sig = (user_signal_data_t *)malloc(sizeof(user_signal_data_t));
	memset(sig, 0, sizeof(user_signal_data_t));

	sig->checkCode = USER_SIGNAL_CHECK_CODE;
	sig->message = message;
	sig->data1 = data1;
	sig->data2 = data2;

	val.sival_ptr = sig;

	/* send signal to a specific thread. this can ensure signals are processed correctly. because:
	* 1. only one thread processes all signals, so it makes asynchronous signal processing to become synchronously.
	* 2. system functions like malloc, printf, etc. will not be interrupted by other signals, so they are reentrant in this prcocess.
	*/
	hr = pthread_sigqueue(g_userSig.tidp, g_userSig.signum, val);
	if (0 != hr)
	{
		//EAGAIN
		printf("message post: hr:%d, errorcode: %d, errorstr: %s\n", hr, errno, strerror(errno));
		hr = E_PTHREAD_SIGQUEUE_FAILED;
	}

	return hr;
}

接收并处理消息

总线消息处理专用线程接收到消息

原理请参考:《Linux-UNIX系统编程手册》22.10 以同步方式等待信号

	sig = sigwaitinfo(&set, &info);
	...
	usersig->msgHandler(pdata->message, pdata->data1, pdata->data2, g_userSig.userData);
总线消息处理函数
int message_handler(int message, void *msgData1, void *msgData2, void *userData)
{
	CBaseService *service = (CBaseService *)userData;
	service->on_message(message, msgData1, msgData2);
	return S_OK;
}
消费者总线消息处理虚函数
int CBaseComsumerService::on_message(int message, void *msgData1, void *msgData2)
{
	int hr = E_NOTIMPL;
	return hr;
}
消费者消息处理实函数
int CComsumerService::on_message(int message, void *msgData1, void *msgData2)
{
	int hr = S_OK;
	hr = CBaseComsumerService::on_message(message, msgData1, msgData2);
	if (SUCCEEDED(hr))
		return hr;

    //process messages related to myself only
	switch (message)
	{
	case UM_MESSAGE_A:
		on_msg_a(message, msgData1, msgData2);
		break;
	case UM_MESSAGE_B:
		on_msg_b(message, msgData1, msgData2);
		break;
	default:
		hr = E_NOTIMPL;
		break;
	}

	return hr;
}

核心原理

??不可重入函数、异步信号不安全函数,均无法在信号处理函数中安全加以调用。因为这些原因,所以当多线程应用程序必须处理异步产生的信号时,通常不应该将信号处理函数作为接收信号到达的通知机制。相反,推荐的方法如下。

  • 所有线程都阻塞进程可能接收的所有异步信号。最简单的方法是,在创建任何其他线程之前,由主线程阻塞这些信号。后续创建的每个线程都会继承主线程信号掩码的一份拷贝。
  • 再创建一个专用线程,调用函数sigwaitinfo()、sigtimedwait()或sigwait()来接收收到的信号。当接收到信号时,专有线程可以安全地修改共享变量(在互斥量的保护之下),并可调用并非异步信号安全(non-async-signal-safe)的函数。也可以就条件变量发出信号,并采用其他线程或进程的通讯及同步机制。

??使用线程间信号来实现的进程内消息总线,就是依据此推荐方案来实现的。

sigprocmask阻塞所有异步消息

	sigset_t newmask, oldmask;

	//block SIGUSER for all threads
	sigemptyset(&oldmask);
	sigemptyset(&newmask);
	sigaddset(&newmask, SIGUSER);
	sigprocmask(SIG_BLOCK, &newmask, &oldmask);

pthread_sigqueue发送消息

int message_post(int message, void *data1, void *data2)
{
    ...
	hr = pthread_sigqueue(g_userSig.tidp, g_userSig.signum, val);
    ...
}

创建总线消息专用线程使用sigwaitinfo接收消息

static void * message_thread_function(void *arg)
{
	while (true)
	{
		sig = sigwaitinfo(&set, &info);

	    ...
		usersig->msgHandler(pdata->message, pdata->data1, pdata->data2, g_userSig.userData);
		...
	}

	return nullptr;
}

小结

??使用线程间信号来实现的进程内消息总线,基本实现了与Windows的PostMessage(SendMessage)函数功能类似的进程内消息总线的功能。本文的代码片断,主要用来帮助理解设计的原理和过程。有兴趣的朋友有自己基于这些片断,实现自己的业务功能。

原文地址:https://www.cnblogs.com/augustuss/p/12191189.html

时间: 2024-10-09 15:27:21

Linux进程内消息总线设计的相关文章

[转载] 站内消息DB设计思路

[转载]:点击打开链接  原文标题:两年后,再议“站内信”的实现  注:仅因为在工作中用到相似的东西,故借鉴作者的文章,如有不便请告知,即刻删除 两年前,万仓一黍在博客园发了两篇关于站内信的设计实现博文,<群发“站内信”的实现>.<群发“站内信”的实现(续)>,其中阐述了他关于站内信群发的设计思想,很具有借鉴意义.他在设计时考虑到用户量和存储空间的占用等问题.当然,在他的两篇博文中强调了站内信的设计要考虑具体情况,没有理想的设计方案,他的设计只是对于群发(点到面)的解决方案. 在此

linux 进程间消息队列通讯

转自:http://blog.csdn.net/lifan5/article/details/7588529 http://www.cnblogs.com/kunhu/p/3608589.html 前言: 消息队列是内核地址空间中的内部链表,通过linux内核在各个进程之间传递内容,消息顺序地发送到消息队列中,并且以几种不同的方式 从队列中获取,每个消息队列可以用IPC标识符唯一的进行标识,内核中的消息队列是通过IPC的标识符来区别的,不同的消息队列之间是 相互独立的,每个消息队列中的消息又构成

ZeroMQ接口函数之 :zmq_inproc – &#216;MQ 本地进程内(线程间)传输方式

ZeroMQ API 目录 :http://www.cnblogs.com/fengbohello/p/4230135.html ————————————————————————————————————— ZeroMQ 官方地址:http://api.zeromq.org/4-2:zmq-inproc zmq_inproc(7)   ØMQ Manual - ØMQ/4.2.0 Name zmq_inproc – ØMQ 本地进程内(线程间)传输方式 Synopsis 进程内传输方式意味着在共享

微服务实战(二):落地微服务架构到直销系统(构建消息总线框架接口)

从上一篇文章大家可以看出,实现一个自己的消息总线框架是非常重要的内容,消息总线可以将界限上下文之间进行解耦,也可以为大并发访问提供必要的支持. 消息总线的作用: 1.界限上下文解耦:在DDD第一波文章中,当更新了订单信息后,我们通过调用经销商界限上下文的领域模型和仓储,进行了经销商信息的更新,这造成了耦合.通过一个消息总线,可以在订单界限上下文的WebApi服务(来源微服务-生产者)更新了订单信息后,发布一个事件消息到消息总线的某个队列中,经销商界限上下文的WebApi服务(消费者)订阅这个事件

消息总线授权设计

我曾在之前的一篇文章中对比过消息队列跟消息总线.它们其中的一个不同点就是:消息总线更关注通信安全,消息总线可以管控通信双方,对通信的管控是建立在授权的基础上.因此授权模型的设计是消息总线必须考虑的问题.所谓的授权,就是校验通信双方有没有建立可信任的通信关系.这篇文章我们来谈谈消息总线的权限设计. 消息总线使用场景及RabbitMQ通信简介 在介绍授权设计之前,我们先了解一些必要信息.通常我们将消息总线应用于以下这些场景: 缓冲类--自生产自消费 解耦类.异步类--生产者消费者模型 服务调用类(R

Linux进程调度器的设计--Linux进程的管理与调度(十七)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 前景回顾 进程调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换. 内核必须提供一种方法, 在各个进程之间尽可能公平地

[转]Linux进程通信之POSIX消息队列

进程间的消息队列可以用这个实现,学习了下. http://blog.csdn.net/anonymalias/article/details/9799645?utm_source=tuicool&utm_medium=referral 消息队列是Linux IPC中很常用的一种通信方式,它通常用来在不同进程间发送特定格式的消息数据. 消息队列和之前讨论过的管道和FIFO有很大的区别,主要有以下两点: 一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待该消息的到达,而管道和FIFO是相

java linux 项目经常无故被关闭 进程无故消息

布了几个项目.居然天天会自动的挂掉.急了.花时间解决了一下.总结方案如下: 1.磁盘满了.这大家都懂,清一下 2.tomcat在关闭的或是重启的时候,常常后台进程没有被关闭.需要用ps aux|grep java 这个命令查一下,把多余的进程关掉,再启动startup.sh 3.这种情况比较少见,就是在系统资源缺少的情况下,被系统自动DOWN掉,或是被其它软件干掉了. 其实在我这里,这样还是没有解决,进程还是莫名其妙会自己挂掉,日志也没有任何报错.后来打开tomcat主目录下的子目录conf里面

java linux 项目常常无故被关闭 进程无故消息

布了几个项目.竟然天天会自己主动的挂掉.急了.花时间攻克了一下.总结方案例如以下: 1.磁盘满了.这大家都懂,清一下 2.tomcat在关闭的或是重新启动的时候,经常后台进程没有被关闭.须要用ps aux|grep java 这个命令查一下,把多余的进程关掉,再启动startup.sh 3.这样的情况比較少见,就是在系统资源缺少的情况下,被系统自己主动DOWN掉,或是被其他软件干掉了. 事实上在我这里,这样还是没有解决,进程还是莫名其妙会自己挂掉,日志也没有不论什么报错.后来打开tomcat主文