Processes

As we stated in the Overview chapter, the Neutrino OS architecture consists of a small microkernel and some number of cooperating processes. We also pointed out that your applications should be written the same way — as a set of cooperating processes.

In this chapter, we'll see how to start processes (also known as creating processes) from code, how to terminate them, and how to detect their termination when it happens.

For another perspective, see the Processes and Threads and Message Passing chapters of Getting Started with QNX Neutrino.

Starting processes — two methods

In embedded applications, there are two typical approaches to starting your processes at boot time. One approach is to run a shell script that contains the command lines for running the processes. There are some useful utilities such as on and nice for controlling how those processes are started.

The other approach is to have a starter process run at boot time. This starter process then starts up all your other processes. This approach has the advantage of giving you more control over how processes are started, whereas the script approach is easier for you (or anyone) to modify quickly.

Process creation

The process manager component of procnto is responsible for process creation. If a process wants to create another process, it makes a call to one of the process-creation functions, which then effectively sends a message to the process manager.

Here are the process-creation functions:

There are several versions of spawn*() and exec*(); the asterisk represents one to three letters, where:

For details on each of these functions, see their entries in the Library Reference. Here we'll mention some of the things common to many of them.

When you start a new process, it replaces the existing process if:

The calling thread in the existing process is suspended while the new process executes (control continues at the point following the place where the new process was started) in the following situations:

Concurrency

Three possibilities can happen to the creator during process creation:

  1. The child process is created and runs concurrently with the parent. In this case, as soon as process creation is successful, the process manager replies to the parent, and the child is made READY. If it's the parent's turn to run, then the first thing it does is return from the process-creation function. This may not be the case if the child process was created at a higher priority than the parent (in which case the child will run before the parent gets to run again).

    This is how fork(), forkpty(), popen(), and spawn() work. This is also how the spawn*() family of functions work when you specify a mode of P_NOWAIT or P_NOWAITO.

  2. The child replaces the parent. In fact, they're not really parent and child, because the image of the given process simply replaces that of the caller. Many things will change, but those things that uniquely identify a process (such as the process ID) will remain the same. This is typically referred to as “execing,” since usually the exec*() functions are used.

    Many things will remain the same (including the process ID, parent process ID, and file descriptors) with the exception of file descriptors that had the FD_CLOEXEC flag set using fcntl(). See the exec*() functions for more on what will and will not be the same across the exec.

    The login command serves as a good example of execing. Once the login is successful, the login command execs into a shell.

    Functions you can use for this type of process creation are the exec*() and spawn*() families of functions, with mode passed as P_OVERLAY.

  3. The calling thread in the parent waits until the child terminates. You can make this happen by passing the mode as P_WAIT when you call one of the spawn*() functions.

    Note that what is going on underneath the covers in this case is that spawn() is called as in the first possibility above. Then, after it returns, waitpid() is called in order to wait for the child to terminate. This means that you can use any of the functions mentioned in our first possibility above to achieve the same thing if you follow them by a call to one of the wait*() functions (e.g., wait() or waitpid()).

Using fork() and forkpty()

As of this writing, you can't use fork() and forkpty() in a process that has threads. The fork() and forkpty() functions will simply return -1 and errno will be set to ENOSYS.


Note: Many programmers coming from the Unix world are familiar with the technique of using a call to fork() followed by a call to one of the exec*() functions in order to create a process that's different from the caller. In QNX Neutrino, you can usually achieve the same thing more efficiently with a single call to one of the posix_spawn*() or spawn*() functions.

Inheriting file descriptors

The documentation in the QNX Neutrino Library Reference for each function describes in detail what the child inherits from the parent. One thing that we should talk about here, however, is file-descriptor inheritance.

With many of the process-creation functions, the child inherits the file descriptors of the parent. For example, if the parent had file descriptor 5 in use for a particular file when the parent creates the child, the child will also have file descriptor 5 in use for that same file. The child's file descriptor will have been duplicated from the parent's. This means that at the filesystem manager level, the parent and child have the same open control block (OCB) for the file, so if the child seeks to some position in the file, then that changes the parent's seek position as well. It also means that the child can do a write(5, buf, nbytes) without having previously called open().

If you don't want the child to inherit a particular file descriptor, then you can use fcntl() to prevent it. Note that this won't prevent inheritance of a file descriptor during a fork(). The call to fcntl() would be:

fcntl(fd, F_SETFD, FD_CLOEXEC);

If you want the parent to set up exactly which files will be open for the child, then you can use the fd_count and fd_map parameters with spawn(). Note that in this case, only the file descriptors you specify will be inherited. This is especially useful for redirecting the child's standard input (file descriptor 0), standard output (file descriptor 1), and standard error (file descriptor 2) to places where the parent wants them to go.

Alternatively this file descriptor inheritance can also be done through use of fork(), one or more calls to dup(), dup2(), and close(), and then exec*(). The call to fork() creates a child that inherits all the of the parent's file descriptors. dup(), dup2() and close() are then used by the child to rearrange its file descriptors. Lastly, exec*() is called to replace the child with the process to be created. Though more complicated, this method of setting up file descriptors is portable whereas the spawn() method is not.

Process termination

A process can terminate in one of the following basic ways:

In some operating systems, if a parent process dies, then all of its child processes die too. This isn't the case in Neutrino.

When a process terminates—no matter why—all of its resources are cleaned up:

Normal process from exit()

A process can terminate itself by having any thread in the process call exit(). Returning from main() (i.e., in the main thread) also terminates the process, because the code that's returned to calls exit(). This isn't true of threads other than the main thread. Returning normally from one of them causes pthread_exit() to be called, which terminates only that thread.

The value passed to exit() or returned from main() is called the exit status.

When a process dies by calling exit(), “normal exit processing” happens. This includes:

Process termination from signals

A process can be terminated by a signal for a number of reasons. Ultimately, all of these reasons will result in a signal's being set on the process. A signal is something that can interrupt the flow of your threads at any time. The default action for most signals is to terminate the process.


Note:
  • What causes a particular signal to be generated is sometimes processor-dependent.
  • The cleanup of the terminated process occurs by default at the priority of the thread that sent the signal. As a QNX Neutrino extension to POSIX functions, if you OR the SIG_TERMER_NOINHERIT flag (defined in <signal.h>) into the signal number, the cleanup occurs at the priority of the thread that received the signal.

Here are some of the reasons that a process might be terminated by a signal:

When a process dies due to a signal that isn't handled or masked, “normal exit processing” doesn't happen, so this is often called abnormal termination of a process.

To get the kernel to display some diagnostics whenever a process terminates abnormally, configure procnto with multiple -v options. If the process has fd 2 open, then the diagnostics are displayed using (stderr); otherwise; you can specify where the diagnostics get displayed by using the -D option to your startup. For example, the -D as used in this buildfile excerpt will cause the output to go to a serial port:

[virtual=x86,bios +compress] .bootstrap = {
    startup-bios -D 8250..115200
    procnto -vvvv
}

You can also have the current state of a terminated process written to a file so that you can later bring up the debugger and examine just what happened. This type of examination is called postmortem debugging. This happens only if the process is terminated due to one of these signals:

Signal Description
SIGABRT Program-called abort function
SIGBUS Parity error
SIGEMT EMT instruction (emulation trap)

Note that SIGEMT and SIGDEADLK (mutex deadlock—see SyncMutexEvent()) refer to the same signal.

SIGFPE Floating-point error or division by zero
SIGILL Illegal instruction executed

One possible cause for this signal is trying to perform an operation that requires I/O privileges. A thread can request these privileges by calling ThreadCtl(), specifying the _NTO_TCTL_IO flag:

ThreadCtl( _NTO_TCTL_IO, 0 );

SIGQUIT Quit
SIGSEGV Segmentation violation
SIGSYS Bad argument to a system call
SIGTRAP Trace trap (not reset when caught)
SIGXCPU Exceeded the CPU limit

The process that dumps the state to a file when the process terminates is called dumper, which must be running when the abnormal termination occurs. This is extremely useful, because embedded systems may run unassisted for days or even years before a crash occurs, making it impossible to reproduce the actual circumstances leading up to the crash.

Process termination from thread loss

A process must have one or more threads. If the number of threads in a process goes to 0, the process is terminated.

