存档

‘C/C++’ 分类的存档

用LUA做程序配置文件

2011年10月27日 16hot 没有评论

本来想用bison、flex来写一个配置文件的。但是看了看资料,不容易懂,而且也不灵活。还是放弃了。
后来想起了lua,现在流行用lua来做服务程序的配置文件。强大、灵活,而且与C可以紧密结合。

下面是C程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
 
/*
 * getcfg.c
 * cc -o getcfg getcfg.c -I/usr/local/include/lua51 -L/usr/local/lib/lua51 -llua -lm
 */
 
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
 
 
int
main(void)
{
    int status, result, ind;
	const char       *key, *value;
    double sum;
    lua_State *L;
 
    /*
     * All Lua contexts are held in this structure. We work with it almost
     * all the time.
     */
    L = luaL_newstate();
 
    luaL_openlibs(L); /* Load Lua libraries */
 
    /* Load the file containing the script we are going to run */
    if ( luaL_loadfile(L, "server.cfg") || lua_pcall(L, 0, 0, 0) ) {
        /* If something went wrong, error message is at the top of */
        /* the stack */
        fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
		lua_pop(L, 1);
        exit(1);
    }
 
	lua_getglobal(L, "socket" );
	const char *socket = lua_tostring(L, -1);
 
	printf( "socket %s\n", lua_tostring(L, -1));
 
	lua_pop(L, 2);
 
	lua_getglobal(L, "servers"); 
 
	if (!lua_istable(L, -1))
	{
		fprintf(stderr, "%s\n", lua_tostring(L, -1));
		lua_pop(L, 1); // remove the error-msg 
		return -1; 
	}
	ind = lua_gettop(L);
	printf( "index: %d\n", ind );
	lua_pushnil(L);	
	while (lua_next(L, -2) != 0)
	{
		/** 获取服务索引名 */
			key = lua_tostring(L, -2);
			printf( "key name: %s\n", key );
		if (lua_istable(L, -1))
		{
			ind = 1;
			lua_pushnil(L);
 
 
			while (lua_next(L, -2) != 0)
			{
				/** 获取服务配置 */
				key = lua_tostring(L, -2);
				if(strcmp("bin", key) == 0)
				{
					printf( "bin %s\n", lua_tostring(L, -1) );
				}
				else if(strcmp("pid", key) == 0)
				{
					printf( "pid %s\n", lua_tostring(L, -1) );
				}
				else if(strcmp("params", key) == 0)
				{
					printf( "params %s\n", lua_tostring(L, -1) );
				}
				else if(strcmp("config", key) == 0)
				{
					printf( "config %s\n", lua_tostring(L, -1) );
				}
			    else {
					printf("Unkonw key: %s\n", key );
				}
				lua_pop( L, 1 );
			}
 
		}
		lua_pop(L, 1); 
	}
 
 
    lua_close(L);   /* Cya, Lua */
 
    return 0;
}

下面是LUA代码:

1
2
3
4
5
6
7
8
9
10
11
12
 
daemon = false
 
socket = "/var/run/daemon-server.sock"
 
servers = {
	["phpd"] = { 
		pid = "/var/run/phpd.pid",
		bin = "/usr/local/sbin/phpd",
		delay = 300, 
	}
}
分类: C/C++, LUA 标签: ,

FreeBSD下根据进程名称获取PID

2011年10月27日 16hot 没有评论

想通过进程名称来监控进程是否健在,在网上找了一个小例子,改成如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * 
 * gcc getproc.c -I/usr/include -I/usr/local/include -L/usr/lib/ -L/usr/local/lib/ -lkvm -o getproc
 */
 
