Chinaunix首页 | 论坛 | 博客
  • 博客访问: 831714
  • 博文数量: 137
  • 博客积分: 3477
  • 博客等级: 中校
  • 技术积分: 1409
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-30 21:53
文章分类

全部博文(137)

文章存档

2024年(8)

2023年(10)

2022年(4)

2021年(10)

2020年(9)

2018年(1)

2017年(1)

2014年(4)

2013年(3)

2012年(12)

2011年(24)

2010年(2)

2009年(8)

2008年(6)

2007年(34)

2006年(1)

分类: C/C++

2011-09-21 00:44:58

程序实现思想:
Linux 系统的进程号是保存在 /proc 目录下的,一个在 linux 系统上运行的程序,只要是在运行状态下,就必然会在 /proc 目录下写入一批文件,其文件写入的方式如下:

/proc/<进程ID>/文件组

这里面的文件组是指一批文件,不是指一个文件,大致的文件名信息如下:
  1. dr-xr-xr-x 2 work work 0 09-21 00:51 attr
  2. -r-------- 1 work work 0 09-21 00:51 auxv
  3. -r--r--r-- 1 work work 0 09-21 00:51 cmdline
  4. -rw-r--r-- 1 work work 0 09-21 00:51 coredump_filter
  5. -r--r--r-- 1 work work 0 09-21 00:51 cpuset
  6. lrwxrwxrwx 1 work work 0 09-21 00:51 cwd -> /home/work/tmp
  7. -r-------- 1 work work 0 09-21 00:51 environ
  8. lrwxrwxrwx 1 work work 0 09-21 00:51 exe -> /home/work/tmp/tt
  9. dr-x------ 2 work work 0 09-21 00:51 fd
  10. dr-x------ 2 work work 0 09-21 00:51 fdinfo
  11. -r-------- 1 work work 0 09-21 00:51 io
  12. -r--r--r-- 1 work work 0 09-21 00:51 limits
  13. -rw-r--r-- 1 work work 0 09-21 00:51 loginuid
  14. -r--r--r-- 1 work work 0 09-21 00:51 maps
  15. -rw------- 1 work work 0 09-21 00:51 mem
  16. -r--r--r-- 1 work work 0 09-21 00:51 mounts
  17. -r-------- 1 work work 0 09-21 00:51 mountstats
  18. -r--r--r-- 1 work work 0 09-21 00:51 numa_maps
  19. -rw-r--r-- 1 work work 0 09-21 00:51 oom_adj
  20. -r--r--r-- 1 work work 0 09-21 00:51 oom_score
  21. lrwxrwxrwx 1 work work 0 09-21 00:51 root -> /
  22. -r--r--r-- 1 work work 0 09-21 00:51 schedstat
  23. -r--r--r-- 1 work work 0 09-21 00:51 smaps
  24. -r--r--r-- 1 work work 0 09-21 00:51 stat
  25. -r--r--r-- 1 work work 0 09-21 00:51 statm
  26. -r--r--r-- 1 work work 0 09-21 00:51 status
  27. dr-xr-xr-x 3 work work 0 09-21 00:51 task
  28. -r--r--r-- 1 work work 0 09-21 00:51 wchan
比如,我们运行了一个程序 tt ,假设其进程号为 31673 ,则在 /proc 目录下会形成 /proc/31673/ 的目录,且在其目录下存在上述的文件组。这个里面,我们需要的文件为 status ,该文件保存的是 tt 当前运行得状态,我们来看看这个文件的内容:

  1. Name: tt
  2. State: S (sleeping)
  3. SleepAVG: 98%
  4. Tgid: 31673
  5. Pid: 31673
  6. PPid: 7977
  7. TracerPid: 0
  8. Uid: 500 500 500 500
  9. Gid: 500 500 500 500
  10. FDSize: 256
  11. Groups: 500
  12. VmPeak: 3828 kB
  13. VmSize: 3664 kB
  14. VmLck: 0 kB
  15. VmHWM: 300 kB
  16. VmRSS: 300 kB
  17. VmData: 44 kB
  18. VmStk: 88 kB
  19. VmExe: 4 kB
  20. VmLib: 1448 kB
  21. VmPTE: 32 kB
  22. StaBrk: 09e04000 kB
  23. Brk: 09e04000 kB
  24. StaStk: 7ffff0e895d0 kB
  25. Threads: 1
  26. SigQ: 0/8192
  27. SigPnd: 0000000000000000
  28. ShdPnd: 0000000000000000
  29. SigBlk: 0000000000000000
  30. SigIgn: 0000000000000000
  31. SigCgt: 0000000000000000
  32. CapInh: 0000000000000000
  33. CapPrm: 0000000000000000
  34. CapEff: 0000000000000000
  35. Cpus_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,ffffffff
  36. Mems_allowed: 00000000,00000001