When a thread calls pthread_exit(), that thread is terminated. If any thread but the main thread returns from its thread function, then the wrapping code calls pthread_exit(). If the last thread in a process calls pthread_exit(), then the process is terminated; in this case, “normal exit processing” doesn't happen.

Detecting process termination

In an embedded application, it's often important to detect if any process terminates prematurely and, if so, to handle it. Handling it may involve something as simple as restarting the process or as complex as:

  1. Notifying other processes that they should put their systems into a safe state.
  2. Resetting the hardware.

This is complicated by the fact that some Neutrino processes call procmgr_daemon(). Processes that call this function are referred to as daemons. The procmgr_daemon() function:

As a result of the above, their termination is hard to detect.

Another scenario is where a server process wants to know if any of its clients disappear so that it can clean up any resources it had set aside on their behalf.

Let's look at various ways of detecting process termination.

Using the High Availability Framework

The High Availability Framework provides components not only for detecting when processes terminate, but also for recovering from that termination.

The main component is a process called the High Availability Manager (ham) that acts as a “smart watchdog”. Your processes talk to ham using the HAM API. With this API you basically set up conditions that ham should watch for and take actions when these conditions occur. For example, you can tell ham to detect when a process terminates and restart it. The HAM can even detect the termination of daemon processes.

In fact, the High Availability Manager can restart a number of processes, wait between restarts for a process to be ready, and notify the process that this is happening.

The HAM also does heartbeating. Processes can periodically notify ham that they're still functioning correctly; if a process-specified amount of time goes by between these notifications, then ham can take some action.

The above are just a sample of what's possible with the High Availability Framework. For more information, see the High Availability Framework Developer's Guide

Detecting termination from a starter process

If you've created a set of processes using a starter process as discussed at the beginning of this section, then all those processes are children of the starter process, with the exception of those that have called procmgr_daemon(). If all you want to do is detect that one of those children has terminated, then a loop that blocks on wait() or sigwaitinfo() will suffice. Note that when a child process calls procmgr_daemon(), both wait() and sigwaitinfo() behave as if the child process died, although the child is still running.

The wait() function will block, waiting until any of the caller's child processes terminate. The other wait*() functions include waitpid(), which lets you wait for a specific child process, wait3(), and wait4(). Lastly, there is waitid(), which is the lower level of all the wait*() functions and returns the most information.

The wait*() functions won't always help, however. If a child process was created using one of the spawn*() family of functions with the mode passed as P_NOWAITO, then the wait*() functions won't be notified of its termination!

What if the child process terminates, but the parent hasn't yet called wait*()? This would be the case if one child had already terminated, so wait*() returned, but then before the parent got back to the wait*(), a second child terminates. In that case, some information would have to be stored away about the second child for when the parent does get around to its wait*().

This is in fact the case. The second child's memory will have been freed up, its files will have been closed, and in general the child's resources will have been cleaned up with the exception of a few bytes of memory in the process manager that contain the child's exit status or other reason that it had terminated and its process ID. When the second child is in this state, it's referred to as a zombie. The child will remain a zombie until the parent either terminates or finds out about the child's termination (e.g., the parent calls wait*()).

What this means is that if a child has terminated and the parent is still alive but doesn't yet know about the terminated child (e.g., hasn't called wait*()), then the zombie will be hanging around. If the parent will never care, then you may as well not have the child become a zombie. To prevent the child from becoming a zombie when it terminates, create the child process using one of the spawn*() family of functions and pass P_NOWAITO for the mode.

Sample parent process using wait()

The following sample illustrates the use of wait() for waiting for child processes to terminate.

/* 
 * waitchild.c
 *
 * This is an example of a parent process that creates some child
 * processes and then waits for them to terminate. The waiting is
 * done using wait(). When a child process terminates, the
 * wait() function returns.
*/

#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

main(int argc, char **argv)
{
    char                *args[] = { "child", NULL };
    int                 i, status;
    pid_t               pid;
    struct inheritance  inherit;

    // create 3 child processes
    for (i = 0; i < 3; i++) {
        inherit.flags = 0;
        if ((pid = spawn("child", 0, NULL, &inherit, args, environ)) == -1)
            perror("spawn() failed");
        else
            printf("spawned child, pid = %d\n", pid);
    }

    while (1) {
        if ((pid = wait(&status)) == -1) {
            perror("wait() failed (no more child processes?)");
            exit(EXIT_FAILURE);
        }
        printf("a child terminated, pid = %d\n", pid);
        
        if (WIFEXITED(status)) {
            printf("child terminated normally, exit status = %d\n",
                WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("child terminated abnormally by signal = %X\n",
                WTERMSIG(status));
        } // else see documentation for wait() for more macros
    }
}

The following is a simple child process to try out with the above parent.

#include <stdio.h>
#include <unistd.h>

main(int argc, char **argv)
{
    printf("pausing, terminate me somehow\n");
    pause();
}

Sample parent process using sigwaitinfo()

The sigwaitinfo() function blocks, waiting until any signals that the caller tells it to wait for are set on the caller. If a child process terminates, then the SIGCHLD signal is set on the parent. So all the parent has to do is request that sigwaitinfo() return when SIGCHLD arrives.

The following sample illustrates the use of sigwaitinfo() for waiting for child processes to terminate.

/* 
 * sigwaitchild.c
 *
 * This is an example of a parent process that creates some child
 * processes and then waits for them to terminate.  The waiting is
 * done using sigwaitinfo().  When a child process terminates, the
 * SIGCHLD signal is set on the parent.  sigwaitinfo() will return
 * when the signal arrives.
*/

#include <errno.h>
#include <spawn.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/neutrino.h>

void
signal_handler(int signo)
{
    // do nothing
}

int main(void)
{
    char                *args[] = { "child", NULL };
    int                 i;
    pid_t               pid;
    sigset_t            mask;
    siginfo_t           info;
    struct inheritance  inherit;
    struct sigaction    action;

    // mask out the SIGCHLD signal so that it will not interrupt us,
    // (side note: the child inherits the parents mask)
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigprocmask(SIG_BLOCK, &mask, NULL);

    // by default, SIGCHLD is set to be ignored so unless we happen
    // to be blocked on sigwaitinfo() at the time that SIGCHLD
    // is set on us we will not get it.  To fix this, we simply
    // register a signal handler.  Since we've masked the signal
    // above, it will not affect us.  At the same time we will make
    // it a queued signal so that if more than one are set on us,
    // sigwaitinfo() will get them all.
    action.sa_handler = signal_handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = SA_SIGINFO; // make it a queued signal
    sigaction(SIGCHLD, &action, NULL);

    // create 3 child processes
    for (i = 0; i < 3; i++) {
        inherit.flags = 0;
        if ((pid = spawn("child", 0, NULL, &inherit, args, environ)) == -1)
            perror("spawn() failed");
        else
            printf("spawned child, pid = %d\n", pid);
    }

    while (1) {
        if (sigwaitinfo(&mask, &info) == -1) {
            perror("sigwaitinfo() failed");
            continue;
        }
        switch (info.si_signo) {
        case SIGCHLD:
            // info.si_pid is pid of terminated process, it is not POSIX
            printf("A child terminated; pid = %d\n", info.si_pid);
            break;
        default:
            // Should not get here since we asked only for SIGCHLD
            printf("Unexpected signal: %d\n", info.si_signo);
        }
    }
}

Detecting dumped processes

As mentioned above, you can run dumper so that when a process dies, dumper writes the state of the process to a file.

You can also write your own dumper-type process to run instead of, or as well as, dumper. This way the terminating process doesn't have to be a child of yours.

To do this, write a resource manager that registers the name, /proc/dumper with type _FTYPE_DUMPER. When a process dies due to one of the appropriate signals, the process manager will open /proc/dumper and write the pid of the process that died — then it'll wait until you reply to the write with success and then it'll finish terminating the process.

It's possible that more than one process will have /proc/dumper registered at the same time, however, the process manager notifies only the process that's at the beginning of its list for that name. Undoubtedly, you want both your resource manager and dumper to handle this termination. To do this, request the process manager to put you, instead of dumper, at the beginning of the /proc/dumper list by passing _RESMGR_FLAG_BEFORE in the flags argument to resmgr_attach(). You must also open /proc/dumper so that you can communicate with dumper if it's running. Whenever your io_write handler is called, write the pid to dumper and do your own handling. Of course this works only when dumper is run before your resource manager; otherwise, your open of /proc/dumper won't work.

The following is a sample process that demonstrates the above:

/*
 *  dumphandler.c
 *
 *  This demonstrates how you get notified whenever a process
 *  dies due to any of the following signals:
 *
 *  SIGABRT
 *  SIGBUS
 *  SIGEMT
 *  SIGFPE
 *  SIGILL
 *  SIGQUIT
 *  SIGSEGV
 *  SIGSYS
 *  SIGTRAP
 *  SIGXCPU
 *
 *  To do so, register the path, /proc/dumper with type
 *  _FTYPE_DUMPER. When a process dies due to one of the above
 *  signals, the process manager will open /proc/dumper, and
 *  write the pid of the process that died - it will wait until
 *  you reply to the write with success, and then it will finish
 *  terminating the process.
 *
 *  Note that while it is possible for more than one process to
 *  have /proc/dumper registered at the same time, the process
 *  manager will notify only the one that is at the beginning of
 *  its list for that name.
 *
 *  But we want both us and dumper to handle this termination.
 *  To do this, we make sure that we get notified instead of
 *  dumper by asking the process manager to put us at the
 *  beginning of its list for /proc/dumper (done by passing
 *  _RESMGR_FLAG_BEFORE to  resmgr_attach()).  We also open
 *  /proc/dumper so that we can communicate with dumper if it is
 *  running.  Whenever our io_write handler is called, we write
 *  the pid to dumper and do our own handling.  Of course, this
 *  works only if dumper is run before we are, or else our open
 *  will not work.
 *
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>
#include <sys/procfs.h>
#include <sys/stat.h>

int io_write (resmgr_context_t *ctp, io_write_t  *msg,
              RESMGR_OCB_T *ocb);

static int  dumper_fd;

resmgr_connect_funcs_t  connect_funcs;
resmgr_io_funcs_t       io_funcs;
dispatch_t              *dpp;
resmgr_attr_t           rattr;
dispatch_context_t      *ctp;
iofunc_attr_t           ioattr;

char    *progname = "dumphandler";

main(int argc, char **argv)
{
    /* find dumper so that we can pass any pids on to it */
    dumper_fd = open("/proc/dumper", O_WRONLY);

    dpp = dispatch_create();

    memset(&rattr, 0, sizeof(rattr));
    rattr.msg_max_size = 2048;

    iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs,
                     _RESMGR_IO_NFUNCS, &io_funcs);
    io_funcs.write = io_write;

    iofunc_attr_init(&ioattr, S_IFNAM | 0600, NULL, NULL);

    resmgr_attach(dpp, &rattr, "/proc/dumper", _FTYPE_DUMPER,
                  _RESMGR_FLAG_BEFORE, &connect_funcs,
                  &io_funcs, &ioattr);

    ctp = dispatch_context_alloc(dpp);

    while (1) {
        if ((ctp = dispatch_block(ctp)) == NULL) {
            fprintf(stderr, "%s:  dispatch_block failed: %s\n",
                             progname, strerror(errno));
            exit(1);
        }
        dispatch_handler(ctp);
    }
}