#include <stdio.h>   
#include <string.h>   
#include <err.h>   
#include <kvm.h>   
#include <sys/param.h>   
#include <sys/sysctl.h>   
#include <sys/user.h>   
#include <sys/param.h>   
#include <fcntl.h>   
#include <stdlib.h>   
#include <sysexits.h>    
static int  
get_pid_of_process(char *process_name)   
{   
 static kvm_t *kd = NULL;   
 struct kinfo_proc *p;   
 int i, n_processes, processes_found;   
 processes_found = 0;   
 
 if ((kd = kvm_open("/dev/null", "/dev/null", "/dev/null", O_RDONLY, "kvm_open")) == NULL)    
    (void)errx(1, "%s", kvm_geterr(kd));   
 else {   
  p = kvm_getprocs(kd, KERN_PROC_PROC, 0, &n_processes);   
  for (i = 0; i < n_processes; i++)   
   if (strncmp(process_name, p[i].ki_comm, COMMLEN+1) == 0) {   
    (void)printf("progame: %s, pid: %d \n", process_name, (int)p[i].ki_pid);   
    processes_found++;   
   }   
//~ if (p[i].ki_pid == pid){
  //~ (void)printf("%s ", p[i].ki_comm);
  //~ processes_found++;
 //~ }
  kvm_close(kd);   
 }
 return processes_found;   
}   
 
 
int main( int argc, char **argv ) {
    int pf = 0;
 
    if ( argc < 2 ) {
        printf( "Please input a process name!\n" );
        exit(0);
    }
 
    pf = get_pid_of_process( (argv[1]) );
 
    printf( "have found %d proess\n", pf );
    return 0;
}
分类: BSD/linux, C/C++ 标签: ,

关注HipHop移植到FreeBSD

2010年11月28日 16hot 没有评论

从一个博客上看到,已经在着手移植了。

http://huichen.org/en/2010/07/hiphop-for-freebsd/

分类: BSD/linux, C/C++, PHP 标签: , ,

解决了 pam_fedns.so 加载失败的问题

2010年11月27日 16hot 没有评论

为连接ssh,自己开发了个 pam 模块,命名为 pam_fedns.so。在i386环境,连接ssh没问题,amd64的环境连接就提示加载pam_fedns.so失败。

在网上找了个测试例子,检测发现少md5的连接库。加上-lmd 就搞定了。

#include <dlfcn.h>
#include <stdio.h>

int
main (int argc, char **argv) {

void *dlh;

dlh = dlopen(argv[1], RTLD_NOW);
if (dlh) {
printf(“dlopen %s worked\n”, argv[1]);
} else {
printf(“dlopen %s failed: %s\n”, argv[1], dlerror());
}
return 0;
}

原文链接:

http://freebsd.monkey.org/freebsd-stable/200709/msg00136.html

分类: BSD/linux, C/C++ 标签: , , ,

学习PAM模块开发

2010年11月13日 16hot 没有评论

原文地址: http://www.ibm.com/developerworks/cn/linux/l-pamdev/index.html

最近因为在处理SSHD登录,需要用到PAM模块,系统自带的没有能满足需求。只好自己照猫画老虎,模仿学习着写了个pam模块,工作很正常。pam确实很不错的一种认证技术。

=======================================

本文主要通过对Linux PAM源代码进行分析,阐述了PAM的内部实现机制和怎样在应用程序中应用PAM进行认证,以及怎样开发PAM服务模块。

1 引言

身份认证是操作系统安全的重要机制之一,系统通过认证机制核查用户的身份证明,并作为用户进入系统的判定条件,是防止恶意用户进入系统 的第一道门槛。近年来认证理论和技术得到了迅速发展,产生了各种认证机制,如口令机制,RSA, DCE, kerberos认证体制,S/Key和基于智能卡的身份认证等。然而,当系统中引入新的认证机制时,一些系统入口登录服务如login, rlogin和telnet等应用程序就必须改写以适应新的认证机制。为了解决这个问题,1995年Sun公司的Vipin Samar和 Charlie Lai提出了PAM(Pluggable Authentication Modules),并将其应用在Solaris系统上。PAM框架将应用程序与具体的认证机制分离,使得系统改变认证机制时,不再需要修改采用认证机制的 应用程序,而只要由管理员配置应用程序的认证服务模块,极大地提高了认证机制的通用性与灵活性。

现在大多数操作系统都采用PAM实现身份认证,有Linux系统的Linux-PAM和FreeBSD5.x采用的OpenPAM(FreeBSD 4.x采用的是Linux-PAM)等。它们的实现原理一样,只有实现细节不同而已。下面从PAM的应用开发开始介绍。


