公 告

欢迎各位网友添加友情链接,在您添加本博客:http://linux0818.blogspot.com/ 做为链接后, E-mail:linux0818@gmail.com给我,我将将您的网址添加到本博客。

2009年3月25日星期三

OpenSource之进程编程中僵尸进程的产生及防范

今天在做一个监视程序:创建一个进程,在父进程中死循环的监视着多个事件,而在子进程中只是打开一个输入、输出、错误的句柄后就退出了。呵呵,程序运行后,用ps -aux查看,居然出现好几个僵尸进程。后来打开程序一看,竟然发现子进程退出后,父进程没有给进程收尸。我们再看看下面这段话,会让我们更好的理解僵尸进程的产生及防范:

在fork()/execve()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD 信号处理函数调用waitpid()等待子进程结束,又没有显式忽略该信号,则子进程成为僵尸进程,无法正常结束,此时即使是root身份kill-9也 不能杀死僵尸进程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然存在),僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负 责清理僵尸进程。

怎样来清除僵尸进程:
1.改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。
2.把父进程杀掉。父进程死后,僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
3.调用fork两次。

从上面两段话,我们基本上能明白了僵尸进程的产生及消除了,下面我们来模拟一下僵尸进程的产生:
代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
if (0 == fork()) {
printf("In the child process:%d\n", getpid());
} else {
printf("In the parent process:%d\n", getpid());
sleep(10);
exit(0);
}
printf("Pid:%d\n", getpid());
return 0;
}

运行结果:
In the child process:3682
Pid:3682
In the parent process:3681

在程序没有结束之前,我们可以开另一个终端运行ps -aux命令查看,我们可以发现在该程序的旁边还会产生一个其STAT为Z+的僵尸进程,这就是在父进程没有结束之前,子进程已经结束了,且父进程没有回收子进程就会产生僵尸进程了。
下面我们看看父进程怎么接收子进程,即在上面的情况下怎么消除僵尸进程:
代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include<sys/wait.h>

void proc_child(int SIGNO)
{
int pid = -1;
int stat;
pid = waitpid(-1, &stat, WNOHANG);
}

int main(int argc, char **argv)
{
signal(SIGCHLD, proc_child);
if (0 == fork()) {
printf("In the child process:%d\n", getpid());
} else {
printf("In the parent process:%d\n", getpid());
sleep(10);
exit(0);
}
printf("Pid:%d\n", getpid());
return 0;
}

运行结果:
In the child process:3682
Pid:3682
In the parent process:3681

从上面的代码中看出就前一份代码多了一个signal()函数,signal()调用proc_child()函数就是为了等待子进程结束后发送SIGCHLD给父进程,并接收。这采用的是第1种方法。当然,还有这种方法:
代码如下:
int main(int argc, char **argv)
{
signal(SIGCHLD, SIG_IGN); //加入此句即可,忽略SIGCHLD信号
if (0 == fork()) {
printf("In the child process:%d\n", getpid());
} else {
printf("In the parent process:%d\n", getpid());
sleep(10);
exit(0);
}
printf("Pid:%d\n", getpid());
return 0;
}

网上有网友做过测试,有下面的说法:
在比较繁忙的应用中使用WAIT也无法解决僵尸进程。以前有经历在涉及数据库的程序中,当数据库异常时会出现不正常的僵尸进程,而程序中实际上已经使用了wait命令,并且捕捉了SIGCLD信号。系统很忙时估计SIGCLD信号传递不正常。



--
/**************************************/
Name: Xiong Feng
E-mail:linux0818@gmail.com
MSN:linux0818@hotmail.com
QQ:23562033
Address: GuangZhou.China
/**************************************/

2009年3月24日星期二

OpenSource之在C语言中获得调用系统命令后输出的参数

众所周知,在C语言中调用system()函数是不能够得到shell命令的输出的,但有时在C语言中需要得到系统的命令的输出信息,该怎么办?别急,我们来看看popen()函数,这个函数可以让我们得到想要的信息。

#include <stdio.h>

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

描述

popen() 函数 用 创建管道 的 方式 启动 一个 进程, 并调用 shell. 因为 管道 是被定义成 单向的, 所以 type 参数 只能 定义成 只读 或者 只写, 不能是 两者同时, 结果流 也相应的 是 只读 或者 只写.

