第八章-进程控制

进程控制

  • 进程标识
  • 函数fork
  • 函数vfork
  • 函数exit
  • 函数wait、waitpid和waitid
  • 竞争条件
  • 函数exec
  • 更改用户id和更改组id
  • 解释器文件
  • 函数system
  • 进程会计
  • 进程调度
  • 进程时间

进程标识

  • 进程标识是非负整数,具有唯一性和可复用性
  • ID为0的进程通常是调度进程(swapper),是内核的一部分,ID为1的进程通常是init进程,是一个普通的用户进程,通常读取与系统有关的初始化文件,该进程在自举过程结束时由内核调用,ID为2的进程是页守护进程,负责支持虚拟存储器系统的分页操作。

获得相关标识符的函数

 #include 
       #include 
       pid_t getpid(void);
       pid_t getppid(void);
       uid_t getuid(void);
       uid_t geteuid(void);
       gid_t getgid(void);
       gid_t getegid(void);       
DESCRIPTION
       getpid() returns the process ID of the calling process.  (This is often used by routines that generate unique temporary filenames.)
       getppid() returns the process ID of the parent of the calling process.
       getuid() returns the real user ID of the calling process.
       geteuid() returns the effective user ID of the calling process.
       getgid() returns the real group ID of the calling process.
       getegid() returns the effective group ID of the calling process.

函数fork

#include 
       pid_t fork(void);
DESCRIPTION
       fork()  creates  a  new process by duplicating the calling process.  The new process is referred to as the child process.  The calling process is referred to as the parent process.
  • 写时复制技术,并不执行一个父进程数据段、栈和堆的完全副本
  • 父进程和子进程共享正文段
  • 父进程和子进程共享同一个文件偏移量
  • 父进程和子进程谁先执行并不确定
  • 父进程和子进程每个相同的打开描述符共享一个文件表项

第八章-进程控制_第1张图片
子进程对变量的改变并不影响父进程中改变量的值

函数vfork

 #include 
       #include 
       pid_t vfork(void);
       vfork() is a special case of clone(2).  It is used to create new processes without copying the page tables of the parent process
       vfork() differs from fork(2) in that the calling thread is suspended until the child terminates (either normally, by calling _exit(2), or  abnormally,  after  delivery  of  a  fatal  signal),  or it makes a call to execve(2).  Until that point, the child shares all memory with its parent,including the stack.  The child must not return from the current function or call exit(3), but may call _exit(2)