回页首

2 PAM的应用开发

2.1 PAM框架概览

PAM即可插拔认证模块。它提供了对所有服务进行认证的中央机制,适用于login,远程登录 (telnet,rlogin,fsh,ftp,点对点协议(PPP)),su等应用程序中。系统管理员通过PAM配置文件来制定不同应用程序的不同认证 策略;应用程序开发者通过在服务程序中使用PAM API(pam_xxxx( ))来实现对认证方法的调用;而PAM服务模块的开发者则利用PAM SPI来编写模块(主要是引出一些函数pam_sm_xxxx( )供PAM接口库调用),将不同的认证机制加入到系统中;PAM接口库(libpam)则读取配置文件,将应用程序和相应的PAM服务模块联系起来。 PAM框架结构如图所示。
图 PAM框架结构图
图 PAM框架结构图

其中,pamh是一个pam_handle类型的结构,它是一个非常重要的处理句柄,是PAM与应用程序通信的唯一数据结构,也是调用PAM接口库API的唯一句柄。pam_handle数据结构将在下面的源代码分析一节的介绍。

另外,如上图所示的服务模块分auth(认证管理)、account(账号管理)、session(会话管理)、passwd(口令管 理)四种类型,各个类型模块的作用以及配置文件的四个组成部分模块类型、控制标志、模块路径、模块参数等在很多讲PAM的配置管理的文章里都有介绍,这里 就不再赘述了。

2.2 在应用程序中使用PAM认证

每个使用PAM认证的应用程序都以pam_start开始,pam_end结束。PAM还提供了pam_get_item和 pam_set_item共享有关认证会话的某些公共信息,例如用户名,服务名,密码和会话函数。应用程序在调用了pam_start ()后也能够用这些APIs来改变状态信息。实际做认证工作的API函数有六个(以下将这六个函数简称为认证API):

  • 认证管理–包括pam_authenticate ()函数认证用户,pam_setcred ()设置,刷新,或销毁用户证书。
  • 账号管理–包括pam_acc_mgmt ()检查认证的用户是否可以访问他们的账户,该函数可以实现口令有效期,访问时间限制等。
  • 会话管理–包括pam_open_session ()和pam_close_session ()函数用来管理会话和记账。例如,系统可以存储会话的全部时间。
  • 口令管理–包括pam_chauthok ()函数用来改变密码。

下面看一个简单的login模拟程序:

 /* 使用PAM所必需的两个头文件*/
#include <security/pam_appl.h>
#include <security/pam_misc.h>
void main(int argc, char *argv[], char **renvp)
{
    /* 初始化,并提供一个回调函数 */
    if ((pam_start("login", user_name, &pam_conv, &pamh)) != PAM_SUCCESS)
        exit(1);
    /* 设置一些关于认证用户信息的参数 */
    pam_set_item(pamh, PAM_TTY, ttyn);
    pam_set_item(pamh, PAM_RHOST, remote_host);
    while (!authenticated && retry < MAX_RETRIES)
    {
        status = pam_authenticate(pamh, 0);/* 认证,检查用户输入的密码是否正确 */
}
/* 认证失败则应用程序退出*/
    if (status != PAM_SUCCESS)
    {
      	……
        exit(1);
}
    /*  通过了密码认证之后再调用帐号管理API,检查用户帐号是否已经过期 */
    if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS)
    {
        if (status == PAM_AUTHTOK_EXPIRED)
        {
            status = pam_chauthtok(pamh, 0);  /* 过期则要求用户更改密码 */
            if (status != PAM_SUCCESS)
                exit(1);
        }
    }
    /* 通过帐户管理检查之后则打开会话 */
    if (status = pam_open_session(pamh, 0) != PAM_SUCCESS)
        exit(status);
	……
    /* 建立认证服务的用户证书*/
    status = pam_setcred(pamh, PAM_ESTABLISH_CRED);
    if (status != PAM_SUCCESS)
       exit(status);
   	……
pam_end(pamh, PAM_SUCCESS);  /* PAM事务的结束 */
……
 }