struct dinfo_s {
    procfs_debuginfo    info;
    char                pathbuffer[PATH_MAX]; /* 1st byte is
                                                 info.path[0] */
};

int
display_process_info(pid_t pid)
{
    char            buf[PATH_MAX + 1];
    int             fd, status;
    struct dinfo_s  dinfo;
    procfs_greg     reg;

    printf("%s: process %d died\n", progname, pid);

    sprintf(buf, "/proc/%d/as", pid);

    if ((fd = open(buf, O_RDONLY|O_NONBLOCK)) == -1)
        return errno;

    status = devctl(fd, DCMD_PROC_MAPDEBUG_BASE, &dinfo,
                    sizeof(dinfo), NULL);
    if (status != EOK) {
        close(fd);
        return status;
    }

    printf("%s: name is %s\n", progname, dinfo.info.path);

    /*
     * For getting other type of information, see sys/procfs.h,
     * sys/debug.h, and sys/dcmd_proc.h
     */
     
    close(fd);
    return EOK;
}

int
io_write(resmgr_context_t *ctp, io_write_t *msg,
         RESMGR_OCB_T *ocb)
{
    char *pstr;
    int status;

    if ((status = iofunc_write_verify(ctp, msg, ocb, NULL))
        != EOK)
        return status;

    if ( (msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
        return ENOSYS;


    if (dumper_fd != -1) {
        /* pass it on to dumper so it can handle it too */

        if (write(dumper_fd, msg+1, msg->i.nbytes) == -1) {
            close(dumper_fd);
            dumper_fd = -1; /* something wrong, no sense in
                               doing it again later */
        }
    }

    /* Proc writes us the pid as a string; get a pointer to
       the write data */

    pstr = (char *) (msg+1);

    /* Assume we have room for a null-byte at the end of the
       pid in our (default) 1500 byte receive buffer */
    pstr[msg->i.nbytes] = '\0';

    if ((status = display_process_info(atoi(pstr))) != EOK)
        return status;

    _IO_SET_WRITE_NBYTES(ctp, msg->i.nbytes);

    return EOK;
}

For more information about getting process information (including using the DCMD_PROC_MAPDEBUG_BASE devctl() command), see Controlling processes via the /proc filesystem,” later in this chapter.

Detecting the termination of daemons

What would happen if you've created some processes that subsequently made themselves daemons (i.e., called procmgr_daemon())? As we mentioned above, the wait*() functions and sigwaitinfo() won't help.

For these you can give the kernel an event, such as one containing a pulse, and have the kernel deliver that pulse to you whenever a daemon terminates. This request for notification is done by calling procmgr_event_notify() or procmgr_event_notify_add() with PROCMGR_EVENT_DAEMON_DEATH in flags.

The difference between these functions is that with procmgr_event_notify(), your process can have one notification request; if you call the function again, the request replaces the previous one. With procmgr_event_notify_add(), your process can have more than one notification request.

See the documentation for procmgr_event_notify() for an example that uses PROCMGR_EVENT_DAEMON_DEATH.

Detecting client termination

The last scenario is where a server process wants to be notified of any clients that terminate so that it can clean up any resources that it had set aside for them.

This is very easy to do if the server process is written as a resource manager, because the resource manager's io_close_dup() and io_close_ocb() handlers, as well as the ocb_free() function, will be called if a client is terminated for any reason. For more information, see Writing a Resource Manager.

If the server process isn't a resource manager, it can pass the _NTO_CHF_DISCONNECT flag when it calls ChannelCreate(). This tells the kernel to deliver a pulse when a client process disconnects from the server. If the server is using name_attach(), that function automatically sets this flag.

Stack allocation

Each thread has its own stack that you can allocate yourself or have the system manage.

The pthread_attr_t structure includes members that specify a new thread's stack address and size; pthread_attr_init() sets the default values, and you can use pthread_attr_setstackaddr() and pthread_attr_setstacksize() to override them. The values of the stack address and size members of this structure control the type of stack allocation that occurs when you create a thread:

Stack address Stack size Allocation
NULL 0 Automatic (the default)
NULL Desired size Partly automatic
Non-NULL Size of the allocated area Manual

Let's compare the types of allocation:

Automatic
The process manager allocates a stack of the default size, 128 KB, in virtual memory. The stack is followed by a read-only guard page that the process manager uses to detect stack overflow.

Initially, only part of the stack is allocated in physical memory. This portion contains the thread local storage (TLS) and other system data that's specific to the thread. The process manager allocates additional 4 KB pages of physical memory when required. The guard page exists only in virtual memory; there's no physical memory allocated for it.

When the thread exits, the process manager automatically deallocates the stack.

Partly automatic
The process manager allocates a stack of the size that you specified, rounded up to a multiple of the page size (4 KB). The space is allocated in virtual memory, with a guard page, and the process manager looks after the stack as it does for automatic allocation.
Manual
The process manager uses the stack that you allocated for the thread. It's up to you to allocate enough space for the thread, and no guard page is provided. The process manager doesn't deallocate the stack when the thread exits.

Note: If you specify a stack size (for partly automatic or manual allocation), it should be the size that you want plus PTHREAD_STACK_MIN, which is the amount of space that the thread needs for its thread local storage and other overhead.

A process's main thread starts with an automatically allocated 512 KB stack, but it isn't deleted when the main thread goes away (e.g., calls pthread_exit() or is cancelled). The main thread's stack includes the command-line arguments and environment variables, which other threads might still need. To specify the stack size for the main thread, use qcc's -N option.

The output of the pidin mem command uses an asterisk (*) to indicate a stack that isn't automatically returned to the system heap when the thread exits.

Controlling processes via the /proc filesystem

Implemented by the Process Manager component of procnto, the /proc virtual filesystem lets you access and control every process and thread running within the system.

The /proc filesystem manifests each process currently running on the system as a directory whose name is the numerical process ID (decimal) of the process. Inside this directory, you'll find a file called as (“address space”) that contains the process's entire memory space. Threads are accessible through the as file created for the process; you can select a thread via devctl() calls. You can use the following standard functions to access the /proc filesystem:

Function Purpose
open() Establish a file descriptor to a process
read() Read data from the process's address space
write() Write data to the process's address space
stat() Return struct stat information
lseek() Establish a position within the process's address space for further operations
devctl() Manipulate a process or thread
close() Release a file descriptor

Ancillary functions (such as readdir(), opendir(), and so on) are supported on the directory /proc itself — this aids in implementing commands such as ls.

This section includes:

and descriptions of the following devctl() commands:

DCMD_PROC_BREAKDCMD_PROC_CHANNELSDCMD_PROC_CLEAR_FLAGDCMD_PROC_CURTHREADDCMD_PROC_EVENTDCMD_PROC_FREEZETHREADDCMD_PROC_GETALTREGDCMD_PROC_GETFPREGDCMD_PROC_GETGREGDCMD_PROC_GETREGSETDCMD_PROC_GET_BREAKLISTDCMD_PROC_INFODCMD_PROC_IRQSDCMD_PROC_MAPDEBUGDCMD_PROC_MAPDEBUG_BASEDCMD_PROC_MAPINFODCMD_PROC_PAGEDATADCMD_PROC_PTINFODCMD_PROC_RUNDCMD_PROC_SETALTREGDCMD_PROC_SETFPREGDCMD_PROC_SETGREGDCMD_PROC_SETREGSETDCMD_PROC_SET_FLAGDCMD_PROC_SIGNALDCMD_PROC_STATUSDCMD_PROC_STOPDCMD_PROC_SYSINFODCMD_PROC_THAWTHREADDCMD_PROC_THREADCTLDCMD_PROC_TIDSTATUSDCMD_PROC_TIMERSDCMD_PROC_WAITSTOP

Establishing a connection

To be able to access a process or thread, you must first use the open() call to get a valid file descriptor. You can then use this file descriptor with the function calls listed below to access the process or thread.


Note: Open the file (/proc/pid/as), not the /proc/pid directory.

In order to read or write data from or to the process, you must have opened the file descriptor in the appropriate mode. You must also have appropriate privileges to open the particular process. By default:


Caution: The default permissions on these files can be a security problem. When you start procnto, you can use the -u option to specify the umask to use for entries in /proc/pid. The downside of tightening up the umask is that some applications (e.g., pidin arg and the shelf's network monitor) assume they can open these files.

When you're done accessing the process or thread, you should close() the file descriptor.

Reading and writing the process's address space

The easiest operation to perform is to access the process's address space. (Since threads exist in the context of a process and have access to everything within a process, there's no need to consider threads in this discussion.)

You can use the read(), write(), and lseek() functions to access the process's address space. The read() function transfers bytes from the current position within the process to the program issuing the read(), and write() transfers bytes from the program to the process.

Determining the offset

The position at which transfer occurs depends on the current offset as set on the file descriptor. In virtual-address systems such as QNX Neutrino, the current offset is taken to be the virtual address from the process's perspective.

For example, to read 4096 bytes at offset 0x00021000 from process ID number 2259, the following code snippet could be used:

int     fd;
char    buf [4096];

fd = open ("/proc/2259/as", O_RDONLY);
lseek (fd, 0x00021000, SEEK_SET);
read (fd, buf, 4096);

Of course, you should check the return values in your real code!

Determining accessibility

If a virtual address process has different chunks of memory mapped into its address space, performing a read or write on a given address may or may not work (or it may not affect the expected number of bytes). This is because the read() and write() functions affect only contiguous memory regions. If you try to read a page of memory that isn't mapped by the process, the read will fail; this is expected.

Manipulating a process or thread

Once you have a file descriptor to a particular process, you can do a number of things to that process and its associated thread(s):

All of these functions are performed using the devctl() call as described in the sections that follow. To be able to use these devctl() calls, you'll need at least the following:

#include <devctl.h>
#include <sys/procfs.h>

Selecting a thread for further operations

When you first perform the open() to a particular process, by default you're connected to the first thread (the thread that executed the main() function).

If you wish to switch a different thread, use the DCMD_PROC_CURTHREAD devctl() command, as described later in this chapter.

To find out how many threads are available in the given process, see the devctl() command DCMD_PROC_INFO, below.

Starting/stopping processes and threads

The following devctl() commands start and stop processes and threads. You must have opened the file descriptor for writing.

Setting breakpoints

The following devctl() commands set breakpoints. You must have opened the file descriptor for writing.

Examining process and thread attributes

You can use the following devctl() commands to examine process and thread attributes:

Thread information

Several of the devctl() commands use a procfs_status structure (which is the same as debug_thread_t), so let's look at this structure before going into the commands themselves:

The debug_thread_t structure is defined as follows in <sys/debug.h>:

typedef struct _debug_thread_info {
    pid_t                       pid;
    pthread_t                   tid;
    uint32_t                    flags;
    uint16_t                    why;
    uint16_t                    what;
    uint64_t                    ip;
    uint64_t                    sp;
    uint64_t                    stkbase;
    uint64_t                    tls;
    uint32_t                    stksize;
    uint32_t                    tid_flags;
    uint8_t                     priority;
    uint8_t                     real_priority;
    uint8_t                     policy;
    uint8_t                     state;
    int16_t                     syscall;
    uint16_t                    last_cpu;
    uint32_t                    timeout;
    int32_t                     last_chid;
    sigset_t                    sig_blocked;
    sigset_t                    sig_pending;
    siginfo_t                   info;
    union {
        struct {
            pthread_t                   tid;
        }                           join;
        struct {
            int32_t                     id;
            uintptr_t                   sync;
        }                           sync;
        struct {
            uint32_t                    nd;
            pid_t                       pid;
            int32_t                     coid;
            int32_t                     chid;
            int32_t                     scoid;
        }                           connect;
        struct {
            int32_t                     chid;
        }                           channel;
        struct {
            pid_t                       pid;
            uintptr_t                   vaddr;
            uint32_t                    flags;
        }                           waitpage;
        struct {
            uint32_t                    size;
        }                           stack;
        uint64_t                        filler[4];
    }                           blocked;
    uint64_t                    start_time;
    uint64_t                    sutime;
    uint8_t                     extsched[8];
    uint64_t                    reserved2[5];
}                           debug_thread_t;

Note: If you ask for information about a specific thread, and the thread no longer exists, the process manager returns information about the one with the next higher thread ID. If there are no threads with a higher ID, devctl() returns ESRCH.

The members include:

pid, tid
The process and thread IDs.
flags
A combination of the following bits:
why, what
The why field indicates why the process was stopped; the what field gives additional information:
why Description what
_DEBUG_WHY_CHILD A child process is ready to run; the parent gets a chance to connect to it The child's process ID
_DEBUG_WHY_EXEC The process was created by a call to an exec*() function 0
_DEBUG_WHY_FAULTED The thread faulted The fault number; see siginfo_t
_DEBUG_WHY_JOBCONTROL The thread is under job control The signal number
_DEBUG_WHY_REQUESTED The thread was working normally before being stopped by request 0
_DEBUG_WHY_SIGNALLED The thread received a signal The signal number
_DEBUG_WHY_TERMINATED The thread terminated The process's exit status
ip
The current instruction pointer.
sp
The thread's stack pointer.
stkbase
The base address of the thread's stack region.
tls
A pointer to the thread's local storage, which is on the thread's stack. For more information, see struct _thread_local_storage in <sys/storage.h>.
stksize
The stack size.
tid_flags
The thread flags; see _NTO_TF_* in <sys/neutrino.h>:
priority
The priority the thread is actually running at (e.g., its priority may have been boosted).
real_priority
The actual priority the thread would be at with no boosting and so on.
policy
The scheduling policy; one of SCHED_FIFO, SCHED_RR, SCHED_OTHER, or SCHED_SPORADIC.
state
The thread's state. The states themselves are defined in <sys/states.h>; for descriptions, see Thread life cycle in the QNX Neutrino Microkernel chapter of the System Architecture guide. If the thread is waiting for something, the blocked member may hold additional information, as described below.
syscall
The last system call; one of the __KER_* values defined in <sys/kercalls.h>.
last_cpu
The processor the thread last ran on.
timeout
_NTO_TF_ACTIVE|_NTO_TF_IMMEDIATE|(1 << state) — set by TimerTimeout().
last_chid
The ID of the last channel this thread received a message on.
sig_blocked
The set of signals that are blocked for the thread.
sig_pending
The set of signals that are pending for the thread.
info
A siginfo_t structure that contains information about the last signal or fault received.
blocked
A union of the following:
start_time
The thread's starting time, in nanoseconds.
sutime
The thread's system plus user running time, in nanoseconds.
extsched
Extended scheduling information; a struct extsched_aps_dbg_thread structure if the adaptive partitioning thread scheduler is installed.

DCMD_PROC_BREAK

Set or remove a breakpoint in the process that's associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_BREAK   __DIOTF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 14, procfs_break)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process. You must have opened the file descriptor for writing.
dcmd DCMD_PROC_BREAK
dev_data_ptr A pointer to a procfs_break structure
n_bytes sizeof(procfs_break)
dev_info_ptr NULL

The argument is a pointer to a procfs_break structure (see debug_break_t in <sys/debug.h>) that specifies the breakpoint to be set or removed. For example:

procfs_break        brk;

memset(&brk, 0, sizeof brk);
brk.type = _DEBUG_BREAK_EXEC;
brk.addr = acc->break_addr.offset;
brk.size = 0;
devctl(fd, DCMD_PROC_BREAK, &brk, sizeof brk, 0);
  

Use a size of 0 to set a breakpoint, and a size of -1 to delete it.


Note: Breakpoints other than _DEBUG_BREAK_EXEC are highly dependent on the hardware. In many architectures, other types of breakpoints cause the kernel to make the process run in single-step, checking the watchpoints each time, which can be very slow.

DCMD_PROC_CHANNELS

Get information about the channels owned by the specified process.

#include <sys/procfs.h>

#define DCMD_PROC_CHANNELS   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 29, procfs_channel)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_CHANNELS
dev_data_ptr NULL, or an array of procfs_channel structures
n_bytes 0, or the size of the array
dev_info_ptr A pointer to an integer where the number of channels will be stored.

Call this the first time with an argument of NULL to get the number of channels:

devctl(fd, DCMD_PROC_CHANNELS, NULL, 0, &n);
  

Next, allocate a buffer that's large enough to hold a procfs_channel structure (see debug_channel_t in <sys/debug.h>) for each channel, and pass it to another devctl() call:

my_buffer = (procfs_channel *)
               malloc( sizeof(procfs_channel) * n );
if ( my_buffer == NULL ) {
  /* Not enough memory. */
}

devctl( fd, DCMD_PROC_CHANNELS, my_buffer,
        sizeof(procfs_channel) * n, &dummy);
  

DCMD_PROC_CLEAR_FLAG

Clear specific debug flags with the values provided for the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_CLEAR_FLAG   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 19, uint32_t)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_CLEAR_FLAG
dev_data_ptr A pointer to a uint32_t
n_bytes sizeof(uint32_t)
dev_info_ptr NULL

The flags that can be cleared are described in <sys/debug.h>. The argument is a pointer to an unsigned integer that specifies the debug flags to clear. For example:

int flags;

flags = _DEBUG_FLAG_KLC;        /* Kill-on-Last-Close flag */
devctl( fd, DCMD_PROC_CLEAR_FLAG, &flags, sizeof(flags), NULL);
  

To set the flags, use DCMD_PROC_SET_FLAG.

DCMD_PROC_CURTHREAD

Switch to another thread.

#include <sys/procfs.h>

#define DCMD_PROC_CURTHREAD   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 8, pthread_t)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_CURTHREAD
dev_data_ptr A pointer to a pthread_t
n_bytes sizeof(pthread_t)
dev_info_ptr NULL

The argument to this command is a pthread_t value that specifies the thread that you want to be made the current thread. For example:

if ((err=devctl( fd, DCMD_PROC_CURTHREAD, &tid,
                 sizeof(tid), NULL)) != EOK) {
  /* An error occurred. */
}

DCMD_PROC_EVENT

Define an event to be delivered when the process associated with the file descriptor reaches a point of interest.

#include <sys/procfs.h>

#define DCMD_PROC_EVENT   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 17, struct sigevent)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_EVENT
dev_data_ptr A pointer to a struct sigevent
n_bytes sizeof(struct sigevent)
dev_info_ptr NULL

Use the DCMD_PROC_RUN command to set up the point of interest.


Note: The DCMD_PROC_EVENT command won't work unless you've set _DEBUG_RUN_ARM in the flags field of the procfs_run structure for the DCMD_PROC_RUN command.

Unlike DCMD_PROC_WAITSTOP, the DCMD_PROC_EVENT command doesn't block the calling process.

The argument is a pointer to the sigevent that you want to be delivered at the appropriate time. For example:

struct sigevent     event;

// Define a sigevent for process stopped notification.
event.sigev_notify = SIGEV_SIGNAL_THREAD;
event.sigev_signo = SIGUSR2;
event.sigev_code = 0;
event.sigev_value.sival_ptr = prp;
event.sigev_priority = -1;
devctl( fd, DCMD_PROC_EVENT, &event, sizeof(event), NULL);
  

DCMD_PROC_FREEZETHREAD

Freeze a thread in the process that's associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_FREEZETHREAD  __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 15, pthread_t)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process. You must have opened the file descriptor for writing.
dcmd DCMD_PROC_FREEZETHREAD
dev_data_ptr A pointer to a pthread_t
n_bytes sizeof(pthread_t)
dev_info_ptr NULL

The argument is a pointer to a pthread_t value that specifies the thread to be frozen. For example:

devctl( fd, DCMD_PROC_FREEZETHREAD, &tid, sizeof tid, NULL);
  

To unfreeze the thread, use DCMD_PROC_THAWTHREAD.

DCMD_PROC_GETALTREG

Get the information stored in the alternate register set for the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_GETALTREG   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 21, procfs_fpreg)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_GETALTREG
dev_data_ptr A pointer to a procfs_fpreg structure
n_bytes sizeof(procfs_fpreg)
dev_info_ptr NULL, or a pointer to an integer where the size of the register set can be stored

