1、前言
在应用程序的设计过程中,有时需要保证程序只被发起一次,即程序不能被重复发起,本文将分析实现该功能的过程并编写程序测试。
2、分析
2.1 ps命令
ps
命令用于显示当前系统中的进程信息,包括进程的ID、状态、占用资源情况等。通过ps
命令可以查看系统中正在运行的进程,帮助用户监控和管理系统资源的使用情况。其常用的选项和作用见下表:
选项 | 作用 |
---|---|
-e | 显示所有进程,包括其他用户的进程 |
-f | 显示进程的详细信息 |
-l | 长格式显示进程信息 |
-u | 显示指定用户的进程信息 |
-a | 显示终端上的所有进程 |
-x | 显示没有控制终端的进程 |
使用ps -aux可以查看当前进程信息,编写一个程序Running,使用sudo ./Running命令发起后查看如下,不使用sudo的话只有./Running一个进程,使用的话能查看到两个:
2.2 grep命令
grep
是一个在文本中搜索指定模式的强大工具,常用于查找包含指定字符串的行,grep
命令的常用选项:
选项 | 描述 |
---|---|
-i | 忽略大小写 |
-v | 反向匹配,显示不包含匹配文本的行 |
-r | 递归搜索目录下的文件 |
-n | 显示匹配行的行号 |
-c | 只显示匹配的行数 |
-w | 只匹配整个单词 |
-l | 只显示包含匹配文本的文件名 |
-E | 支持扩展正则表达式 |
-A | 显示匹配行及其后$行 |
-B | 显示匹配行及其前$行 |
-C | 显示匹配行及其前后$行 |
通过grep命令使用ps -aux | grep Running对ps -aux的结果进行过滤:
不关心sudo行和grep行,使用ps -aux | grep Running | grep -v sudo | grep -v grep进一步过滤:
2.3 awk命令
Awk是一种强大的文本处理工具,通常用于逐行处理文本文件。它可以根据指定的规则,对文本进行分割、筛选、计算等操作,非常适合处理结构化的数据。几种常用方法:
方法/参数 | 描述 | 举例 |
---|---|---|
{print $1, $3} |
打印指定列 | awk '{print $1, $3}' filename.txt |
$3 > 50 {print $1, $2} |
根据条件筛选行 | awk '$3 > 50 {print $1, $2}' filename.txt |
-F, |
自定义分隔符 | awk -F, '{print $1, $3}' filename.csv |
{sum += $1} END {print sum} |
计算列的和 | awk '{sum += $1} END {print sum}' filename.txt |
使用ps -aux | grep Running | grep -v sudo | grep -v grep| awk '{print $11}'命令对结果进行进一步过滤:
经过上述分析,我们可以通过shell命令获取当前正在运行的进程的名称,当程序发起两次就可以获取到两条结果:
2.4 popen函数
通过上述的shell命令可以得到程序重复发起的结果,在应用程序中可以使用popen函数来执行shell命令并获取到shell命令的结果,popen函数是一个标准C库函数,用于创建一个进程并打开一个管道,允许在父进程中执行一个命令并与其进行双向通信。
FILE *popen(const char *command, const char *type);
-
入参:
const char *command
:要执行的命令字符串。const char *type
:管道的类型,可以是"r"
(读取模式)或"w"
(写入模式)。
-
返回值:
- 返回一个指向
FILE
结构的指针,可以像文件操作一样对其进行读取或写入
- 返回一个指向
3、编程测试
根据上述分析编写测试程序如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
// 获取当前程序的名称
void vGetProssName(char cArgv0[], char cName[])
{
char *lastSlash = strrchr(cArgv0, '/');
if (lastSlash != NULL)
{
strcpy(cName, lastSlash + 1);
}
else
{
strcpy(cName, cArgv0);
}
}
// 判断程序是否正在运行
int isRunning(char cName[])
{
int count = 0;
FILE *fstream=NULL;
char buff[1024] = {0}, cmd[128] = {0};
char *pos = NULL;
sprintf(cmd, "ps -aux | grep %s | grep -v grep | grep -v sudo |awk '{print $11}'", cName);
if(NULL==(fstream = popen(cmd, "r")))
{
return -1;
}
while(NULL!=fgets(buff, sizeof(buff), fstream))
{
// 按照\n拆分每一行
char *line = strtok(buff, "\n");
while (line != NULL)
{
// 判断是否是该程序
char *lastSlash = strrchr(line, '/');
if(strcmp((lastSlash + 1), cName) == 0)
{
count++;
if(count == 2)
{
return 1;
}
}
line = strtok(NULL, "\n"); // 继续拆分下一行
}
}
pclose(fstream);
return 0;
}
int main(int argc, char *argv[])
{
char cName[64] = {0};
vGetProssName(argv[0], cName);
printf("Name: %s\n", cName);
if(isRunning(cName))
{
printf("Pro Is Running Already!\n");
return 0;
}
while(1)
{
sleep(1);
}
return 0;
}
测试结果如下:
4、总结
本文详细讲解了一种在linux应用程序编程过程中防止程序重复发起运行的方法。