从上面程序中,我们可以了解到使用PAM认证的一般流程,同时也可以看出PAM API使得使用认证的应用程序不仅不用关心底层使用的服务模块,而且编写起来简洁明了得多。

有关开发使用PAM的应用程序更加详细完整的阐述请参考The Linux-PAM Application Developers’ Guide。

2.3 怎样开发PAM服务模块

首先在编写的服务模块的源程序里要包含下列头文件:

#include <security/pam_modules.h>

PAM的服务模块是一个一个的动态链接库文件(也可以是静态库),PAM接口库通过dlopen来装载这些库。假设源程序名为pam_module-name.c,则需要用下列命令将其编译成动态链接库:

gcc -fPIC -c pam_module-name.c
ld -x --shared -o pam_module-name.so pam_module-name.o

选项-fPIC是指位置无关代码(Position Independent Code),这类代码支持大偏移。使用–shared选项将目标代码放进共享目标库中。

四种类型的模块各自要实现的函数如下表所示:

模块类型 要实现的函数 函数功能
认证管理 PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 认证用户
PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 设置用户证书
账号管理 PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) 帐号管理
会话管理 PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 打开会话
PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 关闭会话
口令管理 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 设置口令

当然同一个服务模块可以同时属于多种类型,只要这些类型模块要实现的函数都实现了就可以,比如PAM自带的经典口令认证机制模块pam_unix.so 就可以支持四种模块类型。

下面来看一个最简单的pam_deny模块的源程序pam_deny.c:

1.	#define PAM_SM_AUTH
2.	#define PAM_SM_ACCOUNT
3.	#define PAM_SM_SESSION
4.	#define PAM_SM_PASSWORD
5.	#include "../../libpam/include/security/pam_modules.h"
6.	/* --- 认证管理函数的实现--- */
7.	PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
8.	,const char **argv)
9.	{
10.	    return PAM_AUTH_ERR;
11.	}
12.	PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
13.	,const char **argv)
14.	{
15.	    return PAM_CRED_UNAVAIL;
16.	}
17.	/* --- 账号管理函数的实现 --- */
18.	PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc
19.	,const char **argv)
20.	{
21.	    return PAM_ACCT_EXPIRED;
22.	}
23.	/* --- 口令管理函数的实现 --- */
24.	PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc
25.	,const char **argv)
26.	{
27.	    return PAM_AUTHTOK_ERR;
28.	}
29.	/* --- 会话管理函数的实现 --- */
30.	PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc
31.	,const char **argv)
32.	{
33.	    return PAM_SYSTEM_ERR;
34.	}
35.	PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
36.	,const char **argv)
37.	{
38.	    return PAM_SYSTEM_ERR;
39.	}
40.	/* 模块定义结束 */
41.	/* 静态模块数据 */
42.	#ifdef PAM_STATIC
43.	struct pam_module _pam_deny_modstruct = {
44.	   "pam_deny",
45.	    pam_sm_authenticate,
46.	    pam_sm_setcred,
47.	    pam_sm_acct_mgmt,
48.	    pam_sm_open_session,
49.	    pam_sm_close_session,
50.	    pam_sm_chauthtok
51.	};
52.	#endif

很容易看出,pam_deny模块支持四种模块类型。前4行包含静态模块的一些原型申明,第37-39行实现了四种模块类型的函数,因 为这只是一个简单的拒绝服务的模块,所以这些函数只是简单地返回认证错或系统错等PAM错误。最后几行定义了该程序被编译成静态模块所需的一个模块数据结 构。

因此,PAM SPI使得服务模块的开发也相当简单和专一,因为服务模块不再需要考虑和应用程序的交互,只要将自己采用的算法实现好就可以了。

模块源程序可用的flags参数值和返回值的定义这里不作全面介绍,有兴趣者请参考The Linux-PAM Module Writers’ Guide。


回页首

3 PAM接口库源代码分析

上面我们介绍了怎样使用PAM和怎样开发PAM服务模块,要想对PAM的内部机制有个透彻的理解,还需要进一步分析PAM接口库的代码。下面基于Linux 的pam-0.75-40.src.rpm包所得的源代码进行分析。

3.1 PAM接口库主要数据结构