The argument is a pointer to a procfs_fpreg structure (see debug_fpreg_t in <sys/debug.h>) that's filled in with the required information on return. If you provide a non-NULL extra argument, it's filled with the actual size of the register set. For example:

procfs_fpreg reg;
int regsize;

devctl( fd, DCMD_PROC_GETALTREG, &reg, sizeof(reg), &regsize);
  

Note: If the thread hasn't used the alternate register set (e.g., AltiVec registers), the read may fail.

To set the alternate register set, use DCMD_PROC_SETALTREG.

DCMD_PROC_GETFPREG

Get the information stored in the Floating Point Data registers for the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_GETFPREG   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 12, procfs_fpreg)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_GETFPREG
dev_data_ptr A pointer to a procfs_fpreg structure
n_bytes sizeof(procfs_fpreg)
dev_info_ptr NULL, or a pointer to an integer where the size of the data can be stored

The argument is a pointer to a procfs_fpreg structure (see debug_fpreg_t in <sys/debug.h>) that's filled in with the required information on return. If you provide a non-NULL extra argument, it's filled with the size of the data. For example:

procfs_fpreg my_fpreg;

devctl( fd, DCMD_PROC_GETFPREG, my_fpreg, sizeof(procfs_fpreg), &size);
  

Note: If the thread hasn't used any floating-point arithmetic, the read may fail because an FPU context has not yet been allocated.

