0

Operating system: IBM AIX 5.3

Compiler: xlc

Hello Everyone

I have a project that using C to process some files using multi-processes. The number of sub-processes are mainly depends on the the number of incoming files and the number of current running processes. I need a reliable method to count on how many sub-processes are running at background.

By comparing the efficiency, directly reading /proc directory may have better performance than invoking popen() to execute $ ps -ef | grep blah.

I wrote a function to read psinfo in /proc/pid/psinfo and compare the arugments. Pseudo code is as follow:

int count = 0;
dp = opendir("/proc");
while (readdir_r(...))
{
    if (dir is not a process)
        return -1;
    if (dir's owner is not current user)
        return -2;
    if (failed to open "/proc/[pid]/psinfo")
        return -3;
    if (failed to read "/proc/[pid]/psinfo")
        return -4;
    if (process's name matches the given pname)
        count += 1;
}
return count;

The function generally runs perfectly at single call. However, it returns -2 or -3 or even wrong counts when embedded in while loop.

The function failed to read the attribute of /proc/pid randomly. It tells No such file or directory.

There is also a small chance to get wrong counts at the end. There seems to be an extra process with certain pid but disappeared when printing the current processes using ps.

I think there is any change were made when reading from sub-directory rapidly after parent directory were being listed.

Is there any thing I made wrong or is there any method to avoid the race condition?

Come extra information about psinfo in AIX http://www-01.ibm.com/support/knowledgecenter/ssw_aix_53/com.ibm.aix.files/doc/aixfiles/proc.htm%23files-proc?lang=en[233]

Here is the full source code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/procfs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int countProcess(char *pname)
{
    DIR                 *dir;
    int                 fd;
    int                 pid;
    int                 uid;
    int                 pcounter = 0;
    struct psinfo       pinfo;
    struct dirent       entry;
    struct dirent       *result;
    struct stat         fsstat;
    char                path[256];
    char                process_path[256];

    /* Open the /proc directory */
    if ((dir = opendir("/proc")) == NULL)
    {
        return -1;
    }

    /* Retrieve the current user id */
    uid = getuid();

    /* Walk through the /proc dir */
    for (readdir_r(dir, &entry, &result); result != NULL; readdir_r(dir, &entry, &result))
    {
        /* See if this is a process, e.g., the dirname is a number
           If not, then start off again
         */
        if ((pid = atoi(entry.d_name)) == 0)
        {
            continue;
        }

        /* Get the attributes of process dir */
        snprintf(process_path, sizeof(process_path), "/proc/%s", entry.d_name);
        if (stat(process_path, &fsstat) == -1)
        {
            closedir(dir);
            return -2;
        }


        /* Verify if the process runs by current user
           If not, then start off again
         */
        if (fsstat.st_uid != uid)
        {
            continue;
        }

        /* Open and read from psinfo file */
        snprintf(path, sizeof(path), "/proc/%s/psinfo", entry.d_name);

        if ((fd = open(path, O_RDONLY)) < 0)
        {
            close(fd);
            closedir(dir);
            return -3;
        }

        if (read(fd, &pinfo, sizeof(pinfo)) < 0)
        {
            close(fd);
            closedir(dir);
            return -4;
        }

        /* String comparison, if same, increase the counter */
        if (!strcmp(pinfo.pr_psargs, pname))
        {
            pcounter++;
        }
        close(fd);

    }

    /* returns counter */
    closedir(dir);
    return pcounter;
}

Update 13/Jan/2015

Thanks to CoreyStup. The race condition can be bypassed by getprocs() function provided in procinfo.h

Here is the code for the solution

#include <stdio.h>
#include <unistd.h>
#include <procinfo.h>
#include <sys/types.h>

int countProcess(const char *pname)
{
    struct procsinfo            pinfo;
    pid_t                       pid = 0;
    uid_t                       uid;
    char                        args[256];
    int                         index;
    int                         pcounter = 0;

    memset(args, 0, sizeof(args));
    uid = getuid();

    /* Get procsinfo from internal API */
    while (0 < getprocs(&pinfo, (int)sizeof(struct procsinfo), NULL, 0, &pid, 1))
    {
        /* Skip the process that doesn't belong to current user */
        if (pinfo.pi_uid != uid)
        {
            continue;
        }

        /* Get process arguments */
        if (getargs(&pinfo, sizeof(struct procsinfo), args, sizeof(args)) != 0)
        {
            return -1;
        }

        /* getargs returns the args list seperated by 0, we need to use space to replace 0 */
        for (index = 0; index < 256 - 1 && !(args[index] == 0 && args[index + 1] == 0); index++)
        {
            if (args[index] == 0)
            {
                args[index] = ' ';
            }
        }
        if (!strncmp(args, pname, strlen(pname)))
        {
            pcounter++;
        }

    }
    return pcounter;
}
Ran Bao
  • 156
  • 2
  • 5

1 Answers1

1

Try using getprocs(). I find it works better than shelling out with /proc or ps.

I gave an example here: Need help in getting the process name based on the pid in aix

Community
  • 1
  • 1
CoreyStup
  • 1,488
  • 13
  • 14