command 参数 是 一个 字符串指针, 指向的 是 一个 以 null 结束符 结尾的字符串, 这个 字符串 包含 一个 shell 命令. 这个命令 被送到 /bin/sh-c 参数 执行, 即由 shell 来执行. type 参数 也是 一个 指向 以 null 结束符 结尾的 字符串的指针, 这个字符串 必须是 'r' 或者 'w’ 来指明 是 读还是写.

popen() 函数 的 返回值 是 一个 普通的 标准I/O流, 它只能用 pclose() 函数 来关闭, 而不是 fclose(). 函数. 向 这个流 的 写入 被转化为 对 command 命令的 标准输入; 而 command 命令的 标准输出 则是和 调用 popen(), 函数 的 进程 相同,除非 这个 被command命令 自己 改变. 相反的, 读取 一个 “被popen了的” 流, 就相当于 读取 command 命令的 标准输出, 而 command 的 标准输入 则是和 调用 popen, 函数的 进程 相同.

注意, popen 函数的 输出流 默认是 被全缓冲的.

pclose 函数 等待 相关的进程 结束 并返回 一个 command 命令的 退出状态, 就像 wait4 函数 一样


举例如下:

#include <stdio.h>

void command_mess(char *command, char *buf, int length)
{
    FILE   *stream;

    stream = popen(command,"r");
    fread( buf, sizeof(char), length,  stream);
    pclose( stream );
    return;
}

int main( void )
{
    char buf[4096] = "";  //请注意系统命令要输出的数据大小,小心得到的数据不全哦
    char com[64] = "ls -l";
    command_mess(com, buf, sizeof(buf));
    printf("Message:\n%s",buf);
    return 0;
}

运行结果:

-------------------------------------------------------------------------------

Message:
总计 616
-rw-r--r-- 1 root root    186 12-05 16:14 advertise
-rw-r--r-- 1 root root   1432 11-28 00:39 agent.c
-rwxr-xr-x 1 root root   9593 03-24 18:21 a.out
-rw-r--r-- 1 root root    307 02-06 14:32 array.c
-rw-r--r-- 1 root root    120 11-16 00:01 asi.c
-rw-r--r-- 1 root root    675 12-01 12:11 compareagent.c
-rw-r--r-- 1 root root    570 01-18 13:21 ethfile
-rw-r--r-- 1 root root   1223 03-23 17:33 getmac.c
-rw-r--r-- 1 root root   1200 03-23 17:32 getmac.c~
-rw-r--r-- 1 root root    203 11-14 00:24 ifelse.c
-rw-r--r-- 1 root root     46 03-23 18:38 info
-rw-r--r-- 1 root root   1856 01-17 17:07 mac.c
-rw-r--r-- 1 root root   3587 01-17 16:07 mac.c~
-rw-r--r-- 1 root root   1049 03-16 11:53 meminfo.c
-rw-r--r-- 1 root root    830 03-16 09:44 meminfo.c~
drwxr-xr-x 2 root root   4096 01-05 15:53 mingetty-1.07.orig
-rw-r--r-- 1 root root   2277 01-17 16:37 mymac.c
-rw-r--r-- 1 root root    995 01-18 17:05 netaddress.c
-rw-r--r-- 1 root root    624 03-23 17:08 netcardcount.c
-rw-r--r-- 1 root root    939 03-23 17:05 netcardcount.c~
-rw-r--r-- 1 root root   1291 02-13 17:27 newt.c
-rw-r--r-- 1 root root   1152 12-05 17:09 parse.c
-rw-r--r-- 1 root root   1306 12-03 12:07 parse.c~
-rw-r--r-- 1 root root   2774 01-18 13:27 parseeth.c
-rw-r--r-- 1 root root   1649 01-09 16:17 parseeth.c~
-rw-r--r-- 1 root root    361 03-24 18:21 popen1.c
-rw-r--r-- 1 root root    880 03-24 17:37 popen.c
-rw-r--r-- 1 root root   1942 12-18 19:03 regular_expression.c
-rw-r--r-- 1 root root   1357 12-18 18:23 regular_expression.c~
-rw-r--r-- 1 root root    765 12-17 16:06 select.c
-rw-r--r-- 1 root root    596 12-17 16:02 select.c~
-rw-r--r-- 1 root root 189715 02-13 17:21 slang-1.4.5-2.i386.rpm
-rw-r--r-- 1 root root    168 02-05 17:49 sprintf.c
-rw-r--r-- 1 root root   2033 12-05 16:15 squid.conf
-rw-r--r-- 1 root root    169 01-17 14:10 system.c
-rw-r--r-- 1 root root   8694 03-20 17:09 test
-rw-r--r-- 1 root root    100 12-31 18:07 test.c
-rwxr-xr-x 1 root root 206454 03-21 11:08 top
-rw-r--r-- 1 root root   2101 03-20 15:34 top.c
-rw-r--r-- 1 root root   3458 03-20 15:34 top.c~
-rw-r--r-- 1 root root   1552 01-05 16:39 uname.c
-rw-r--r-- 1 root root   1289 01-05 16:12 uname.c~
-rw-r--r-- 1 root root   4174 03-23 18:36 write.c
-rw-r--r-- 1 root root   4109 03-23 18:34 write.c~
-rw-r--r-- 1 root root   1886 01-14 20:24 x.c
-rw-r--r-- 1 root root   1886 01-14 20:24 x.c~
-rwxr-xr-x 1 root root     37 12-31 18:07 xx.sh
-rw-r--r-- 1 root root   1817 01-14 20:24 y.c
-rw-r--r-- 1 root root   1772 01-14 20:22 y.c~
-rw-r--r-- 1 root root    141 03-24 18:19 z.c