To set the Floating Point Data registers, use DCMD_PROC_SETFPREG.

DCMD_PROC_GETGREG

Get the information stored in the CPU registers based on the current thread of the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_GETGREG   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 10, procfs_greg)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_GETGREG
dev_data_ptr A pointer to a procfs_greg structure
n_bytes sizeof(procfs_greg)
dev_info_ptr NULL, or a pointer to an integer where the size of the data can be stored

The argument is a pointer to a procfs_greg structure (see debug_greg_t in <sys/debug.h>) that's filled in with the required information on return. If you provide a non-NULL extra argument, it's filled with the size of the data. For example:

procfs_greg my_greg;

devctl( fd, DCMD_PROC_GETGREG, my_greg, sizeof(procfs_greg), &size);
  

To set the CPU registers, use DCMD_PROC_SETGREG.

DCMD_PROC_GETREGSET

Read the given register set.

#include <sys/procfs.h>

#define DCMD_PROC_GETREGSET   __DIOTF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 25, procfs_regset)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_GETREGSET
dev_data_ptr A pointer to a procfs_regset structure
n_bytes sizeof(procfs_regset)
dev_info_ptr NULL

The argument is a pointer to a procfs_regset structure that's filled in with the required information on return. For example:

procfs_regset regset;

regset.id = REGSET_PERFREGS;
devctl( fd, DCMD_PROC_GETREGSET, &regset, sizeof(regset), NULL );
  