先看一下PAM接口库用到的一些主要数据结构。pam_handle和其他几个主要的数据结构(见../libpam/pam_private.h)及其之间的关系如下图所示。

其中pam_handle包含认证的用户的token、用户名、应用程序名、终端名等信息,以及一个service结构(handlers);前面几节提到的pamh句柄就是一个pam_handle结构。service结构包含服务模块的相关信息,各个域的含义是:

1. module–该结构包含装载的模块的名字、类型(静态或动态模块)、链接句柄(装载模块时的句柄)。

2. modules_allocated–分配的模块数。

3. modules_used–已使用的模块数。

4. handlers_loaded–是否对操作(handlers结构)进行了初始化,handlers结构和初始化handlers见下面的介绍。

5. conf–由应用程序相对应的配置文件指定的服务模块的handlers。

6. other–为缺省配置文件指定的服务模块的handlers。

handlers结构包含六个handler结构链表的指针,六个指针分别对应六种不同的认证API;libpam通过这些指针找到对应模块的SPI服务函数。如下表所示:

handler指针 API函数 SPI函数
authenticate pam_authenticate( ) pam_sm_authenticate( )
setcred pam_setcred( ) pam_sm_setcred( )
acct_mgmt pam_acct_mgmt( ) pam_sm_acct_mgmt( )
open_session pam_open_session( ) pam_sm_open_session( )
close_session pam_close_session( ) pam_sm_close_session( )
chauthtok pam_chauthtok( ) pam_sm_chauthtok( )

handler数据结构是最直接保存服务模块的SPI服务函数的地址及参数的结构,其包含的主要的域的含义如下:

1. (*func)–该函数指针指向handlers所装载的服务模块的服务函数。

2. argc、**argv–分别为*func所指向的函数的参数个数和参数列表。

3. *next–指向堆栈模块中的下一个服务模块的服务函数。由此指针形成所有堆栈模块的服务函数链。

3.2 PAM接口库重要内部函数分析

PAM接口库中有一系列_pam开头的内部函数,那些APIs主要是调用这些内部函数来完成其功能的。

int _pam_add_handler(pam_handle_t *pamh, int must_fail, int other, int type, int *actions, const char *mod_path, int argc, char **argv, int argvlen)

该函数负责加载服务模块的SPI函数。这个函数代码很长有300多行,这里就不列举了。其主要步骤如下:

1. 根据mod_path提供的模块路径装载服务模块(dl_open)。

2. 由type确定的类型来决定要装入的SPI函数名并找到该函数(dlsym)的地址。type类型对应配置文件中的服务模块的四种类型标记,type的值及其对应的要装入的SPI函数名见下表。

模块类型 type SPI函数
认证管理模块 PAM_T_AUTH pam_sm_authenticate
pam_sm_setcred
会话管理模块 PAM_T_SESS pam_sm_open_session
pam_sm_close_session
帐号管理模块 PAM_T_ACCT pam_sm_acct_mgmt
口令管理模块 PAM_T_PASS pam_sm_chauthtok

3. 新分配一个handler结构,将dlsym找到的函数的地址赋给该handler结构的func域,并填充其他的结构信息。

4. 将新分配的handler结构插入到handlers的对应handler结构链表中。

_pam_parse_conf_file函数负责读并分析PAM配置文件,将相关的信息填充到pamh句柄中,并调用_pam_add_handlers加载服务模块的服务函数。

_pam_init_handlers函数主要做handler的初始化工作,先判断handler是否已初始化,若没有则调用_pam_parse_conf_file分析配置文件加载服务模块的服务函数。

_pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,……)

该函数负责遍历执行模块堆栈中的每一个服务模块对应的SPI函数,即执行h指向的handler结构链表中的每一个func指向的函数,并返回模块堆栈的结果值。

int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)

该函数首先通过调用_pam_init_handlers将模块调度请求转换为指向实际要运行的模块堆栈函数链表的指针,并将该指针传 递给_pam_dispatch_aux函数来遍历模块堆栈执行服务函数。该函数是实现六个认证API函数的主要部分和公共调用的函数,通过choice 选项来区分是哪个认证API函数。