-------------------------------------------------------------------------------

很简单的一个函数就可以实现我们想要的功能了,呵呵!

--

/**************************************/
Name: Xiong Feng
E-mail:linux0818@gmail.com
MSN:linux0818@hotmail.com
QQ:23562033
Address: GuangZhou.China
/**************************************/

OpenSource之获取某进程内存比,CPU比等

我下面实现的代码主要是对系统中squid进程监控的一个函数,这个函数主要实现的功能就是取得squid实时占系统内存的比率,并与函数传递进来的maxmem比较,如果小于它就返回0,反之,返回1。其中取得该进程的内存比主要是通过top或者ps等系统命令来实现的。

代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//get squid memory scale
int squid_mem_info(int maxmem)
{
    FILE   *stream;
    char   buf[1024] = "";
    char   mem[32] = "";
    float  float_mem;
    float  float_maxmem;

    float_maxmem = (float)maxmem;
    memset( buf, '\0', sizeof(buf) );
    //stream = popen("./top -d 1 -o cpu |grep -r \"squid\" |awk '{print $9}'", "r" );
    stream = popen("ps acux|grep squid |grep -v \"0.00\" |awk '{print $4}'","r");  //通过ps取得squid占系统的内存比,上面注释的也可以,只需调整一下。
    fread( buf, sizeof(char), sizeof(buf),  stream);
    if(strlen(buf)==0){  //when squid is not run.
        float_mem = 0.1;
    }else{
        float_mem = atof(mem);
    }

    if(float_mem > float_maxmem){
        printf("float_mem > float_maxmem\n");
        return 1;
    }else{
        printf("float_mem <= float_maxmem\n");
        return 0;
    }

    pclose( stream );
}

int main( void )
{
    squid_mem_info(10);
    return 0;
}

运行结果:
float_mem <= float_maxmem

这段代码主要是告诉我们如何通过ps,top等命令取得某一进程系统的内存,CPU等信息。
下面一段代码告诉我们如何通过读取文件得到系统的一些信息,如系统总的内存,空闲内存,交换分区大小等等。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int mem_scale(char *filename)
{
    char total[64]="";
    char free[64]="";
    char temp[64]="";
    char MemTotal[64]="";
    char MemFree[64]="";
    FILE *fp;
    int i, j;
    float k;

    if ((fp = fopen(filename, "r")) == NULL) {
        printf("cannot open outfile\n");
        exit(-1);
    }
    for (i = 0; i < 5; i++) {
        fgets(temp, 80, fp);
        if (i == 0) {
            strcpy(total, temp);
        }
        if (i == 1) {
            strcpy(free, temp);
        }
    }
    fclose(fp);

    for (i = 0, j = 0; i < strlen(total); i++) {
        if (isdigit(total[i])) {
            MemTotal[j++] = total[i];
        }
    }

    for (i = 0, j = 0; i < strlen(free); i++) {
        if (isdigit(free[i])) {
            MemFree[j++] = free[i];
        }
    }
    printf("memtotal:%ld M\n",atol(MemTotal)/1024);
    printf("memfree:%ld M\n",atol(MemFree)/1024);
    k = atof(MemFree)/atof(MemTotal);
    printf("k:%f\n",k);
    if(k>0.4)
        return 0;
    else
        return 1;
}