我们发现,这个文件的第一行就是文件名,有了这个东西,那我们就有了获取当前正在运行的特定程序的进程号的办法了。
基本的解决思路是——我们通过遍历 /proc 目录下所有用数字作为目录名的子目录,依次打开这些目录下的 status 文件,获取其中的第一行内容,并从中获取对应的 Name 节的信息,与我们传入的需要查询的程序名字进行比较,如果一致,则该目录的数字就是该程序的进程号。

  1. /* find_pid_by_name()
  2.  *
  3.  * This finds the pid of the specified process.
  4.  * Currently, it's implemented by rummaging through
  5.  * the proc filesystem.
  6.  * Returns a list of all matching PIDs
  7.  */

  8. #include <unistd.h>
  9. #include <sys/types.h>
  10. #include <sys/stat.h>
  11. #include <unistd.h>
  12. #include <dirent.h>
  13. #include <string.h>
  14. #include <stdlib.h>
  15. #include <stdio.h>

  16. #define READ_BUF_SIZE 1024


  17. long* find_pid_by_name( char* pidName)
  18. {
  19.         DIR *dir;
  20.         struct dirent *next;
  21.         long* pidList=NULL;
  22.         int i=0;


  23.         dir = opendir("/proc");
  24.         if (!dir)
  25.         {
  26.                 //perror_msg_and_die("Cannot open /proc");
  27.                 fprintf(stderr, "Cannot open /proc\n");
  28.                 return pidList;
  29.         }

  30.         while ((next = readdir(dir)) != NULL)
  31.         {
  32.                 FILE *status;
  33.                 char filename[READ_BUF_SIZE];
  34.                 char buffer[READ_BUF_SIZE];
  35.                 char name[READ_BUF_SIZE];

  36.                 /* Must skip ".." since that is outside /proc */
  37.                 if (strcmp(next->d_name, "..") == 0)
  38.                         continue;

  39.                 /* If it isn't a number, we don't want it */
  40.                 if (!isdigit(*next->d_name))
  41.                         continue;
  42.                 sprintf(filename, "/proc/%s/status", next->d_name);

  43.                 if (! (status = fopen(filename, "r")) )
  44.                 {
  45.                         continue;
  46.                 }

  47.                 //Read first line in /proc/?pid?/status

  48.                 if (fgets(buffer, READ_BUF_SIZE-1, status) == NULL)
  49.                 {
  50.                         fclose(status);
  51.                         continue;
  52.                 }

  53.                 fclose(status);

  54.                 // Buffer should contain a string like "Name: binary_name"a

  55.                 sscanf(buffer, "%*s %s", name);

  56.                 if ( name != NULL && name[0] != '\0')
  57.                 {
  58.                         if (strcmp(name, pidName) == 0)
  59.                         {
  60.                                 pidList=realloc( pidList, sizeof(long) * (i+2));
  61.                                 pidList[i++]=strtol(next->d_name, NULL, 0);
  62.                         }

  63.                 }

  64.         }

  65.         if (pidList)
  66.         {
  67.                 pidList[i]=0;
  68.         }
  69.         return pidList;
  70. }



  71. int main ( int argc , char **argv)
  72. {
  73.         long *pid_t=NULL;
  74.         if ( argc != 2 )
  75.         {
  76.                 fprintf(stdout,"Usage %s \n",argv[0]);
  77.                 return 0;
  78.         }

  79.         pid_t = find_pid_by_name( argv[1]);

  80.         while ( pid_t != 0 && *pid_t != 0)
  81.         {
  82.                 fprintf(stdout,"\n%s is [ %d]\n",argv[1],*pid_t);
  83.                 pid_t++;
  84.         }
  85.         return 0;
  86. }

这段程序中特别需要注意的是:

main 函数中的 while(pid_t != 0 && *pid_t != 0) 这一行,开始的时候俺没有注意,一直报段错误,仔细观察后发现,不仅需要判断这个指针的值是否是等于 0 , 同时还得判断这个指针本身是否等于 0 ,否则会出现查询的程序根本就没有运行时出现段错误,或者是查询的程序运行了,也会出现段错误的情况。

出现段错误是由于 pid_t 指针的值是空值,传给 fprintf 函数时会出现段错误。

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

此程序在需要限制某些程序在特定的时间内只能一次运行的情况下很有好处,这种情况多数出现在金融系统、保险系统、收费系统等核心业务领域,通常在日终账务处理时,绝对不允许在同一个时间内连续两次运行扎帐程序,否则会造成业务数据紊乱。