To set a given register set, use DCMD_PROC_SETREGSET.

DCMD_PROC_GET_BREAKLIST

Get a list of the active breakpoints for the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_GET_BREAKLIST __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 28, procfs_break)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process. You must have opened the file descriptor for writing.
dcmd DCMD_PROC_GET_BREAKLIST
dev_data_ptr NULL, or an array of procfs_break structures
n_bytes 0, or the size of the array
dev_info_ptr A pointer to an integer where the number of breakpoints can be stored

Call this the first time with an argument of NULL to get the number of breakpoints:

devctl( fd, DCMD_PROC_GET_BREAKLIST, NULL, 0, &n);
  

The total number of breakpoints returned is provided as the extra field. Next, allocate a buffer that's large enough to hold a procfs_break structure (see debug_break_t in <sys/debug.h>) for each breakpoint, and pass it to another devctl() call:

my_buffer = (procfs_break *) malloc( sizeof(procfs_break) * n );
if ( my_buffer == NULL ) {
  /* Not enough memory. */
}
devctl( fd, DCMD_PROC_GET_BREAKLIST, my_buffer,
        sizeof(procfs_break) * n, &dummy);
  

To set or clear breakpoints, use DCMD_PROC_BREAK.

DCMD_PROC_INFO

Obtain information about the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_INFO   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 1, procfs_info)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_INFO
dev_data_ptr A pointer to a procfs_info structure
n_bytes sizeof(procfs_info)
dev_info_ptr NULL

The argument is a pointer to a procfs_info structure (see debug_process_t in <sys/debug.h>) that's filled in with the required information on return. For example:

procfs_info my_info;

devctl( fd, DCMD_PROC_INFO, &my_info, sizeof(my_info), NULL);
  

For more information, see the section on DCMD_PROC_INFO in the appendix about the /procfs filesystem in The QNX Neutrino Cookbook.

DCMD_PROC_IRQS

Get the interrupt handlers owned by the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_IRQS   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 24, procfs_irq)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_IRQS
dev_data_ptr NULL, or an array of procfs_irq structures
n_bytes 0, or the size of the array
dev_info_ptr A pointer to an integer where the number of interrupt handlers can be stored

Call this the first time with an argument of NULL to get the number of interrupt handlers:

devctl( fd, DCMD_PROC_IRQS, NULL, 0, &n);
  

Next, allocate a buffer that's large enough to hold a procfs_irq structure (see debug_irq_t in <sys/debug.h>) for each handler, and pass it to another devctl() call:

my_buffer = (procfs_irq *) malloc( sizeof(procfs_irq) * n );
if ( my_buffer == NULL ) {
  /* Not enough memory. */
}

devctl( fd, DCMD_PROC_IRQS, my_buffer, sizeof(procfs_irq) * n, &dummy);
  

For more information, see the section on DCMD_PROC_IRQS in the appendix about the /procfs filesystem in The QNX Neutrino Cookbook.

DCMD_PROC_MAPDEBUG

Get the best guess to the ELF object on the host machine.

#include <sys/procfs.h>

#define DCMD_PROC_MAPDEBUG   __DIOTF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 3, procfs_debuginfo)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_MAPDEBUG
dev_data_ptr A pointer to a procfs_debuginfo structure
n_bytes sizeof(procfs_debuginfo)
dev_info_ptr NULL

This is used by debuggers to find the object that contains the symbol information, even though it may have been stripped on the target machine. This call is useful only on MAP_ELF mappings. If any relocation of the ELF object was done, this translation will be undone. This lets you pass in an address within a ELF module, and get in return the address that the original object was linked at so a debugger can find the symbol. (This is an extension from the SYSV interface.)

The argument is a pointer to a procfs_debuginfo structure that's filled in with the required information on return. The procfs_debuginfo structure can specify the base address of the mapped segment that you're interested in. For example:

procfs_debuginfo map;

map.info.vaddr = some_vaddr;
devctl( fd, DCMD_PROC_MAPDEBUG, &map, sizeof map, NULL);
  

DCMD_PROC_MAPDEBUG is useful for non-ELF objects if you need to get the name. Note that the path member in procfs_debuginfo is a one-byte array; if you want to get the name, you need to allocate more space for it. For example:

struct {
    procfs_debuginfo   info;
    char               buff[_POSIX_PATH_MAX];
} map;
  

DCMD_PROC_MAPDEBUG_BASE

Get information pertaining to the path associated with the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_MAPDEBUG_BASE __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 3, procfs_debuginfo)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_MAPDEBUG_BASE
dev_data_ptr A pointer to a procfs_debuginfo structure
n_bytes sizeof(procfs_debuginfo)
dev_info_ptr NULL

This is a convenience extension; it's equivalent to using DCMD_PROC_INFO, and then DCMD_PROC_MAPDEBUG with the base_address field. The base address is the address of the initial executable.

The argument is a pointer to a procfs_debuginfo structure, which is filled in with the required information on return. For example:

procfs_debuginfo dinfop;

devctl( fd, DCMD_PROC_MAPDEBUG_BASE, &dinfop, sizeof(dinfop), NULL);
  

DCMD_PROC_MAPINFO

Obtain segment-specific information about mapped memory segments in the process associated with the file descriptor. This call matches the corresponding mmap() calls.

#include <sys/procfs.h>

#define DCMD_PROC_MAPINFO   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 2, procfs_mapinfo)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_MAPINFO
dev_data_ptr NULL, or an array of procfs_mapinfo structures
n_bytes 0, or the size of the array
dev_info_ptr A pointer to an integer where the number of map entries can be stored

Note: Individual page data isn't returned (i.e., the PG_* flags defined in <mman.h> aren't returned). If you need the page attributes, use DCMD_PROC_PAGEDATA instead.

Call this the first time with an argument of NULL to get the number of map entries:

devctl( fd, DCMD_PROC_MAPINFO, NULL, 0, &n);
  

Next, allocate a buffer that's large enough to hold a procfs_mapinfo structure for each map entry, and pass it to another devctl() call:

my_buffer = (procfs_mapinfo *)
               malloc( sizeof(procfs_mapinfo) * n );
if ( my_buffer == NULL ) {
  /* Not enough memory. */
}

devctl( fd, DCMD_PROC_MAPINFO, my_buffer,
        sizeof(procfs_mapinfo) * n, &dummy);
  

For more information, see the section on DCMD_PROC_MAPINFO and DCMD_PROC_PAGEDATA in the appendix about the /procfs filesystem in The QNX Neutrino Cookbook.

DCMD_PROC_PAGEDATA

Obtain page data about mapped memory segments in the process associated with the file descriptor. This call matches the corresponding mmap() calls.

#include <sys/procfs.h>

#define DCMD_PROC_PAGEDATA   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 20, procfs_mapinfo)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_PAGEDATA
dev_data_ptr NULL, or an array of procfs_mapinfo structures
n_bytes 0, or the size of the array
dev_info_ptr A pointer to an integer where the number of map entries can be stored

