The chapter discusses the workload hierarchy and provides information about projects and tasks. The following topics are covered:
The Solaris Operating System uses the workload hierarchy to organize the work being performed on the system. A task is a collection of processes that represents a workload component. A project is a collection of tasks that represents an entire workload. At any given time, a process can be a component of only one task and one project. The relationships in the workload hierarchy are illustrated in the following figure.
A user who is a member of more than one project can run processes in multiple projects at the same time. All processes that are started by a process inherit the project and task created by the parent process. When you switch to a new project in a startup script, all child processes run in the new project.
An executing user process has an associated user identity (uid), group identity (gid), and project identity (projid). Process attributes and abilities are inherited from the user, group, and project identities to form the execution context for a task.
For an in-depth discussion of projects and tasks, see Chapter 2, Projects and Tasks (Overview), in System Administration Guide: Solaris Containers-Resource Management and Solaris Zones. For the administration commands for managing projects and tasks, see Chapter 3, Administering Projects and Tasks, in System Administration Guide: Solaris Containers-Resource Management and Solaris Zones.
The project file is the heart of workload hierarchy. The project database is maintained on a system through the /etc/project file or over the network through a naming service, such as NIS or LDAP.
The /etc/project file contains five standard projects.
This project is used for all system processes and daemons.
All root processes spawned by root logins and root cron, at, and batch jobs.
This special project is for IPQoS.
A default project is assigned to every user.
This project is used for all users in the group staff.
To access the project file programmatically, use the following structure:
struct project { char *pj_name; /* name of the project */ projid_t pj_projid; /* numerical project ID */ char *pj_comment; /* project comment */ char **pj_users; /* vector of pointers to project user names */ char **pj_groups; /* vector of pointers to project group names */ char *pj_attr; /* project attributes */ };
The project structure members include the following:
Name of the project.
Project ID.
User-supplied project description.
Pointers to project user members.
Pointers to project group members.
Project attributes. Use these attributes to set values for resource controls and project pools.
Resource usage can be controlled through project attributes, or, for zones, configured through the zonecfg command. Four prefixes are used to group the types of resource control attributes:
project.* – This prefix denotes attributes that are used to control projects. For example, project.max-locked-memory indicates the total amount of locked memory allowed, expressed as a number of bytes. The project.pool attribute binds a project to a resource pool. See Chapter 6, Resource Pools.
task.* – This prefix is used for attributes that are applied to tasks. For example, the task.max-cpu-time attribute sets the maximum CPU time that is available to this task's processes, expressed as a number of seconds.
process.* – This prefix is used for process controls. For example, the process.max-file-size control sets the maximum file offset that is available for writing by this process, expressed as a number of bytes.
zone.* – The zone.* prefix indicates a zone-wide resource control applied to projects, tasks, and processes in a zone. For example, zone.max-lwps prevents too many LWPs in one zone from affecting other zones. A zone's total LWPs can be further subdivided among projects within the zone within the zone by using project.max-lwps entries.
For the complete list of resource controls, see resource_controls(5).
The following functions are provided to assist developers in working with projects. The functions use entries that describe user projects in the project database.
Close the project database and deallocate resources when processing is complete.
Returns a pointer to a structure containing an entry in the project database. Rather than using nsswitch.conf, fgetprojent() reads a line from a stream.
Check the validity of the project keyword, look up the project, and return a pointer to the project structure if found.
Search the project database for an entry with the number that specifies the project ID.
Search the project database for an entry with the string that specifies project name.
Returns a pointer to a structure containing an entry in the project database.
Check whether the specified user is permitted to use the specified project.
Calling process joins the target project by creating a new task in the target project.
Rewind the project database to allow repeated searches.
The key points for this example include the following:
setprojent() rewinds the project database to start at the beginning.
getprojent() is called with a conservative maximum buffer size that is defined in project.h.
endprojent() closes the project database and frees resources.
#include <project.h> struct project projent; char buffer[PROJECT_BUFSZ]; /* Use safe buffer size from project.h */ ... struct project *pp; setprojent(); /* Rewind the project database to start at the beginning */ while (1) { pp = getprojent(&projent, buffer, PROJECT_BUFSZ); if (pp == NULL) break; printf("%s:%d:%s\n", pp->pj_name, pp->pj_projid, pp->pj_comment); ... }; endprojent(); /* Close the database and free project resources */
The following example calls getprojbyid() to get a project database entry that matches the caller's project ID. The example then prints the project name and the project ID.
#include <project.h> struct project *pj; char buffer[PROJECT_BUFSZ]; /* Use safe buffer size from project.h */ main() { projid_t pjid; pjid = getprojid(); pj = getprojbyid(pjid, &projent, buffer, PROJECT_BUFSZ); if (pj == NULL) { /* fail; */ } printf("My project (name, id) is (%s, %d)\n", pp->pj_name, pp->pj_projid); }
Consider the following issues when writing your application:
No function exists to explicitly create a new project.
A user cannot log in if no default project for the user exists in the project database.
A new task in the user's default project is created when the user logs in.
When a process joins a project, the project's resource control and pool settings are applied to the process.
setproject() requires privilege. The newtask command does not require privilege if you own the process. Either can be used to create a task, but only newtask can change the project of a running process.
No parent/child relationship exists between tasks.
Finalized tasks can be created by using newtask -F or by using setproject() to associate the caller with a new project. Finalized tasks are useful when trying to accurately estimate aggregate resource accounting.
The reentrant functions, getprojent(), getprojbyname(), getprojbyid(), getdefaultproj(), and inproj(), use buffers supplied by the caller to store returned results. These functions are safe for use in both single-threaded applications and multithreaded applications.
Reentrant functions require these additional arguments: proj, buffer, and bufsize. The proj argument must be a pointer to a project structure allocated by the caller. On successful completion, these functions return the project entry in this structure. Storage referenced by the project structure is allocated from the memory specified by the buffer argument. bufsize specifies the size in number of bytes.
If an incorrect buffer size is used, getprojent() returns NULL and sets errno to ERANGE.