因此,我们可以在程序运行得时候,首先获取自身的进程号 pid (用 getpid()函数获取),然后通过上述的程序代码,获取当前程序的进程号,如果存在两个,则退出当前的程序,从而保证在一个特定的时间内,扎帐程序只有一个运行。

阅读(8427) | 评论(18) | 转发(12) |
给主人留下些什么吧!~~

snow8882011-10-19 19:24:42

sunlan: pid文件本身什么都不是,但在这个文件里可以写daemon进程的进程号啊!你可以去通过pid文件中保存的进程号判断daemon进程的状态啊。
虽然存在daemon挂掉后同一进.....
老大有心了。
pid文件本身什么都不是,但在这个文件里可以写daemon进程的进程号啊!你可以去通过pid文件中保存的进程号判断daemon进程的状态啊。
虽然存在daemon挂掉后同一进程号被其他进程占用的情况,但你可以再加一步操作,用ps+管道去检查一下指定进程的名称。而且你可以评估一下这种情况发生的概率有多大!

除非是用于航天飞机等环境的极端要求,否则花那么大力气去解决一个无关紧要的问题完全犯不着。搞开发切忌钻牛角尖!
其实俺想说的情况是,即使 pid 文件存在,那么也不能代表将要运行的程序是否已经运行了,因为程序有可能被 kil

sunlan2011-10-19 15:43:53

snow888: 俺想说的正是这个问题啊,即使 pid 文件存在也不能代表程序是否在运行,因此依靠 pid 文件来判定当前需要运行的程序是否已经运行是不可靠的。所以俺才想到了这个.....
pid文件本身什么都不是,但在这个文件里可以写daemon进程的进程号啊!你可以去通过pid文件中保存的进程号判断daemon进程的状态啊。
虽然存在daemon挂掉后同一进程号被其他进程占用的情况,但你可以再加一步操作,用ps+管道去检查一下指定进程的名称。而且你可以评估一下这种情况发生的概率有多大!

除非是用于航天飞机等环境的极端要求,否则花那么大力气去解决一个无关紧要的问题完全犯不着。搞开发切忌钻牛角尖!

snow8882011-10-19 12:05:26

sunlan: 这个问题根本不是问题。即使pid文件存在,不说明守护程序还在运行。最简单的解决办法是把pid文件里的进程号读出来,然后通过kill()判断一下这个进程是否在运行。.....
俺想说的正是这个问题啊,即使 pid 文件存在也不能代表程序是否在运行,因此依靠 pid 文件来判定当前需要运行的程序是否已经运行是不可靠的。所以俺才想到了这个办法来解决这个问题,而这个问题也是目前俺想到的最可靠的判断进程是否已经运行的最好办法。

因为不管是异常退出还是被 kill 掉,操作系统都会把这个进程的进程号删除,也就是说,在需要唯一运行的情况下,这个方法应该是最为可靠的方法。

老大最近可好 。。。 ^_^.  好久不见了,怪想兄弟们的。

sunlan2011-10-19 10:36:27

snow888: 俺说了,俺过去也是用的 pid 文件,即将当前程序的 pid 写入到一个文件中,但这个里面有一个问题,如果程序因为某些原因而异常退出的时候,这个 pid 文件是会保.....
这个问题根本不是问题。即使pid文件存在,不说明守护程序还在运行。最简单的解决办法是把pid文件里的进程号读出来,然后通过kill()判断一下这个进程是否在运行。如果该进程仍在运行,则当前进程退出;否则当前进程开始运行,并用自己的pid覆盖pid文件中的原pid号。
唯一可能存在的问题是前一守护进程异常退出过久而未被即时发现、pid值经过一轮循环后刚好被另一进程占用,这种情况就需要手工处理了。不过从概率上来说,这种情况的发生概率极小

snow8882011-10-16 00:12:50

James_think: pid文件加锁也没问题啊!那个pid文件就当配置文件用,不需要删除!
进程进取后先去获得文件锁,已经锁了就退出!已经有一个进程运行了!
而且你的这个方法只适合.....
有人将你的 deamon kill 了,会出现何种情况?这个俺没有测试过。

本段例子就是在 linux 下面写的,如果是 aix 应该也有类似的办法。同时 aix 和 hp-ux 等都应该是各不相同的。这里只是实现一个思想,不能生搬硬套的,这里俺只是说通过系统级的判断当前是否有本程序的进程存在,远比自己去实现这样一个 pid 文件或者是锁文件更为合理和有效。