int main(int argc,char **argv)
{
    char infile[15] = "/proc/meminfo";
    mem_scale(infile);

    return 0;

}

运行结果:
memtotal:3290 M
memfree:3146 M
k:0.956202




--
/**************************************/
Name: Xiong Feng
E-mail:linux0818@gmail.com
MSN:linux0818@hotmail.com
QQ:23562033
Address: GuangZhou.China
/**************************************/

2009年3月23日星期一

OpenSource之Linux文件格式的读,写,查找等一系列函数

源代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STRING_SIZE 1024
#define FILENAME "/root/fxiong/info"

struct keyvalue {
char key[STRING_SIZE];
char value[STRING_SIZE];
struct keyvalue *next;
};


/* Sets up the list. First entry is a dummy one to avoid having to special
* * case empty lists. */
struct keyvalue *initkeyvalues(void)
{
struct keyvalue *head = malloc(sizeof(struct keyvalue));

strcpy(head->key, "KEY");
strcpy(head->value, "VALUE");
head->next = NULL;

return head;
}

/* Splats all the entries in a list. */
void freekeyvalues(struct keyvalue *head)
{
struct keyvalue *cur = head->next;
struct keyvalue *next;

while (cur) {
next = cur->next;
free(cur);
cur = next;
}
}


/*
* Appends a entry. Not very efficent because it rescans the list looking
* for the end. Maybe fix this later.
*/
void appendkeyvalue(struct keyvalue *head, char *key, char *value)
{
struct keyvalue *new = malloc(sizeof(struct keyvalue));
struct keyvalue *cur = head->next;
struct keyvalue *tail = head;

strncpy(new->key, key, STRING_SIZE);
strncpy(new->value, value, STRING_SIZE);
new->next = NULL;

while (cur) {
tail = cur;
cur = cur->next;
}
tail->next = new;
}

/* Reads from a file into a new list. Uses appendkeyvalue to add entries.
* * Will bomb out on a error (eg bad format line). */
int readkeyvalues(struct keyvalue *head, char *filename)
{
FILE *file;
char buffer[STRING_SIZE];
char *temp;
char *key, *value;

if (!(file = fopen(filename, "r")))
return 0;

while (fgets(buffer, STRING_SIZE, file)) {
temp = buffer;
while (*temp) {
if (*temp == '\n')
*temp = '\0';
temp++;
}
if (!strlen(buffer))
continue;
if (!(temp = strchr(buffer, '='))) {
fclose(file);
return 0;
}
*temp = '\0';
key = buffer;
value = temp + 1;
/* See if string is quoted. If so, skkip first quote, and
* * nuke the one at the end. */
if (value[0] == '\'') {
value++;
if ((temp = strrchr(value, '\'')))
*temp = '\0';
else {
fclose(file);
return 0;
}
}
if (strlen(key))
appendkeyvalue(head, key, value);
}

fclose(file);

return 1;
}

/* Writes out a list to a file. Easy. */
int writekeyvalues(struct keyvalue *head, char *filename)
{
FILE *file;
struct keyvalue *cur = head->next;

if (!(file = fopen(filename, "w")))
return 0;

while (cur) {
/* No space in value? If there is, we need to quote the value
* * * so the shell can read it. */
if (!strchr(cur->value, ' '))
fprintf(file, "%s=%s\n", cur->key, cur->value);
else
fprintf(file, "%s=\'%s\'\n", cur->key, cur->value);
cur = cur->next;
}

fclose(file);

return 1;
}


/* Finds a key and copies the value back. Would be nice to have a func that
* * just returns a pointer to the value? */
int findkey(struct keyvalue *head, char *key, char *value)
{
struct keyvalue *cur = head->next;

while (cur) {
if (strcmp(key, cur->key) == 0) {
strcpy(value, cur->value);
return 1;
}
cur = cur->next;
}

return 0;
}