`As with fork(2), the child process created by vfork() inherits copies of various of the caller's process attributes (e.g., file descriptors, signal  dispositions,  and  current  working  directory);  the vfork() call differs only in the treatment of the virtual address space, as described
       above.
       Signals sent to the parent arrive after the child releases the parent's memory (i.e., after the child terminates or calls execve(2))

在子进程调用exec、exit或_exit之前,它在父进程的空间中运行

函数exit

#include 
       void exit(int status);
DESCRIPTION
       The exit() function causes normal process termination and the value of status & 0377 is returned to the parent (see wait(2)).
  • 调用各个终止处理程序(在调用atexit函数时登记)
  • 关闭所有标准I/O流
  • 调用_exit函数或_Exit函数

函数wait、waitpid和waitid

       #include 
       #include 
        /*The wait() system call suspends execution of the calling process until one of its children terminates.  The call wait(&status) is equivalent to:waitpid(-1, &status, 0);*/
       pid_t wait(int *status); 
       /*The waitpid() system call suspends execution of the calling process until a child specified by pid argument has changed state.  By default, waitpid() waits only for terminated children, but this behavior is modifiable via the options argument, as described below.
       The value of pid can be:
       < -1   meaning wait for any child process whose process group ID is equal to the absolute value of pid.
       -1     meaning wait for any child process.
       0      meaning wait for any child process whose process group ID is equal to that of the calling process.
       > 0    meaning wait for the child whose process ID is equal to the value of pid.
       The value of options is an OR of zero or more of the following constants:
       WNOHANG     return immediately if no child has exited.
       WUNTRACED   also return if a child has stopped (but not traced via ptrace(2)).  Status for traced children which have stopped is provided even if this option is not specified.
       WCONTINUED (since Linux 2.6.10)
                   also return if a stopped child has been resumed by delivery of SIGCONT.*/
       pid_t waitpid(pid_t pid, int *status, int options);
       /*The waitid() system call (available since Linux 2.6.9) provides more precise control over which child state changes to wait for.
       The idtype and id arguments select the child(ren) to wait for, as follows:
       idtype == P_PID
              Wait for the child whose process ID matches id.
       idtype == P_PGID
              Wait for any child whose process group ID matches id.
       idtype == P_ALL
              Wait for any child; id is ignored.*/
       int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

下面是一个fork两次以避免僵死进程的实例

#include "apue.h"
#include 
int
main(void)
{
        pid_t   pid;
        if ((pid = fork()) < 0) {
                err_sys("fork error");
        } else if (pid == 0) {          /* first child */
                if ((pid = fork()) < 0)
                        err_sys("fork error");
                else if (pid > 0)
                        exit(0);        /* parent from second fork == first child */

                /*
                 * We're the second child; our parent becomes init as soon
                 * as our real parent calls exit() in the statement above.
                 * Here's where we'd continue executing, knowing that when
                 * we're done, init will reap our status.
                 */
                sleep(2);
                printf("second child, parent pid = %ld\n", (long)getppid());
                exit(0);
        }

        if (waitpid(pid, NULL, 0) != pid)       /* wait for first child */
                err_sys("waitpid error");

        /*
         * We're the parent (the original process); we continue executing,
         * knowing that we're not the parent of the second child.
         */
        exit(0);
}

竞争条件

当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程的顺序时,我们认为发生了竞争条件。

函数exec

#include 
       extern char **environ;
       int execl(const char *path, const char *arg, ...
                       /* (char  *) NULL */);
       int execlp(const char *file, const char *arg, ...
                       /* (char  *) NULL */);
       int execle(const char *path, const char *arg, ...
                       /*, (char *) NULL, char * const envp[] */);
       int execv(const char *path, char *const argv[]);
       int execvp(const char *file, char *const argv[]);
       int execvpe(const char *file, char *const argv[],
                       char *const envp[]);

fork函数创建新的进程,调用一种exec函数执行另一个程序,exec只是用磁盘上的一个新程序替换当前进程的正文段、数据段、堆段和栈段,并不改变进程ID。
7个exec函数的区别如下,
第八章-进程控制_第2张图片

  • 可执行文件引用的方式,取路径名,文件名或fd(PATH变量包含了一张目录表-称为路径前缀)。
  • 参数表,列表方式要求每个命令行参数都说明为一个单独的参数,矢量方式要求用一个指向各参数的指针数组。
  • 环境表,用environ变量为新程序复制现有的环境,e方式用一个指向环境字符串的 指针数组。

系统对环境表和参数表的总长度都有一个限制

grep getrlimit /usr/share/man/*/*
find /usr/share/man -type f -print | xargs grep getrlimit
find /usr/share/man -type f -print | xargs bzgrep getrlimit

对打开文件的处理:每个描述符执行时关闭标志,若设置了该标志,则在执行exec时关闭该描述符。
以下是这7个函数的关系,只有execve是系统调用
第八章-进程控制_第3张图片

更改用户id和更改组id

解释器文件

其起始行的形式:
#! pathname[optional-argument]
以上感叹号和pathname之间的空格是可选的
当调用exec函数的过程时实际上内核执行该解释器文件第一行中pathname所指定的文件。
下面是一个调用exec函数执行一个解释器文件的实例

#include "apue.h"
#include 

int
main(void)
{
	pid_t	pid;

	if ((pid = fork()) < 0) {
		err_sys("fork error");
	} else if (pid == 0) {			/* child */
		if (execl("/home/xiangke/bin/testinterp",
				  "testinterp", "myarg1", "MY ARG2", (char *)0) < 0)
			err_sys("execl error");
	}
	if (waitpid(pid, NULL, 0) < 0)	/* parent */
		err_sys("waitpid error");
	exit(0);
}

解释器文件的内容如下

#! /home/xiangke/bin/echoall foo

示例程序执行时,内核exec如上解释器文件定义的解释器“/home/xiangke/bin/echoall”,结果如下
第八章-进程控制_第4张图片
前两个参数是解释器及其可选参数,后面的参数才是exec函数传入的参数
在解释器文件中用-f选项,如下是一个awk脚本文件

#!/usr/bin/awk -f
# Note: on Solaris, use nawk instead
BEGIN {
        for (i = 0; i < ARGC; i++)
                printf "ARGV[%d] = %s\n", i, ARGV[i]
        exit
}

该awk脚本可像shell脚本一样执行,如下

xiangke@xiangke-virtual-machine:/home/apue.3e/proc$ sudo ./awkexample file1 FILENAME2 f3
ARGV[0] = awk
ARGV[1] = file1
ARGV[2] = FILENAME2
ARGV[3] = f3

函数system

 #include 
 /* execute a shell command*/
       int system(const char *command);

在unix系统中,system总是可用的,在其实现中调用了fork、exec和waitpid,有三种返回值

  • fork失败或者waitpid返回出错(除EINTR之外),返回-1
  • exec失败,如同shell执行了exit一样
  • shell的终止状态

进程会计

 #include 
/*switch process accounting on or off*/
       int acct(const char *filename);
  • 进行进程会计处理,每当进程结束时内核就写一个会计记录。
  • 会计记录写到指定的文件,linux系统中,该文件是/var/account/pacct或/var/log/account/pacct
  • 相关命令是accton
  • 会计记录结构定义在头文件,如下:
struct acct
{
  char ac_flag;                 /* Flags.  */
  u_int16_t ac_uid;             /* Real user ID.  */
  u_int16_t ac_gid;             /* Real group ID.  */
  u_int16_t ac_tty;             /* Controlling terminal.  */
  u_int32_t ac_btime;           /* Beginning time.  */
  comp_t ac_utime;              /* User time.  */
  comp_t ac_stime;              /* System time.  */
  comp_t ac_etime;              /* Elapsed time.  */
  comp_t ac_mem;                /* Average memory usage.  */
  comp_t ac_io;                 /* Chars transferred.  */
  comp_t ac_rw;                 /* Blocks read or written.  */
  comp_t ac_minflt;             /* Minor pagefaults.  */
  comp_t ac_majflt;             /* Major pagefaults.  */
  comp_t ac_swaps;              /* Number of swaps.  */
  u_int32_t ac_exitcode;        /* Process exitcode.  */
  char ac_comm[ACCT_COMM+1];    /* Command name.  */
  char ac_pad[10];              /* Padding bytes.  */
};

进程调度

#include 
/* nice()  adds  inc to the nice value for the calling process.  (A higher nice value means a low priority.)  Only the superuser may specify a negative increment, or priority increase.  The range for nice values is described in getpriority(2)*/
       int nice(int inc);
 #include 
 #include 
 /* The value which is one of PRIO_PROCESS, PRIO_PGRP, or PRIO_USER*/
 /* The  getpriority()  call returns the highest priority (lowest numerical value) enjoyed by any of the specified processes.*/
 int getpriority(int which, id_t who);
 /*The setpriority() call sets the priorities of all of the specified processes to the specified value.  Only the superuser may lower priorities*/
 /*The prio argument is a value in the range -20 to 19*/
 int setpriority(int which, id_t who, int prio);

进程时间

#include 
/*get process times*/
       clock_t times(struct tms *buf);
 /*times() stores the current process times in the struct tms that buf points to.  The struct tms is as defined in :
           struct tms {
               clock_t tms_utime;  /* user time */
               clock_t tms_stime;  /* system time */
               clock_t tms_cutime; /* user time of children */
               clock_t tms_cstime; /* system time of children */
           };*/
/*times()  returns  the  number  of clock ticks that have elapsed since an arbitrary point in the past*/       
/*The number of clock ticks per second can be obtained using:
           sysconf(_SC_CLK_TCK);*/    

你可能感兴趣的:(unix环境高级编程,嵌入式,c语言)