Note: If you need the segment-specific attributes, use DCMD_PROC_MAPINFO instead.

Call this the first time with an argument of NULL to get the number of map entries:

devctl(fd, DCMD_PROC_PAGEDATA, NULL, 0, &n);
  

Next, allocate a buffer that's large enough to hold a procfs_mapinfo structure for each map entry, and pass it to another devctl() call:

my_buffer = (procfs_mapinfo *)
                malloc( sizeof(procfs_mapinfo) * n );
if ( my_buffer == NULL ) {
  /* Not enough memory. */
}

devctl( fd, DCMD_PROC_PAGEDATA, my_buffer,
        sizeof(procfs_mapinfo) * n, &dummy);
  

For more information, see the section on DCMD_PROC_MAPINFO and DCMD_PROC_PAGEDATA in the appendix about the /procfs filesystem in The QNX Neutrino Cookbook.

DCMD_PROC_PTINFO

Return the pagetable mapping information for the specified memory segment.

#include <sys/procfs.h>

#define DCMD_PROC_PTINFO   __DIOTF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 34, procfs_mapinfo)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_PTINFO
dev_data_ptr An array of at least one procfs_mapinfo structure
n_bytes The size of the array
dev_info_ptr A pointer to an integer where the number of mappings can be stored

Call this command with an array of at least one procfs_mapinfo structure (defined in <sys/procfs.h>). The virtual address you're interested in must be the vaddr field of the first procfs_mapinfo entry passed in. If the supplied virtual address doesn't match an existing segment, devctl() provides information on the next monotonically increasing segment.

The information is based on the current state of the pagetable mappings, and each mapping returned represents a page table entry. This means that while the mapping entry for the pagetable entry has PROT_READ | PROT_WRITE protection bits, it may not have been read from or written to, in which case the actual page table entry may indicate PROT_NONE or PROT_READ.

The size field indicates the size of the page mapping. The default is 4 KB, but larger mappings are possible on some machines.


Note: The number of mappings returned is the total number of mappings that could be returned, regardless of the size of the buffer passed.

For example:

procfs_mapinfo *my_buffer;
int num_mappings;

my_buffer = (procfs_mapinfo *) malloc( sizeof(procfs_mapinfo) * 10 );
if ( my_buffer == NULL ) {
  /* Not enough memory. */
}

if ((err = devctl( fd, DCMD_PROC_PTINFO, my_buffer,
                   sizeof(procfs_mapinfo) * 10, 
                   &num_mappings)) != EOK)
{
  /* An error occurred. */
}

DCMD_PROC_RUN

Resume the process that's associated with the file descriptor, if it has previously been stopped.

#include <sys/procfs.h>

#define DCMD_PROC_RUN   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 9, procfs_run)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process. You must have opened the file descriptor for writing.
dcmd DCMD_PROC_RUN
dev_data_ptr A pointer to a procfs_run structure
n_bytes sizeof(procfs_run)
dev_info_ptr NULL

To stop the process, use DCMD_PROC_STOP.

The DCMD_PROC_RUN command also lets you set the “points of interest” (e.g., signals or faults you want to stop on) and other run flags (e.g., instruction pointer or single-step).

The argument is a pointer to a procfs_run structure (see debug_run_t in <sys/debug.h>). This structure is passed on as control information to the process before it resumes. For example:

procfs_run      run;

memset( &run, 0, sizeof(run) );
run.flags |= _DEBUG_RUN_CLRFLT | _DEBUG_RUN_CLRSIG;
devctl( fd, DCMD_PROC_RUN, &run, sizeof(run), NULL);
  

The procfs_run or debug_run_t structure is defined as follows:

typedef struct _debug_run {
    uint32_t                    flags;
    pthread_t                   tid;
    sigset_t                    trace;
    sigset_t                    hold;
    fltset_t                    fault;
    uintptr_t                   ip;
} debug_run_t;

The members include:

flags
A combination of zero or more of the following bits:
tid
The ID of the thread that you want to become the current thread, for use with _DEBUG_RUN_CURTID.
trace
A set of signals (SIG*) to trace, for use with _DEBUG_RUN_TRACE.
hold
Not currently used.
fault
A set of faults (FLT*) to trace, for use with _DEBUG_RUN_FAULT.
ip
The new value for the instruction pointer, for use with _DEBUG_RUN_VADDR.

Use sigemptyset() and sigaddset() to build the set of signals or faults for the trace, hold and fault members.

DCMD_PROC_SETALTREG

Set the alternate register set with the values provided for the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_SETALTREG   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 22, procfs_fpreg)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process. You must have opened the file descriptor for writing.
dcmd DCMD_PROC_SETALTREG
dev_data_ptr A pointer to a procfs_fpreg structure
n_bytes sizeof(procfs_fpreg)
dev_info_ptr NULL

The argument is a pointer to a procfs_fpreg structure (see debug_fpreg_t in <sys/debug.h>) that specifies to set the values of the alternate register set. For example:

procfs_fpreg reg;

/* Set the members of reg as required. */
devctl( fd, DCMD_PROC_SETALTREG, &reg, sizeof(reg), NULL);
  

To get the alternate register set, use DCMD_PROC_GETALTREG.

DCMD_PROC_SETFPREG

Set the Floating Point Data registers with the values provided for the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_SETFPREG   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 13, procfs_fpreg)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process. You must have opened the file descriptor for writing.
dcmd DCMD_PROC_SETFPREG
dev_data_ptr A pointer to a procfs_fpreg structure
n_bytes sizeof(procfs_fpreg)
dev_info_ptr NULL

The argument is a pointer to a procfs_fpreg structure (see debug_fpreg_t in <sys/debug.h>) that specifies the values of the Floating Point Data registers. For example:

procfs_fpreg my_fpreg;

/* Set the members of my_fpreg as required. */
devctl( fd, DCMD_PROC_SETFPREG, my_fpreg, sizeof(procfs_fpreg), NULL);
  

To get the Floating Point Data registers, use DCMD_PROC_GETFPREG.

DCMD_PROC_SETGREG

Set the CPU registers with the values provided for the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_SETGREG   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 11, procfs_greg)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process. You must have opened the file descriptor for writing.
dcmd DCMD_PROC_SETGREG
dev_data_ptr A pointer to a procfs_greg structure
n_bytes sizeof(procfs_greg)
dev_info_ptr NULL

The argument is a pointer to a procfs_greg structure (see debug_greg_t in <sys/debug.h>) that specifies the values to assign to the CPU registers. For example:

procfs_greg my_greg;

/* Set the members of my_greg as required. */
devctl( fd, DCMD_PROC_SETGREG, my_greg, sizeof(procfs_greg), NULL);
  

To get the CPU registers, use DCMD_PROC_GETGREG.

DCMD_PROC_SETREGSET

Set the given register set.

#include <sys/procfs.h>

#define DCMD_PROC_SETREGSET   __DIOTF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 26, procfs_regset)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_SETREGSET
dev_data_ptr A pointer to a procfs_regset structure
n_bytes sizeof(procfs_regset)
dev_info_ptr NULL

The argument is a pointer to a procfs_regset structure that specifies the values to assign to the register set. For example:

procfs_regset regset;

regset.id = REGSET_PERFREGS;
devctl( fd, DCMD_PROC_SETREGSET, &regset, sizeof(regset), NULL );
  

To get the given register set, use DCMD_PROC_GETREGSET.

DCMD_PROC_SET_FLAG

Set specific debug flags with the values provided for the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_SET_FLAG   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 18, uint32_t)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_SET_FLAG
dev_data_ptr A pointer to a uint32_t
n_bytes sizeof(uint32_t)
dev_info_ptr NULL

The flags that can be set are described in <sys/debug.h>. The argument is a pointer to an unsigned integer that specifies the debug flags to set. For example:

int flags;

flags = _DEBUG_FLAG_KLC;        /* Kill-on-Last-Close flag */
devctl( fd, DCMD_PROC_SET_FLAG, &flags, sizeof(flags), NULL);
  

To clear the debug flags, use DCMD_PROC_CLEAR_FLAG.

DCMD_PROC_SIGNAL

Drop a signal on the process that's associated with the file descriptor. This is a way for a debugger to artificially generate signals as if they came from the system.

#include <sys/procfs.h>