下面分析我们最关心的部分,也即那些认证API是怎样找到和调度配置文件中配置的服务模块的?

3.3 PAM认证API的实现

为了更加清楚地说明PAM接口库是怎样来调度使用模块的,下面给出了pam_authenticate函数执行的流程图:

其他的认证API函数(pam_open_session等)执行过程前面五个步骤同上图,只是在最后一步时传递给_pam_dispatch_aux的指针参数不同,传递3.1节表中每个API函数相对应的那个handler型指针,然后执行相对应的SPI服务函数链。


回页首

4 小结

无论从PAM的应用开发还是它的实现原理来看,这个框架及其思想都是非常完美的,所以几乎各种版本的 UNIX 系统都提供对 PAM 的支持。本文通过对Linux-PAM 进行了深入仔细的分析,阐述了它的内部实现机制,并且讲述了怎样在应用程序中使用PAM和怎样开发PAM服务模块,希望能对大家做认证相关的开发工作有所 帮助。

参考资料

  1. [Vipin Samar, Charlie Lai, 1995]
    Making Login Services Independent of Authentication Technologies. Sun Technical report.
  2. [Andrew G. Morgan, 2001]
    The Linux-PAM Module Writers’ Guide. Linux-PAM Documentation.
  3. [Andrew G. Morgan, 2001]
    The Linux-PAM Module Writers’ Guide. Linux-PAM Documentation.

关于作者

程卫芳,国防科技大学计算机学院在读硕士,研究方向为安全操作系统。 Email: desertbow@sina.com

分类: BSD/linux, C/C++, 转载 标签: , ,

线程模式下工作

2010年11月8日 16hot 没有评论

这几天将memcached的线程模式移植到ednsd来,性能有所提升,但是没有以为中的高倍提升。下面是不连redis的统计。

Queries per second:   51600.484825 qps
Queries per second:   51664.028522 qps
Queries per second:   49371.465948 qps
Queries per second:   48488.016842 qps
Queries per second:   46953.006773 qps

Queries sent:         4485137 queries
Queries completed:    4485137 queries

经过测试数据表明,不是开的线程越多,性能越高,而是跟CPU(或者内核)数对应,性能最高。比如4核的CPU,4个线程,性能是最优的。另外,在10万个数据请求时,只有3.6万QPS。在440万个数据请求时,能上到5万QPS左右。

另一方面也说明了,线程模式在高负载的情况下,才能发挥性能。否则有点杀鸡用牛刀之嫌。

分类: C/C++, DNS 标签:

判断两个链表有无交点,如果有请给出交点

2010年11月4日 16hot 没有评论
  1. /*
  2. * if list_a and list_b has cross point return the addrss of cross-point.
  3. * else return NULL
  4. */
  5. static List *has_cross(List *list_a, List *list_b)
  6. {
  7. List    *pa;
  8. List    *pb;
  9. int     len_a, len_b;
  10. int     i;
  11. len_a = len_b = 0;
  12. pa = list_a; /* 遍历链表a,并记录下此链表的长度 */
  13. while (pa->next != NULL) {
  14. pa = pa->next;
  15. len_a++;
  16. }
  17. pb = list_b; /* 遍历链表b,并记录下链表b的长度 */
  18. while (pb->next != NULL) {
  19. pb = pb->next;
  20. len_b++;
  21. }
  22. if (pa != pb) /* 如果指针pa,pb最后不相等,则两链表没有焦点 */
  23. return NULL;
  24. /* 较长的链表先向后调整到第N个元素(N = max(len_a, len_b) - min(len_a, len_b))
  25. 然后两链表同步向后调整,同时比较地址是否相等,如果相等则为首焦点。*/
  26. pa = list_a;
  27. pb = list_b;
  28. if (len_a > len_b) {
  29. for (i = len_a - len_b; i; i–)
  30. pa = pa->next;
  31. } else {
  32. for (i = len_b - len_a; i; i–)
  33. pb = pb->next;
  34. }
  35. while (pa != pb) {
  36. pa = pa->next;
  37. pb = pb->next;
  38. }
  39. return pa;
  40. }
分类: C/C++, 转载 标签:

删除C程序中的注释