/* Otherwrites a key with a new value, or if it dosn't exist, appends it
* * on the end. */
void replacekeyvalue(struct keyvalue *head, char *key, char *value)
{
struct keyvalue *cur = head->next;

while (cur) {
if (strcmp(cur->key, key) == 0) {
strncpy(cur->value, value, STRING_SIZE);
return;
}
cur = cur->next;
}

appendkeyvalue(head, key, value);
}

int main(int argc, char **argv)
{
char value[64] = "";
int ret = 0;
struct keyvalue *kv = initkeyvalues();
struct keyvalue *rkv = initkeyvalues();

replacekeyvalue(kv, "Name", "XiongFeng");
replacekeyvalue(kv, "Sex", "Man");
replacekeyvalue(kv, "Age", "25");
replacekeyvalue(kv, "Phone", "123456789");

writekeyvalues(kv, FILENAME);

readkeyvalues(rkv, FILENAME);

if (findkey(rkv, "Sex", value))
printf("Found the value:%s\n", value);
else
printf("Not found the value!\n");

freekeyvalues(kv);
freekeyvalues(rkv);


return 0;
}

运行结果:
Found the value:Man

该程序运行会在/root/fxiong/下创建一个info文件,将数据按照一定格式写入该文件。
程序简单,实用,不错吧 : )
--
/**************************************/
Name: Xiong Feng
E-mail:linux0818@gmail.com
MSN:linux0818@hotmail.com
QQ:23562033
Address: GuangZhou.China
/**************************************/

OpenSource之获取Linux Mac地址

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <linux/if.h>
#include <sys/ioctl.h>

int getnicmac(char *dest, int size, char *dev)
{
int fd = 0;
int found = 0;
struct ifreq req;

strcpy(dest, "");

/* Create socket to do ioctl. */
if ((fd = socket(PF_INET, SOCK_DGRAM, 0))) {
/* Set ifrn_name to device name eg. "eth0" */
snprintf(req.ifr_ifrn.ifrn_name, IFNAMSIZ, dev);

/* Do ioctl to get hardware address (MAC) */
if (!(ioctl(fd, SIOCGIFHWADDR, &req))) {
/* Format MAC into colon seperated string format. */
snprintf(dest, size, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
req.ifr_ifru.ifru_hwaddr.sa_data[0] & 0xff,
req.ifr_ifru.ifru_hwaddr.sa_data[1] & 0xff,
req.ifr_ifru.ifru_hwaddr.sa_data[2] & 0xff,
req.ifr_ifru.ifru_hwaddr.sa_data[3] & 0xff,
req.ifr_ifru.ifru_hwaddr.sa_data[4] & 0xff,
req.ifr_ifru.ifru_hwaddr.sa_data[5] & 0xff);
found = 1;
}
}

if (fd)
close(fd);

return (found);
}

int main(int argc, char **argv)
{
char mac[32] = "";
int ret = 0;
ret = getnicmac(mac, 512, "eth0");
printf("ret:%d\n", ret);
printf("Mac:%s\n", mac);

return 0;
}
运行结果:
ret:1
Mac:00:18:8B:2F:1D:A4


--
/**************************************/
Name: Xiong Feng
E-mail:linux0818@gmail.com
MSN:linux0818@hotmail.com
QQ:23562033
Address: GuangZhou.China
/**************************************/

OpenSource之如何获取Linux系统的网卡数目

其实只是对/proc/net/dev设备的读取并解析,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int countcards(void)
{
FILE *file;
char buffer[512];
char *start;
int niccount = 0;

if (!(file = fopen("/proc/net/dev", "r"))) {
printf("Unable to open /proc/net/dev in countcards()\n");
return 0;
}

while (fgets(buffer, 512, file)) {
start = buffer;
while (*start == ' ')
start++;
if (strncmp(start, "eth", strlen("eth")) == 0)
niccount++;
}

fclose(file);

return niccount;
}



int main(int argc, char **argv)
{
int count = 0;

count = countcards();
printf("Net Cards Count:%d\n",count);


return 0;
}
运行结果:
Net Cards Count:2



--
/**************************************/
Name: Xiong Feng
E-mail:linux0818@gmail.com
MSN:linux0818@hotmail.com
QQ:23562033
Address: GuangZhou.China
/**************************************/