#define DCMD_PROC_SIGNAL   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 4, procfs_signal)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_SIGNAL
dev_data_ptr A pointer to a procfs_signal structure
n_bytes sizeof(procfs_signal)
dev_info_ptr NULL

The argument is a pointer to a procfs_signal structure that specifies the signal to send. For example:

procfs_signal   signal;

signal.tid = 0;
signal.signo = SIGCONT;
signal.code = 0;
signal.value = 0;

devctl( fd, DCMD_PROC_SIGNAL, &signal, sizeof signal, NULL);
  

DCMD_PROC_STATUS

Get the current status of the current thread in the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_STATUS   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 7, procfs_status)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_STATUS
dev_data_ptr A pointer to a procfs_status structure
n_bytes sizeof(procfs_status)
dev_info_ptr NULL

The argument is a pointer to a procfs_status structure (see debug_thread_t in <sys/debug.h>) that's filled in with the required information on return. For example:

procfs_status my_status;

devctl( fd, DCMD_PROC_STATUS, &my_status, sizeof(my_status), NULL);
  

Note: If the current thread no longer exists, the process manager returns information about the one with the next higher thread ID. If there are no threads with a higher ID, devctl() returns ESRCH.

For more information about the contents of this structure, see Thread information,” earlier in this chapter.

DCMD_PROC_STOP

Stop the process that's associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_STOP   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 5, procfs_status)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process. You must have opened the file descriptor for writing.
dcmd DCMD_PROC_STOP
dev_data_ptr A pointer to a procfs_status structure
n_bytes sizeof(procfs_status)
dev_info_ptr NULL

The argument to this command is the address of a procfs_status structure (see debug_thread_t in <sys/debug.h>). This structure is filled with status information on return. For example:

procfs_status my_status;

devctl( fd, DCMD_PROC_STOP, &my_status, sizeof(my_status), NULL);
  

For more information about the contents of this structure, see Thread information,” earlier in this chapter.

To resume the process, use DCMD_PROC_RUN.

DCMD_PROC_SYSINFO

Obtain information stored in the system page.

#include <sys/procfs.h>

#define DCMD_PROC_SYSINFO   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 0, procfs_sysinfo)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_SYSINFO
dev_data_ptr NULL, or an array of procfs_sysinfo structures
n_bytes 0, or the size of the array
dev_info_ptr NULL, or a pointer to an integer where the required size can be stored

The argument is a pointer to a procfs_sysinfo structure that's filled in with the required information upon return. To get the whole system page, you have to make two calls: the first gets the size required:

devctl( fd, DCMD_PROC_SYSINFO, NULL, 0, &totalsize );
  

You then allocate a buffer of the required size and pass that buffer to the second call:

buffer = malloc( totalsize );
devctl( fd, DCMD_PROC_SYSINFO, buffer, totalsize, NULL );
  

The procfs_sysinfo structure is the same as the system page; for more information, see Structure of the system page in the Customizing Image Startup Programs chapter of Building Embedded Systems.

DCMD_PROC_THAWTHREAD

Unfreeze a thread in the process that's associated with the file descriptor. You must have opened the file descriptor for writing.

#include <sys/procfs.h>

#define DCMD_PROC_THAWTHREAD   __DIOT(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 16, pthread_t)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_THAWTHREAD
dev_data_ptr A pointer to a pthread_t
n_bytes sizeof(pthread_t)
dev_info_ptr NULL

The argument is a pointer to a pthread_t value that specifies the thread to be thawed. For example:

devctl( fd, DCMD_PROC_THAWTHREAD, &tid, sizeof tid, 0);
  

To freeze a thread, use DCMD_PROC_FREEZETHREAD.

DCMD_PROC_THREADCTL

Perform a ThreadCtl() on another process/thread.

#include <sys/procfs.h>

#define DCMD_PROC_THREADCTL   __DIOTF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 27, procfs_threadctl)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_THREADCTL
dev_data_ptr A pointer to a procfs_threadctl structure
n_bytes sizeof(procfs_threadctl)
dev_info_ptr NULL

The argument is a pointer to a procfs_threadctl structure. For example:

procfs_threadctl    tctl;

tctl.tid = tid;
tctl.cmd = _NTO_TCTL_NAME;

tn = (struct _thread_name *)(&tctl.data);
tn->name_buf_len = sizeof(tctl.data) - sizeof(*tn);

//We can only communicate a maximum buffer size via devctl
if( newname_len > tn->name_buf_len || prevname_len >
    tn->name_buf_len) {
    return E2BIG;
}

tn->new_name_len = newname_len;
if(newname_len > 0) {
    memcpy(tn->name_buf, newname, newname_len);
}

devctl(fd, DCMD_PROC_THREADCTL, &tctl, sizeof(tctl), NULL);
  

DCMD_PROC_TIDSTATUS

Get the current status of a thread in the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_TIDSTATUS   __DIOTF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 7, procfs_status)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_TIDSTATUS
dev_data_ptr A pointer to a procfs_status structure
n_bytes sizeof(procfs_status)
dev_info_ptr NULL

This is a short form of using DCMD_PROC_CURTHREAD to set the current thread, then DCMD_PROC_STATUS to get information about that thread, and then restoring the current thread.

The argument is a pointer to a procfs_status structure (see debug_thread_t in <sys/debug.h>), with the required thread ID specified in the tid field. This structure is filled in with the required information on return. For example:

procfs_status my_status;

my_status.tid = 1;
devctl( fd, DCMD_PROC_TIDSTATUS, &my_status, sizeof(my_status), NULL);
  

Note: If the thread that you specified no longer exists, the process manager returns information about the one with the next higher thread ID (in which case the tid member won't be the same as it was before you called the command). If there are no threads with a higher ID, devctl() returns ESRCH.

For more information about the contents of this structure, see Thread information,” earlier in this chapter.

DCMD_PROC_TIMERS

Get the timers owned by the process associated with the file descriptor.

#include <sys/procfs.h>

#define DCMD_PROC_TIMERS   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 23, procfs_timer)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_TIMERS
dev_data_ptr NULL, or an array of procfs_timer structures
n_bytes 0, or the size of the array
dev_info_ptr A pointer to an integer where the number of timers can be stored

Call this the first time with an argument of NULL to get the number of timers:

devctl( fd, DCMD_PROC_TIMERS, NULL, 0, &n);
  

Next, allocate a buffer that's large enough to hold a procfs_timer structure for each timer, and pass it to another devctl() call:

my_buffer = (procfs_timer *) malloc( sizeof(procfs_timer) * n;
if ( my_buffer == NULL ) {
  /* Not enough memory. */
}

devctl( fd, DCMD_PROC_TIMERS, my_buffer, sizeof(procfs_timer) * n, &dummy);
  

The procfs_timer structure is the same as the debug_timer_t structure, which is defined as follows in <sys/debug.h>:

typedef struct _debug_timer {
        timer_t              id;
        unsigned             spare;
        struct _timer_info   info;
} debug_timer_t;

For information about the _timer_info structure, see the entry for TimerInfo() in the QNX Neutrino C Library Reference, and the section on DCMD_PROC_TIMERS in the appendix about the /procfs filesystem in The QNX Neutrino Cookbook.

DCMD_PROC_WAITSTOP

Hold off the calling process until the process that's associated with the file descriptor reaches a point of interest. You must have opened the file descriptor for writing.

#include <sys/procfs.h>

#define DCMD_PROC_WAITSTOP   __DIOF(_DCMD_PROC, __PROC_SUBCMD_PROCFS + 6, procfs_status)

The arguments to devctl() are:

Argument Value
filedes A file descriptor for the process.
dcmd DCMD_PROC_WAITSTOP
dev_data_ptr A pointer to a procfs_status structure
n_bytes sizeof(procfs_status)
dev_info_ptr NULL

Use the DCMD_PROC_RUN command to set up the point of interest. If you don't want to block the calling process, use DCMD_PROC_EVENT instead of DCMD_PROC_WAITSTOP.

The argument is a pointer to a procfs_status structure (see debug_thread_t in <sys/debug.h>) that's filled with status information on return. For example:

procfs_status my_status;

devctl( fd, DCMD_PROC_WAITSTOP, &my_status, sizeof my_status, NULL);
  

For more information about the contents of this structure, see Thread information,” earlier in this chapter.