2010年11月4日 16hot 没有评论
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. int main(int argc, char **argv)
  5. {
  6. char start;
  7. char end;
  8. char cur;
  9. start = end = ’\0′;
  10. while ((cur = getchar()) != EOF) {
  11. if (cur == ’/' && start == ’/') {
  12. while ( (cur = getchar()) != EOF && cur != ’\n’) {
  13. start = cur;
  14. }
  15. start = ’\0′;
  16. } else if (start == ’/' && cur == ’*') {
  17. while ( (cur = getchar()) != EOF) {
  18. if (end == ’*' && cur == ’/') {
  19. start = end = cur = ’\0′;
  20. break;
  21. }
  22. end = cur;
  23. }
  24. }
  25. if (start != ’\0′)
  26. putchar(start);
  27. start = cur;
  28. }
  29. putchar(start);
  30. exit(EXIT_SUCCESS);
  31. }
分类: C/C++, 转载 标签:

学习管道pipe应用

2010年10月29日 16hot 没有评论

今天学习了pipe在进程和线程下的工作。记录一下。

int pfd[2];
if ( pipe(pfd) < 0 ) {
//出错
}

// pfd[0] 是读,pfd[1] 是写

在线程中使用管道。

void cb_func(evutil_socket_t fd, short what, void *arg)
{
const char *data = arg;

char buf_rr[ EDNSD_LOG_MAX_SIZE * 2 + 1 ];
if(read( fd, buf_rr, EDNSD_LOG_MAX_SIZE * 2 ) > 0 ) {
printf(“BUF_R: in child child process,read from the pipe is %s\n”,buf_rr);
}
}

static void
ednsd_log_server( int log_rfd )
{
struct event_base *event_base = NULL;
event_base = event_base_new();

struct event  *event;

event = event_new(event_base, log_rfd, EV_READ |EV_PERSIST, cb_func,
(char*)”Reading event”);

event_add(event, NULL);
event_base_dispatch(event_base);

}

void ednsd_log_initd()
{
int log_pfd[2];
pthread_t thread_id;
pthread_attr_t  attr;

/**
* 创建日志线程
*/

if ( pipe(log_pfd) < 0 ) {
syslog( LOG_CRIT, “pipe error” );
}

pthread_attr_init(&attr);
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );

pthread_create( &thread_id, &attr, ednsd_log_server, log_pfd[0] );

pthread_attr_destroy(&attr);
ednsd_config.log_fd = log_pfd[1];

}

分类: C/C++ 标签: , ,

管道(PIPE)

2010年10月28日 16hot 没有评论
进程通信(IPC)的几种方式及比较
撰文:黄显国080416
难得闲暇,抽空学习了一下进程通信的知识,现将这几天的所学做一下总结,以备遗忘时参考。
进程通信的方式:
Linux系统继承了三种系统的进程通信模式:
1、 基于system V IPC
2、 基于UNIX IPC
3、 基于POSIX IPC
同时还包含一种socket进程间通信,不过这种是不同处理器系统之间的一种网络通信方式,不是我所关心的。
方式一:管道(PIPE
管道分无名管道与有名管道两种
1、 无名管道。
无名管道用于具有亲缘关系的父子进程,子子进程之间的通讯。它的实现函数有
int pipe(int fd[2]);
//fd[2]为描述符数组,包含一个读描述符与一个写描述符,在使用管道通信时,关闭某些不需要的读或写描述符,建立起单向的读或写管道,然后用readwrite像操作文件一样去操作它即可。
如图便是进程1到进程2的一个读管道。
以下是我写的一个pipe的验证程序,分别在父进程和父子进程里向管道写数据,然后在子进程和子子进程里读数据,当尝试改变各子进程的sleep时间以实现渴望的同步时,会发现结果很有趣。注意创建子进程时将复制父进程的管道。
/*******************************************************************************************/
//pipe.c
//frome the example, we can see:
//pipe创建的无名管道,父子进程,子子进程之间都可以通信,由于read
//write默认为阻塞,而进程与进程之间又有某种意义上的同步方法;
//故而可以从下面的程序中得到一些启示。
/*****************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main()
{
int pipe_fd[2];
char buf_r[100];
char buf_rr[100];
if(pipe(pipe_fd)<0)
{
printf(“pipe create error\n”);
return -1;
}
else
printf(“pipe create success\n”);
printf(“pipe_fd[0]=%d,pipe_fd[1]=%d\n”,pipe_fd[0],pipe_fd[1]);
if(fork()==0)//子进程
{
close(pipe_fd[1]);//关闭子进程的写描述符
printf(“fork()”);
//sleep(2);
if(fork()==0)//子子进程
{
if(read(pipe_fd[0],buf_r,5)>0)
printf(“BUF_R: in child child process,read from the pipe is %s\n”,buf_r);
close(pipe_fd[0]);
exit(0);
}
Else //子进程
{
if(read(pipe_fd[0],buf_rr,3)>0)
printf(“BUF_RR: in child parent process,read from the pipe is %s\n”,buf_rr);
close(pipe_fd[0]);
exit(0);
}
}
else  //父进程
{
close(pipe_fd[0]);
sleep(5);
if(write(pipe_fd[1],”Hello “,5)!=-1)
printf(“write1 parent pipe success\n”);
if(fork()==0)  //父子进程
{
if(write(pipe_fd[1],”PIPE”,5)!=-1)
printf(“write parent child pipe success”);
close(pipe_fd[1]);
exit(0);
}
close(pipe_fd[1]);
exit(0);
}
}
2、 有名管道
有名管道可用于两个无关的进程之间的通信。它的实现函数是:
int mkfifo(const char *filename, mode_t mode)
//创建一个名为filename的管道,模式可选为读或写方式,阻塞或非阻塞方式等。
下面一个实例演示了mkfifo的使用。Fifo_read.c不断从管道文件里读数据,fifo_write.c往管道文件里写数据。改变sleep的值也会产生类似上面进程同步的问题,而会发现一些缓冲区的特性。
两个程序用gcc编译后在两个终端里运行。
//—————————————————————————————————
//fifo_read.c
//创建有名管道,演示两个不相关的进程之间的通信
//int mkfifo(const char *filename, mode_t mode)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#define FIFO “/home/huang/myfifo”
int main(int argc,char **argv)
{
int fd;
int nread;
char buf_r[100];
if(mkfifo(“/home/huang/myfifo”,O_CREAT|O_EXCL)<0)//&&(errno!=EEXIST))
{
perror(“mkfifo:”);
printf(“cann’t create fifoserver\n”);
return -1;
}
printf(“Preparing for reading bytes…\n”);
fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);
if(fd==-1)
{
perror(“open!\n”);
exit(1);
}
while(1)
{
memset(buf_r,0,sizeof(buf_r));
if((nread=read(fd,buf_r,sizeof(buf_r)))==-1)
{
if(errno==EAGAIN)
printf(“no data yet\n”);
}
printf(“read %s from FIFO\n”,buf_r);
sleep(1);
}
pause();
unlink(FIFO);
}
//——————————————————————————————————————
//fifo_write.c
//创建有名管道,演示两个不相关的进程之间的通信
//int mkfifo(const char *filename, mode_t mode)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#define FIFO “/home/huang/myfifo”
int main(int argc, char **argv)
{
int fd;
char w_buf[100];
int nwrite;
if(argc==1)
{
printf(“please send some message\n”);
exit(1);
}
fd=open(FIFO,O_WRONLY|O_NONBLOCK,0);
if(fd==-1)
{
if(errno==ENXIO)
printf(“open error;no reading process\n”);
perror(“open:”);
return -1;
}
memset(w_buf,’a',sizeof(w_buf));
printf(“sizeof(w_buf)=%d\n”,sizeof(w_buf));
while(1)
{
if((nwrite=write(fd,w_buf,strlen(w_buf)))==-1)
{
if(errno==EAGAIN)
printf(“The FIFO has not been write yet.\n”);
perror(“write”);
//     else
//     printf(“error in writting!\n”);
}
else
printf(“write %s to the FIFO\n”,w_buf);
sleep(2);
}
close(fd);
}
分类: BSD/linux, C/C++, 转载 标签: ,